현재강좌 : Winsock 프로그래밍 다음: 5.2 윈속의 동작


5.1 윈속의 이해

▶ 윈속은 마이크로소프트(Microsoft) 윈도우3.1 또는 윈도우95에서 제공하는 TCP/IP 프로그래밍을 위한 API(Appli- cation Program Interface)로서 사용방법 및 기본 동작이 UNIX의 BSD 소켓과 거의 같으며 BSD 소켓과 마찬가지로 클라이언트-서버 모델을 기초로 하여 이용된다.

5.1.1 UNIX BSD 소켓과 윈속의 차이

▶ 윈속의 사용방법과 문법이 BSD 소켓의 경우와 유사하기는 하지만 윈속과 BSD 소켓용으로 작성된 프로그램은 서로 호환성이 없으며 이 두 가지 소켓 응용 프로그램이 호환성을 갖으려면 많은 부분을 수정하여야 한다.

▶ 먼저 윈도우와 UNIX 운영체제의 차이점을 비교하면 다음과 같다.

▶ 첫째, UNIX와 달리 윈도우3.1 이하에서는 멀티태스킹을 지원하지 않는다.

▶ 윈도우95는 멀티태스킹과 유사한 멀티쓰레드(multi- thread)를 지원하기는 하지만 윈속이 처음 소개된 것은 멀티태스킹이 지원되지 않는 윈도우3.1에서였으며 윈도우95와 윈도우3.1에서의 프로그램 호환성을 유지하기 위하여 윈속은 윈도우95에서도 계속 '멀티태스킹이 지원되지 않는' 환경을 가정하고 있다.

▶ 이와같이 UNIX와 윈도우 운영체제의 기본적인 차이로 인하여 윈속은 BSD 소켓과 내부적으로 매우 다른 특성을 갖고 있으며 프로그램 작성시에도 이를 고려하여야 한다.

▶ 둘째로 윈속은 주로 윈도우 프로그래밍으로 구현되는데, 윈도우 프로그래밍이 '메시지 구동형 프로그래밍'이라는 것이 UNIX 프로그램과 크게 다른 점이다.

▶ 메시지는 '어떤 사건(event)의 발생을 알리는 신호'라고 할 수 있는데, 메시지는 사용자가 만든 응용 윈도우들이 서로 주고받거나, 윈도우 운영체제가 사용자 윈도우에게 전달하는 것이다.

▶ 한편 윈속 프로그래밍에서는 윈속의 기능을 제공하는 라이브러리로 DLL(Dynamic Link Library)인 Winsock.dll을 사용한다.

▶ 이러한 DLL 라이브러리는 컴파일시 링크되는 것이 아니라 응용 프로그램 수행시 링크된다.

▶ DLL을 사용하는 것의 장점은 여러 응용 프로그램이 하나의 DLL을 공유하여 사용할 수 있다는 것이다.

▶ 예를들면 하나의 Winsock.dll이 수행중이면 여러 윈속 응용 프로그램들이 이것을 동시에 사용할 수 있게 된다.

▶이외에도 BSD 소켓과 윈속은 소켓을 열고 닫는 데 쓰이는 함수가 다른데 윈속의 경우 WSAStartup(), WSACleanup()함수를 통해 소켓과 Winsock.dll과의 관계를 설정, 해제해 주는 과정이 필요하다.

▶ BSD 소켓처럼 close()를 이용해 소켓을 닫는 것이 아니라 closesocket() 함수를 사용해 닫아야 한다.

▶ 이와같은 차이는 UNIX와 윈도우 운영체제의 특성의 차이로 인한 것이며 함수의 이름만 다른 것이 아니라 함수의 구체적인 내부 동작도 서로 다르다.

5.1.2 윈속의 동작모드

▶ BSD 소켓과 마찬가지로 윈속이 제공하는 소켓도 다음과 같은 세 가지 동작모드 (operating mode)를 가지고 있다.

(1) Blocking 모드

▶ socket() 시스템 콜을 호출하여 하나의 소켓을 만들면 이것은 디폴트로 blocking 모드로 동작하는 소켓이 된다.

▶ 이러한 blocking 모드의 소켓을 대상으로 accept(), close- socket(), connect(), recv(), recvfrom(), send(), sendto()와 같은 함수를 호출하면 함수가 원하는 동작을 완료할 때까지 함수를 호출한 프로세스가 blocking될 수 있다.

▶ 즉, blocking 모드의 소켓에서는 응용 프로그램에서 위와같은 함수를 호출하였을 때 그 동작이 완료되어 함수가 리턴되어야 다음 작업을 할 수 있게 된다.

▶ 한편 멀티태스킹이 지원되는 UNIX 컴퓨터에서는 block- ing 모드의 소켓을 사용하여 어떤 응용 프로그램이 블록되어도 컴퓨터 전체 동작에는 큰 문제가 되지 않는다.

▶ 왜냐하면 멀티태스킹 운영체제에서는 각각의 프로세스들이 독립적으로 수행되므로 한 프로세스가 블록되어 있어도 다른 프로세스들은 계속 수행될 수 있기 때문이다.

