현재강좌 : Winsock 프로그래밍 이전: 5.1 윈속의 이해 다음: 5.3 윈속 채팅 서버


5.2 윈속의 동작

▶ 윈속 관련 함수들의 사용법 즉, 함수호출 순서와 함수의 인자 및 결과 리턴값 등은 2장에서 소개한 BSD 소켓의 사용법과 거의 같다.

▶ 윈속 프로그램을 작성하기 위하여는 윈도우 프로그래밍과 c 언어에 대한 지식을 필요로 하나 이 책에서는 이에 대해 자세한 설명을 하지 않고 예제 프로그램을 이해하는데 필요한 기본적인 내용만 설명하겠다.

▶ 소켓의 기본 개념에 관하여는 2장을 먼저 참고하기 바란다.

5.2.1 기본적인 윈속 함수

(1) socket()

▶ 윈속에서도 BSD 소켓 프로그램에서와 같이 socket() 함수를 호출하여 통신의 창구 역할을 하는 소켓을 생성한다.

▶ 아래는 socket()의 사용 예로 인터넷 도메인의 주소 체계에서 스트림형 소켓을 개설하는 것을 보였다.

SOCKET s; /* SOCKET은 int 타입이다 */

char lpszMsg[100];

s = socket(AF_INET, SOCK_STREAM, 0);

if (s == INVALID_SOCKET) { /* 소켓 개설 에러 발생 */

wsprintf(lpszMsg, "socket() 에러번호 : %d", WSAGetLastError());

MessageBox(hwnd, lpszMsg, "에러", MB_OK);

}

▶ socket() 함수의 첫번째 인자는 소켓이 사용할 주소 체계(Address Family)의 종류를 지정한다.

▶ 윈속 버전 1.1에서는 인터넷 주소체계(AF_INET)만을 지원한다.

▶ 두번째 인자는 트랜스포트 프로토콜을 지정하는데 연결형인 스트림 소켓을 개설하려면 SOCK_STREAM을, 비연결형인 데이터그램 형태의 소켓을 개설하려면 SOCK_ DGRAM을 선택한다.

▶ 세번째 인자는 프로토콜 타입(Type)인데 일반적으로 0을 쓴다.

▶socket()의 수행이 성공한 경우에는 새로운 소켓번호를 리턴하고, 실패한 경우에는 INVALID_SOCKET이 리턴된다.

▶ 위에서 WSAGetLastError()는 가장 최근에 발생한 윈속 관련 함수의 에러코드를 리턴하는 함수이다

▶ MessageBox()는 메시지 상자를 만들어 화면에 출력하는 윈도우 함수이다.

▶ MessageBox()의 원형은 다음과 같으며 위에서는 에러 메시지와 OK 버튼을 출력하고 있다.

int MessageBox (

HWND hWnd, /* 윈도우 핸들 */

LPCTSTR lpText, /* 텍스트 배열 주소 */

LPCTSTR lpCaption, /* 캡션 배열 주소 */

UINT uType); /* 박스 스타일 */

▶ 위에서 HWND는 윈도우 핸들 타입을 나타내는데 실제로는 int 타입이다.

▶ LPCTSTR은 long pointer const char string 타입을 나타내고, UINT는 unsigned int 타입을 나타낸다.

(2) bind()

▶ bind()는 서버에서 호출되는데 socket()의 수행으로 생성된 소켓의 번호(s)와 서버의 소켓주소 구조체(SOCKADDR_IN)를 연결하기 위해 호출한다.

▶ bind()의 첫번째 인자로는 소켓번호를, 두번째 인자는 소켓주소 구조체의 주소를 쓰고 세번째 인자는 소켓주소 구조체의 크기를 쓴다.

▶ 소켓주소 구조체 SOCKADDR은 BSD 소켓에서와 같은 형태를 가진다(2.1.4절 참조).

▶ 아래는 인터넷 전용으로 정의된 소켓주소 구조체 SOCK- ADDR_IN을 보여주고 있다.

