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));