▶ 그러나 윈도우3.1과 같은 단일 태스킹 운영체제에서는 프로그램이 한 곳에 블록되어 있으면 PC 전체가 블록될 수 있다.

▶ 또한 blocking 모드의 소켓을 사용하면 하나의 프로그램에서 여러 개의 소켓을 동시에 개설하여 각각의 입출력을 처리하는 형태의 응용 프로그램을 작성하기가 어렵다.

▶ 따라서 윈속 프로그래밍에서는 이를 해결하기 위하여 소켓을 non-blocking 모드 또는 비동기 모드로 변경하여 사용하는 것이 필요하다.

(2) Non-blocking 모드

▶ Non-blocking 모드 소켓이란 accept(), closesocket(), connect(), recv(), recvfrom(), send(), sendto()와 같은 함수가 호출되었을 때 함수의 원하는 동작이 완료되는 것과 무관하게 일단 함수가 즉시 리턴되는 소켓을 말한다.

▶ 소켓이 처음 만들어지면 디폴트로 blocking 모드가 되는데 프로그래머는 필요에 따라 blocking 모드의 소켓을 non-blocking 모드로 바꿀 수 있다.

▶ Non-blocking 모드의 소켓을 사용하는 이유는 응용 프로그램이 이곳에서 멈추어 있지(block) 않게 하기 위해서이다.

▶ Non-blcoking 모드의 소켓에서 함수 호출이 즉시 리턴되었을 때 그 결과는 다음과 같이 두 가지로 종류로 나눌 수 있다.

1) 성공적인 리턴: 함수의 동작이 즉시 성공적으로 수행되었음.

2) 에러 리턴: 함수 수행중 에러가 발생했거나 함수가 블록되었음.

▶ 즉, non-blocking 모드 소켓에서 즉시 리턴된 함수 호출 결과는 성공적인 경우도 있고 실패한 경우도 있을 수 있다.

▶ 에러 리턴인 경우 에러코드가 WSAEWOULDBLOCK이면, 이것의 의미는 함수의 동작이 잘못되었다는 것이 아니라 '이 함수가 동작 완료될 때까지 기다린다면 block될 수 있다'는 것을 의미한다.

▶ 이와같이 에러코드가 WSAEWOULDBLOCK인 경우 그 원인은 다음의 두 가지 중 하나가 된다.

1) Winsock.dll이 함수가 원하는 동작을 시작했으나 아직 종료되지 않았음.

2) 함수의 동작이 시작되지 못했으며 다시 재시도를 필요로 함.

▶ 위의 첫번째 경우는 응용 프로그램이 함수의 동작 완료 시점을 알아서 그 결과를 처리하여야 하며 두번째 경우는 응용 프로그램이 이 함수가 성공적으로 시작될 때까지 함수를 계속 반복하여 호출해야 한다.

▶ 어떤 경우이든 non-blocking 모드 소켓의 처리는 다소 복잡하며 따라서 다음에 설명할 비동기 모드를 사용하는 것이 편리하다.

(3) Asynchronous(비동기) 모드

▶ 비동기 모드에서도 non-blocking 모드에서처럼 소켓 관련 함수의 호출이 바로 리턴된다.

▶ 그러나 비동기 모드에서는 non-blocking 모드와 달리, 함수의 동작이 완료되는 시점, 또는 함수의 실행이 시작되지 못한 경우 다음에 다시 재시도하여야 하는 시점을 시스템(Winsock.dll)이 메시지 처리 방식으로 나중에 응용 프로그램에게 알려준다.

▶ 즉, 비동기 모드의 소켓에서 소켓관련 함수의 실행결과의 에러코드가 WSAEWOULDBLOCK일 때의 의미는 다음과 같다.

1) 함수의 동작이 완료되면 그 때 Winsock.dll이 메시지를 통하여 동작의 완료를 알려주거나,

2) 함수의 동작이 시작하지 못했으며, 함수를 다시 호출해야 할 시점을 나중에 비동기적으로 알려주겠음.

▶ 앞에 언급한 바와 같이 UNIX와 같은 운영체제에서는 함수가 블록되는 것이 문제가 되지 않으므로 반드시 비동기 모드의 소켓을 사용할 필요는 없다.

▶ 그러나 윈속에서는 비동기 모드로 소켓을 사용하는 것이 편리하다.

(4) Blocking 함수

▶ 한편 blocking 모드의 소켓이 아니더라도 (즉, non- blocking 또는 비동기 모드의 소켓에서도) 어떤 함수들은 함수 자체의 특성상 블록될 수 있는 함수가 있다.

▶ 이러한 함수들은 네트웍 시스템(즉, TCP/IP)이 어떤 정보를 얻어내야만 그 결과를 리턴할 수 있으며 그 정보를 얻는데 다소 시간이 필요하기 때문이다.

▶예를들어 호스트의 도메인 네임을 입력하고 이 호스트의 IP 주소 등의 정보를 얻어내는 함수 gethostbyname()이 처리되기 위하여는, 네트웍 시스템이 DNS 서버에게 필요한 정보를 문의하고 그 결과가 도착할 때까지 기다려야만 한다.