struct SOCKADDR_IN {

short sin_family; /* 주소 체계 */

u_short sin_port; /* 포트번호 */

struct in_addr sin_addr; /* 인터넷 주소 (4바이트) */

char sin_zero[8]; /* 총 16바이트를 맞추기 위한 여백 */

}

struct in_addr {

u_long s_addr; /* 32비트의 IP 주소를 저장할 구조체 */

};

▶ 아래 프로그램 코드는 bind() 함수의 사용 예인데 포트번호로 3000번을 지정하고 있다.

#define PORT_NO 3000

SOCKET s;

char lpszMsg[100];

SOCKADDR_IN m_addr; /* 인터넷 타입의 소켓주소 구조체 */

/* 소켓 개설 */

s = socket(AF_INET, SOCK_STREAM, 0);

/* 소켓주소 구조체의 값을 지정 */

m_addr.sin_family = AF_INET;

m_addr.sin_addr.s_addr = htonl(INADDR_ANY);

m_addr.sin_port = htons(PORT_NO);

bind(s, (LPSOCKADDR)&m_addr, sizeof(m_addr));

▶ 위에서 SOCKADDR_IN과 LPSOCKADDR은 각각 인터넷 타입의 소켓주소 구조체와 일반적인 소켓주소 구조체를 나타냈다.

▶ bind() 함수는 원래 LPSOCKADDR 타입의 인자를 사용하도록 정의되어 있으므로 SOCKADDR_IN 타입의 변수인 m_addr을 bind()를 호출하기 전에 LPSOCKADDR 타입으로 casting하는 것이 필요하다.

▶ 그러나 처음부터 LPSOCKADDR 타입의 소켓주소 구조체를 사용하지 않은 이유는 SOCKADDR_IN 타입이 IP 주소와 포트번호 값을 지정하기에 편리하기 때문이다.

▶ 위에서 htonl()와 htons()는 컴퓨터 내부에서 사용하는 바이트 순서(호스트 바이트 순서)를 네트웍 바이트 순서(데이터의 바이트 단위 전송 순서)로 바꾸는 함수들로 자세한 내용은 2.2.1절을 참조하기 바란다.

(3) listen()

▶ listen()은 연결형(TCP) 소켓을 개설한 서버에서 호출하는데, 지정된 소켓을 통하여 들어오는 클라이언트로부터의 연결요청을 기다리는 함수이다.

▶ listen()의 첫번째 인자는 소켓번호이고, 두번째 인자에는 연결을 대기시킬 수 있는 클라이언트의 최대수를 지정한다.

▶ listen()이 성공적으로 수행된 경우에는 0을 리턴하며, 실패한 경우에는 SOCKET_ERROR를 리턴한다.

▶ 다음은 listen()의 사용 예이다.

listen(s, 5);

(4) accept()

▶ accept()는 연결형 서버에서 listen()을 실행하여 수동 대기모드로 연결요청을 기다리다가 클라이언트에서 서버로 연결요청을 보내오면 이를 수락하기 위하여, 서버가 호출해 두는 함수이다.

▶ accept()의 수행이 성공한 경우에는 소켓이 하나 새로 만들어지며 새로 만들어진 소켓번호가 리턴된다.

▶ 또한 이때 접속된 클라이언트의 소켓주소 구조체의 포인터와 소켓주소 구조체의 길이는 accept() 함수 인자를 통하여 알려준다.

▶ 다음은 accept()의 사용 예인데 accpet()가 성공적으로 리턴되면 연결된 클라이언트의 소켓주소가 구조체 m_client Addr에 실려있게 된다.

SOCKET s; /* 서버가 개설한 소켓번호 */

SOCKET client_s; /* accept()가 리턴할 새로운 소켓번호 */

char lpszMsg[100]; /* 메시지 박스에 출력할 메시지 */

