C/C++로 구현된 오래된 네트워크 코드를 최신 컴파일러로 컴파일하면 일부 API 비추천 되었으므로 새로운 API를 사용하라는 경고가 나온다.
아래 코드는 “TCP/IP 윈도우 소켓 프로그래밍” 이라는 책의 3장에 있는 예제 코드로 도메인주소 To IP 주소, IP 주소 To 도메인 주소를 출력하는 코드이다.
// 소켓 함수 오류 출력 void err_display(char* msg) { LPVOID lpMsgBuf; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, WSAGetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL); printf("[%s] %s", msg, (char*)lpMsgBuf); LocalFree(lpMsgBuf); }
//// 도메인 이름 -> IPv4 주소 BOOL GetIPAddr(char *name, IN_ADDR *addr) { HOSTENT *ptr = gethostbyname(name); if(ptr == NULL){ err_display("gethostbyname()"); return FALSE; } if(ptr->h_addrtype != AF_INET) return FALSE; memcpy(addr, ptr->h_addr, ptr->h_length); return TRUE; }
// IPv4 주소 -> 도메인 이름 BOOL GetDomainName(IN_ADDR addr, char *name, int namelen) { HOSTENT *ptr = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET); if(ptr == NULL){ err_display("gethostbyaddr()"); return FALSE; } if(ptr->h_addrtype != AF_INET) return FALSE; strncpy(name, ptr->h_name, namelen); return TRUE; }
int main(int argc, char *argv[]) { WSADATA wsa; if(WSAStartup(MAKEWORD(2, 2), &wsa) != 0) return 1;
printf("도메인 이름(변환 전) = %s\n", TESTNAME);
// 도메인 이름 -> IP 주소 IN_ADDR addr; if(GetIPAddr(TESTNAME, &addr)){ // 성공이면 결과 출력 printf("IP 주소(변환 후) = %s\n", inet_ntoa(addr)); // IP 주소 -> 도메인 이름 char name[256]; if(GetDomainName(addr, name, sizeof(name))){ // 성공이면 결과 출력 printf("도메인 이름(다시 변환 후) = %s\n", name); } }
WSACleanup(); return 0; } |
위 코드를 컴파일 하면 아래와 같은 경고가 나온다

warning C4996: 'gethostbyname': Use getaddrinfo() or GetAddrInfoW() instead or define _WINSOCK_DEPRECATED_NO_WARNINGS to disable deprecated API warnings
warning C4996: 'gethostbyaddr': Use getnameinfo() or GetNameInfoW() instead or define _WINSOCK_DEPRECATED_NO_WARNINGS to disable deprecated API warnings
warning C4996: 'strncpy': This function or variable may be unsafe. Consider using strncpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
warning C4996: 'inet_ntoa': Use inet_ntop() or InetNtop() instead or define _WINSOCK_DEPRECATED_NO_WARNINGS to disable deprecated API warnings
경고에서 제시해준 API를 사용한 코드는 아래와 같다.
// 도메인 이름 -> IPv4 주소 BOOL GetIPAddr(char* host_name, addrinfo* addr) { struct addrinfo hints; char* szRemoteAddress = NULL, * szRemotePort = NULL; int rc; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP;
rc = getaddrinfo(host_name, szRemotePort, &hints, &addr); if (rc == WSANO_DATA) { return FALSE; }
return TRUE; }
// IPv4 주소 -> 도메인 이름 BOOL GetDomainName(char* pIp, char* name, int namelen) { struct addrinfo* res = 0; getaddrinfo(pIp, 0, 0, &res);
int ret = getnameinfo(res->ai_addr, (socklen_t)res->ai_addrlen, name, namelen, 0, 0, 0); if (ret != 0) { err_display("gethostbyaddr()"); return FALSE; }
return TRUE; }
int main(int argc, char* argv[]) { WSADATA wsa; if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) return 1;
printf("도메인 이름(변환 전) = %s\n", TESTNAME);
// 도메인 이름 -> IP 주소 //IN_ADDR addr; struct addrinfo addr; if (GetIPAddr(TESTNAME, &addr)) { // 성공이면 결과 출력 char clientIP[32] = { 0, }; inet_ntop(AF_INET, &(addr.ai_addr), clientIP, 32);
printf("IP 주소(변환 후) = %s\n", clientIP);
// IP 주소 -> 도메인 이름 char name[256]; if (GetDomainName(clientIP, name, sizeof(name))) { // 성공이면 결과 출력 printf("도메인 이름(다시 변환 후) = %s\n", name); } }
WSACleanup(); return 0; } |
각 API에 대한 설명과 사용 방법은 아래를 참고한다
gethostbyaddr
C++ - 컴퓨터의 실제 IP 얻기 (code) - jacking75
[네트워크/C] getaddrinfo 함수
getnameinfo
getnameinfo function (ws2tcpip.h) - Win32 apps | Microsoft Docs
getaddrinfo, getnameinfo함수(네트워크 -c)
inet_ntop
IPv6 프로그래밍
아래 코드는 서버에서 accept 후 연결된 클라이언트의 IP를 얻을 때이다
SOCKADDR_IN clientaddr; auto addrlen = static_cast<int>(sizeof(clientaddr)); client_sock = accept(listen_sock, (SOCKADDR *)&clientaddr, &addrlen); if(client_sock == INVALID_SOCKET){ err_display("accept()"); break; }
// 접속한 클라이언트 정보 출력 char clientIP[32] = { 0, }; inet_ntop(AF_INET, &(clientaddr.sin_addr), clientIP, 32 - 1); // printf("\n[TCP 서버] 클라이언트 접속: IP 주소=%s, 포트 번호=%d\n", clientIP, ntohs(clientaddr.sin_port)); |
inet_pton
아래 코드는 IP 주소로 connect 함수를 호출할 때 사용하는 주소를 만들 때 사용한다.
// socket() SOCKET sock = socket(AF_INET, SOCK_STREAM, 0); if(sock == INVALID_SOCKET) err_quit("socket()");
// connect() SOCKADDR_IN serveraddr; ZeroMemory(&serveraddr, sizeof(serveraddr)); serveraddr.sin_family = AF_INET;
auto ret = inet_pton(AF_INET, SERVERIP, (void *)&serveraddr.sin_addr.s_addr); serveraddr.sin_port = htons(SERVERPORT);
retval = connect(sock, (SOCKADDR *)&serveraddr, sizeof(serveraddr)); |