▶ 이러한 함수들을 'blocking 함수'라고 부르며 표 5-1에 대표적인 blocking 함수들을 정리하였다.

함 수

기 능

select() 소켓의 상태 변화(읽기, 쓰기, 오류 발생)를 알려줌
gethostbyaddr() 호스트 주소로부터 호스트 정보를 얻음
gethostbyname() 호스트 이름으로부터 호스트 정보를 얻음
getprotobyname() 프로토콜 이름으로부터 프로토콜 번호를 얻음
getprotobynumber() 프로토콜 번호로부터 프로토콜 이름을 얻음
getservbyname() 서비스 이름으로부터 서비스 정보를 얻음
getservbyport() 포트번호로부터 서비스 정보를 얻음

표 5-1 대표적인 blocking 함수

▶ 표 5-1에 소개한 blocking 함수들은 소켓의 동작 모드에 관계없이 항상 블록될 수 있는 함수이다

▶ 이러한 함수를 사용할 때 프로그램이 블록되는 문제를 해결하기 위하여, 윈속에서는 이들 blocking 함수와 같은 기능을 수행하면서 실제로는 비동기 모드로 동작하는 즉, 함수 실행결과를 비동기적으로 알려주는 비동기 함수들을 제공하고 있다.

▶ 이들을 표 5-2에 정리하였으며 자세한 내용은 5.2.2절에서 설명하겠다.

비동기 함수

기 능

WSAAsyncSelect() 소켓의 I/O 상태 변화 즉, 연결요청, 데이터 수신, 송신 버퍼의 사용가능 등의 이벤트를 시스템이 메시지를 통하여 알려주도록 요청함
WSAAsyncGetHostByAddr() 호스트 주소로부터 호스트 정보를 얻음
WSAAsyncGetHostByName() 호스트 이름으로부터 호스트 정보를 얻음
WSAAsyncGetProtoByName() 프로토콜 이름으로부터 프로토콜 번호를 얻음
WSAAsyncGetProtoByNumber() 프로토콜 번호로부터 프로토콜 이름을 얻음
WSAAsyncGetServByName() 서비스 이름으로부터 서비스 정보를 얻음
WSAAsyncGetServByPort() 포트번호로부터 서비스 정보를 얻음

표 5-2 Blocking 함수(표 5-1)의 기능을 수행하는 윈속의 비동기 함수

5.1.3 윈속의 시작과 종료

(1) WSAStartup()

▶ WSAStartup()은 윈속 관련 함수들을 사용하기 전에 응용 프로그램이 반드시 호출해야 하는 함수로, 윈속 라이브러리인 Winsock.dll을 초기화한다.

▶ WSAStartup()은 일반적으로 윈도우 메시지 WM_CREATE나 WM_INITDIALOG의 처리 부분에서 호출된다.

▶ WSAStartup()은 두 개의 인자를 필요로 하는데, 응용 프로그램이 요구하는 최소의 윈속 버전(1.0 또는 1.1) 값과, WSAStartup()이 수행된 후 네트웍 시스템(Winsock.dll)이 윈속 관련 정보를 알려주는데 사용할 윈속정보 구조체, 즉 WSADATA 타입의 구조체 변수의 주소를 필요로 한다.

▶ 다음은 응용 프로그램이 사용할 윈속의 버전값을 지정하고 WSAStartup()을 호출하는 예이다.

WORD wVersionRequired ; /* 사용할 윈속 버전 값 */

WSADATA wsaData ; /* 윈속정보를 담을 구조체 */

wVersionRequired = MAKEWORD(0, 1); /* 윈속 버전 값을 1.0으로 세팅 */

WSAStartup(wVersionRequired, &wsaData); /* 윈속의 시작 */

▶ 위에서 wsaData는 WSADATA 타입의 구조체 변수로 여기에는 WSAStartup() 함수가 개설하여 앞으로 사용할 윈속에 관한 정보가 수록되어 리턴된다.

▶ WSADATA 구조체의 정의는 다음과 같다(Winsock.h 파일 참조).

typedef struct WSADATA {

WORD wVersion; /* 윈속 버전 (예 1.0) */

WORD wHighVersion; /* 최상위의 윈속 버전 (예 1.1) */

char szDescription[WSADESCRIPTION_LEN+1]; /* 회사정보 */

char szSystemStatus[WSASYS_STATUS_LEN+1]; /* 구성정보 */

unsigned short iMaxSockets; /* 한 프로세스가 열 수 있는 소켓 수 */

unsigned short iMaxUdpDg; /* UDP 패킷 크기 */

char FAR *lpVendorInfo; /* 회사별 데이터 구조의 포인터 */

} WSADATA;

 

(2) WSACleanup()

▶ 윈속의 사용을 종료할 때 호출하며 WSAStartup()에 대응되는 함수이다.


현재강좌 : Winsock 프로그래밍 다음: 5.2 윈속의 동작