SOCKADDR_IN m_addr; /* 서버의 소켓주소 구조체 */

SOCKADDR_IN m_clientAddr; /* 접속된 클라이언트의 소켓주소 구조체 */

client_s = accept(s, (LPSOCKADDR)&m_clientAddr, &m_nClientAddrLen);

if (client_s == INVALID_SOCKET) {

wsprintf(lpszMsg, "accept() 에러 번호: %d", WSAGetLastError());

MessageBox(hwnd, lpszMsg, "에러", MB_OK);

} else {

wsprintf(lpszMsg, "client IP: %s",inet_ntoa(m_clientAddr.sin_addr.s_addr)); MessageBox(hwnd, lpszMsg, "에러", MB_OK);

}

(5) connect()

▶ 연결형 소켓을 개설한 클라이언트가 서버와 통신을 하려면 먼저 서버와 연결되어야 하는데 이를 위하여 클라이언트가 호출하는 함수가 connect()이다.

▶ 이러한 클라이언트의 connect() 호출에 대해, 서버가 미리 호출해 둔 accept()가 정상적으로 처리되어 연결이 완료되면 connect()는 0을 리턴한다.

▶ 다음은 connect()의 사용 예이다.

#define SERVER_PORT_NO 3001

#define SERVER_IP_ADDR "203.252.65.3"

SOCKET s;

SOCKADDR_IN m_addr; /* 연결할 서버의 소켓주소 구조체 */

char lpszMsg[100];

/* 클라이언트가 연결하려고 하는 서버의 소켓주소 구조체를 만든다 */

m_addr.sin_family = AF_INET;

m_addr.sin_port = htons(SERVER_PORT_NO);

m_addr.sin_addr.s_addr = inet_addr(SERVER_IP_ADDR);

connect(s, (LPSOCKADDR)&m_addr, sizeof(m_addr));

(6) closesocket()

▶ 소켓의 사용이 끝난 후 소켓을 닫기 위해 호출하며 BSD의 close()와 같은 동작을 한다.

▶ closesocket()을 호출하면 아직 처리되지 않은 데이터들, 즉 송신 버퍼에 들어 있으나 아직 목적지로 전송되지 않은 패킷 또는 수신 버퍼에 도착해 있으나 아직 윈속이 읽어들이지 않은 패킷들이 모두 처리된 후에 실제로 소켓이 종료된다.

▶ 그러나 이들 미처리 패킷들을 바로 버리거나, 정해진 시간까지만 처리되기를 기다리려면 setsockopt()을 호출하여 소켓의 옵션을 바꾸어 두어야 한다(다음 항목의 setsockopt() 참조).

(7) setsockopt()

▶ 개설된 소켓의 동작에 관한 각종 옵션을 지정하는 데 사용된다. setsockopt()의 사용 문법은 다음과 같다.

▶ setsockopt()은 송수신 버퍼의 크기 지정, OOB(Out of band) 데이터 처리 방법, 방송형 메시지의 전송 허용 등 여러 가지 소켓 옵션 지정에 사용될 수 있다.

▶ 주로 사용되는 optname으로는, 먼저 소켓 종료시 아직 송수신이 완료되지 않은 패킷들의 처리 방안을 지정하거나(SO_LINGER), 소켓의 디버그 정보를 출력하도록 지정하거나(SO_DEBUG), 해당 소켓에서 방송형 메시지를 전송하는 것을 허용하는 것(SO_ BROADCAST) 등이다.

▶ SO_LINGER 옵션은 closesocket() 호출시 전송 시스템(TCP/IP) 내부에 남아 있는 미전송 패킷 또는 아직 수신 처리되지 않은 패킷들의 처리 방안을 미리 지정하는 것으로 이때 optval은 다음과 같은 구조체 linger의 주소를 가리키도록 한다.

struct linger {

int l_onoff; /* LINGER 모드의 선택/취소 */

int l_linger; /* LINGER 시간 */

};

▶ 예를들어 미처리된 패킷들을 바로 버리지 않고 지정된 시간내에 처리되기를 기다리려면(즉, LINGER를 선택하려면) l_onoff를 1로 세트하고 l_linger에 처리 유예시간을 초단위로 지정해 준다.

▶ LINGER를 선택하지 않으려면 즉, 미처리된 패킷들을 closesocket() 호출시 즉시 버리도록 하려면 l_onoff를 0으로 하고 SO_DONTLINGER 옵션을 선택하여 setsockopt()을 호출하면 된다.

▶ 한편 소켓이 처음 만들어지면 후자의 상태인 DONT- LINGER 상태로 되어 있다.

5.2.2 비동기 모드의 사용

▶ 윈도우(특히 3.1버전)는 멀티태스킹이 지원되지 않는 운영체제이므로 어떤 함수 호출이 블록되어 있으면 시스템 전체가 멈추어 있게 된다.

▶ 윈속에서 개설한 소켓은 기본적으로 blocking 모드로 동작하는데 예를들어 accept()를 호출하였을 때 클라이언트로부터 요구된 connect()가 없다면 서버는 블록된다.

▶ 이를 해결하기 위하여 소켓을 비동기 모드로 바꾸는 것이 필요한데 이를 위하여 WSAAsyncSelect()를 사용한다.

▶ WSAAsyncSelect()를 호출함으로써 소켓은 자동으로 비동기 모드로 바뀌고 블록될 수 있는 함수들(accept(), send(), recv() 등)을 비동기 모드로 처리할 수 있게 된다.

▶ 아래는 WSAAsyncSelect()의 사용 문법이다.

int WSAAsyncSelect (

SOCKET s, /* 상태변화(이벤트)를 감지할 소켓번호 */

HWND hWnd, /* 소켓 s에서 이벤트가 발생했을 때

메시지를 받을 윈도우 핸들 */

UINT wMsg, /* 이벤트 발생시 보낼 메시지 */

long lEvent); /* 관심을 갖는 이벤트를 규정하는 bitmask */

▶ WSAAsyncSelect()는 호출 즉시 리턴되며 에러발생시에는 SOCKET_ERROR를 리턴한다.

▶ 에러가 발생하는 경우는 Winsock.dll이 초기화되지 않았거나, 네트웍이 동작하지 않거나, 다른 blocking 함수가 처리중이거나, 함수의 인자를 잘못 입력한 경우이다.

▶ WSAAsyncSelect()가 에러없이 리턴되면 다음과 같은 조건이 만족된 것이다.

1) 소켓이 비동기 모드로 되었다.

2) 원하는 이벤트가 발생하면 그 사실이 메시지로 전달될 것이다.

3) 이 소켓에 accept()가 호출되어 새로운 소켓이 만들어지면 새로운 소켓에 대해서도 WSAAsyncSelect() 호출시 요구한 이벤트를 똑같이 처리해 준다.

▶ WSAAsyncSelect()를 호출하면 소켓은 비동기 모드로 바뀌고 소켓에서 지정된 이벤트가 발생하면 메시지 전달을 통하여 이 사실을 WSAAsyncSelect()를 호출한 응용 프로그램에게 알리게 된다.

▶ 응용 프로그램에서는 각 이벤트에 해당하는 기능을 수행하면 된다.

▶ 이벤트의 종류에는 수신 데이터의 도착, 데이터를 송신할 준비가 됨, 연결요청을 accept할 준비가 됨 등의 사건들이 있는데, 응용 프로그램이 관심을 갖는 이벤트 즉, 알고 싶은 소켓의 상태변화는 WSAAsyncSelect()를 호출할 때 lEvent 인자에 지정할 수 있다.

▶ 해당 소켓에서, lEvent 인자에 지정된 이벤트 중 하나라도 발생하면 윈속은 응용 프로그램에게 이 사실을 알려주게 된다.

▶ 이벤트의 종류는 표 5-3과 같다.

이벤트

의 미

FD_READ 수신된 데이터가 있음
FD_WRITE 데이터 송신이 가능함(송신 버퍼가 비었음)
FD_OOB Out of band 데이터가 수신됨
FD_ACCEPT connect 요청이 있어 accept 처리가 가능함 (서버에서)
FD_CONNECT connect 요청이 성공적으로 이루어짐 (클라이언트에서)
FD_CLOSE 소켓 종료가 이루어짐

표 5-3 WSAAsyncSelect()에서 다루는 이벤트 종류

▶ 표 5-3에서 FD_READ 이벤트는 해당 소켓에 데이터가 수신되어 있으며 현재 이를 읽을 수 있다는 것을 알리는 메시지이다.

▶ 이때 응용 프로그램은 소켓 종류에 따라(TCP또는 UDP) recv() 또는 recvfrom()으로 데이터를 읽으면 된다.

▶ FD_WRITE는 연결형 소켓이 상대방 소켓과 처음 연결될 때, 즉 서버에서는 accept()가 성공하거나 클라이언트에서는 connect()가 성공한 때에 처음 발생한다. 이 때 응용 프로그램은 send()를 호출하여 데이터를 송신할 수 있다.

▶ 데이터가 송신버퍼를 떠나 송신버퍼가 비게 되어도 FD_ WRITE가 발생하는데, 이것은 이제 새로운 송신이 가능하다는 것을 의미한다.

▶ FD_ACCEPT는 서버 소켓에서 발생하는데, 클라이언트가 connect()로 연결요청을 해왔으므로 서버가 accept()를 처리할 수 있다는 것을 알려준다. 즉, 해당 소켓으로 accept()를 호출할 수 있음을 알려준다.

▶ FD_CONNECT는 클라이언트에서 발생하는데 connect() 시스템 콜이 성공적으로 이루어졌을 때 발생하는 이벤트이다.

▶ 수신측에 도착한 모든 패킷은 일반적으로 도착한 순서대로 버퍼에 저장된 후 차례대로 처리된다.

▶ OOB 데이터는 이러한 순서를 무시하고 우선적으로 처리되어야 할 데이터를 말하며 이를 처리하기 위하여 OOB 데이터가 도착하면 FD_OOB 이벤트가 발생한다.

▶ OOB 데이터는 'Ctrl-C'같은 제어 문자를 전달하는데 사용된다.

▶ WSAAsyncSelect()는 각 소켓에 대하여 한 번만 부르는 것이 안전하다. 만약 WSAAsyncSelect()를 중복하여 호출하면 이전에 호출된 WSAAsyncSelect()의 각종 옵션은 무시되므로 주의하여야 한다.

▶ 이전에 지정한 WSAAsyncSelect() 옵션을 취소하여 어떤 이벤트 발생에 대한 메시지를 수신하고 싶지 않으면 wMsg=0, lEvent=0로 하여 WSAAsyncSelect()를 다시 호출하면 된다.

▶ 아래는 소켓 m_s로 클라이언트의 접속요청 connect()가 접수되어 FD_ACCEPT 이벤트가 발생하면 사용자 메시지(WM_USER + 1)를 보내도록 하는 예이다.

WSAAsyncSelect(m_s, m_hWnd, WM_USER + 1, FD_ACCEPT);

▶ 이벤트를 처리하는 프로그램에서는 이벤트의 종류를 알기 위하여 변수 lParam를 참조하여야 하는데 lParam의 하위 16비트에는 발생한 이벤트의 종류가 실려있고 상위 16비트에는 에러코드가 실려있다.

▶ 이러한 이벤트 종류와 에러코드를 lParam에서 편리하게 찾아내기 위하여 매크로 WSAGETSELECTEVENT()와 WSA- GETSELECTERROR()가 다음과 같이 정의되어 있다.

#define WSAGETSELECTEVENT(lParam) LOWORD(lParam)

#define WSAGETSELECTERROR(lParam) HIWORD(lParam)

▶ 아래의 코드는 응용 프로그램에서 발생한 이벤트가 FD_ACCEPT인지를 즉, 클라이언트로부터 연결요청이 와서 이벤트가 발생했는지를 확인하고 accept()를 호출하는 예이다.

if (WSAGETSELECTEVENT(lParam) == FD_ACCEPT) {

m_clientS = accept(m_s, NULL, NULL);

}

▶ 어떤 소켓에서 이벤트가 하나 발생하면 메시지 전송은 한번만 일어난다. 그러나 이 소켓에 같은 종류의 이벤트가 다시 발생할 수 있는데, 이와같이 같은 이벤트가 다시 발생할 때마다 새로운 메시지가 응용 프로그램으로 전달되도록 하는 것이 필요하다.

▶ 예를들어 FD_READ 이벤트가 발생한 소켓에 응용 프로그램이 recv() 함수를 호출하여 데이터를 읽어들인 후에 새로운 데이터가 수신되면 FD_READ 이벤트가 다시 발생하여야 이를 읽을 수 있다.

▶ 이것은 recv() 호출시 내부적으로 자동으로 처리되도록 되어 있다(즉, revc() 호출이 WASAsyncSelect()를 다시 호출한 효과를 준다).

▶ 표 5-4에 같은 종류의 메시지를 반복하여 발생시키도록 하는 함수들과 해당 이벤트를 정리하였다.

▶ 예를들어 FD_ACCEPT 이벤트가 발생하였을 때 accept()를 호출하여 이를 처리하면 새로운 FD_ACCEPT가 발생할 때 같은 종류의 메시지가 다시 응용 프로그램에게 전달된다.

메시지 반복 발생 함수

관련 이벤트

accept() FD_ACCEPT
send(), sendto() FD_WRITE
recv(), recevfrom() FD_READ
recv() FD_OOB

표 5-4 메시지 반복 발생 함수

5.2.3 윈속의 IP 주소 변환 함수

(1) WSAAsyncGetHostByAddr()

▶ IP 주소로부터 해당 호스트의 이름과 기타 정보를 알아내기 위한 비동기 함수이며 BSD 소켓의 gethostbyaddr()과 마찬가지로 hostent 구조체를 리턴한다.

▶ hostent 구조체의 내용은 아래와 같다.

struct hostent {

char FAR * h_name; /* 호스트 이름 */

char FAR * FAR * h_aliases; /* 호스트의 별명 목록 */

shor h_addrtype; /* 호스트 주소 타입 */

short h_length; /* 주소의 길이 */

char FAR * FAR * h_addr_list; /* 주소 목록 */

#define h_addr h_addr_list[0] /* 대표적인 주소를 h_addr에 저장 */

};

▶ WSAAsyncGetHostByAddr()의 사용 문법은 다음과 같다.

HANDLE WSAAsyncGetHostByAddr (

HWND Hwnd, /* 윈도우 핸들 */

unsigned int Msg, /* 이 함수 종료시 전송할 메시지 */

const char *addr, /* 호스트의 IP 주소 */

int len, /* addr의 길이 */

int type, /* 주소 타입으로 인터넷에서는 AF_INET */

char *buf, /* hostent 구조체의 포인터 */

int buflen); /* buf의 길이 */

▶ 아래는 WSAAsyncGetHostByAddr()의 사용 예인데 IP 주소가 203.252.65.3인 호스트의 도메인 네임을 메시지 박스에 출력한다.

▶ 아래에서 PHOSTENT는 hostent 구조체를 가리키는 포인터 타입을 지정하며 MAXGETHOSTSTRUCT는 hostent 구조체를 담기에 충분한 크기의 상수로 Winsock.h에 정의되어 있다.

u_long m_Addr ;

char m_lpszHostEntryBuf[MAXGETHOSTSTRUCT] ;

PHOSTENT m_phostent; /* hostent 구조체 포인터 */

m_Addr = inet_addr("203.252.65.3");

WSAAsyncGetHostByAddr(m_hWnd, WM_USER_ASYNCGETHOSTBYADDR,

(char *)&m_Addr, 4, PF_INET, m_lpszHostEntryBuf, MAXGETHOSTSTRUCT) ;

m_phostent = (PHOSTENT)m_lpszHostEntryBuf ;

MessageBox(hwnd, m_phostent->h_name, "호스트 이름", MB_OK);

▶ 한편 WSAAsyncGetXByY과 같은 형태의 비동기 함수의 실행 결과는 lParam에 들어있게 된다.

▶ lParam의 상위 16비트에는 에러코드가 하위 16비트에는 비동기 함수 실행 결과가 들어있는 버퍼의 크기가 들어 있다.

▶ 이러한 에러 값과 버퍼의 크기 값을 lParam으로부터 편리하게 알아내기 위하여 WSAGETASYNCERROR()와 WSA- GETASYNCBUFLEN() 매크로가 각각 정의되어 있다.

(2) WSAAsyncGetServByName()

▶ 이 함수는 ftp, mail과 같은 TCP/IP 표준 응용 프로그램의 포트번호를 찾기 위해 사용되는 비동기 함수이다.

▶ 이 함수가 성공적으로 수행되면 servent라는 구조체의 포인터를 리턴하는데 servent 구조체의 내용은 다음과 같다.

struct servent {

char FAR * s_name; /* 공식적인 서비스 이름 */

char FAR * FAR * s_aliases; /* 서비스의 별명 리스트 */

short s_port; /* 포트번호 */

char FAR * s_proto; /* 프로토콜 종류 */

};

▶ WSAAsyncGetServByName()의 사용 문법은 다음과 같다.

HANDLE WSAAsyncGetServByName (

HWND Hwnd, /* 이 함수의 메시지를 수신할 윈도우 핸들 */

unsigned int Msg, /* 이 함수 종료시 전송할 메시지 */

const char *name, /* 포트번호를 알고자 하는 서비스 이름 */

const char *proto, /* TCP나 UDP를 지정(또는 NULL) */

char *buf, /* servent 구조체의 포인터 */

int buflen); /* buf의 길이 */

▶ 이 함수의 사용 예로 UDP 프로토콜로 구현된 time 서비스의 포트번호를 찾아내어 화면에 출력하는 예를 아래에 보였다.

▶ 여기서 PSERVENT는 servent 구조체를 가리키는 포인터 타입을 말한다.

PSERVENT m_pservent; /* servent 구조체 포인터 */

char m_lpszMsg[100]; /* 화면에 출력할 메시지 버퍼 */

char m_lpszServEntryBuf[MAXGETHOSTSTRUCT];

WSAAsyncGetServByName(m_hWnd, WM_USER_ASYNCGETSERVBYNAME, "time",

"udp", m_lpszServEntryBuf, MAXGETHOSTSTRUCT) ;

m_pservent = (PSERVENT)m_lpszServEntryBuf;

wsprintf(m_lpszMsg, "%s 서비스 포트 : %d", m_pservent->s_name,

m_pservent->s_port);

(3) WSAAsyncGetHostByName()

▶ 이 함수는 호스트 이름을 사용하여 IP 주소를 찾아내는 함수이며 사용 문법은 아래와 같다.

HANDLE WSAAsyncGetHostByName (

HWND hwnd, /* 메시지를 수신할 윈도우 핸들 */

unsigned int wMsg, /* 수신할 메시지 */

char FAR *name, /* 찾을 호스트 네임의 포인터 */

char FAR *buf, /* 결과가 리턴되는 버퍼의 포인터 */

int bufflen); /* 버퍼의 길이 */


현재강좌 : Winsock 프로그래밍 이전: 5.1 윈속의 이해 다음: 5.3 윈속 채팅 서버