2015. 2. 10.

[Windows Network Programming]윈속 초기화와 종료, 소켓 생성과 닫기

 OSI 7 layer, TCP/IP 등의 네트워크 기본지식에 대한 내용보다는 코드로 어떻게 구현하는지에 집중해서 Windows Network Programming을 공부하고자 한다. 구현하는 내용을 기반으로 해서 소켓프로그래밍을 어떻게 하는지 감을 잡아보자.


윈속 초기화와 종료

 윈속의 초기화와 종료는 이를 수행하는 각각의 함수 한 개씩을 호출하면 끝이 난다. 이번에는 이 함수를 사용하기 위해 필요한 라이브러리는 무엇이고, 이를 링크하는 것까지 다루도록 하자. 함수만 소개하면 정말 내용이 부실해질 것이다.
 먼저 전체코드를 훑어보자. Windows.h 파일은 윈도우즈 시스템 프로그래밍을 하기 위해 필요하다. 이 파일 하나면 윈도우즈와 관련된 대부분은 포함할 수 있다고 생각해도 된다. 바로 아랫줄에 나오는 winsock.h 파일도 Windows.h 파일을 include하면 필요없게 된다. 궁금하면 해보도록 하자. 이번에는 winsock.h 파일을 소개하고자 추가하였다.

 WSADATA라는 보는 자료형이 등장하였다. 이 자료형은 윈도우즈 소켓을 초기화하는 정보를 보관하는 구조체이다. 윈속 버전을 인자로 전달한다. 그리고 WSAStartup() 함수를 통해 생성한 윈속에 대한 세부 사항을 우리가 선언한 WSADATA에 저장할 수 있다. WSAStartup() 함수는 프로그램에 사용할 윈속 버전을 요청함으로써 윈속 라이브러리(ws2_32.dll)을 초기화하는 역할을 한다. 그래서 WSAStartup() 함수를 사용하기 위해서는 ws2_32.lib 파일을 링크해야 한다.

먼저 프로젝트 속성으로 가자. 솔루션 탐색기에서 프로젝트를 우클릭하면 가장 아랫쪽에 있을 것이다. 그리고 이를 선택하면 속성창이 뜰 것이다. 우리가 수정해야 하는 부분은 '링커->외부 종속성'이다. 이 부분에 ws2_32.lib을 추가해주면 끝난다.

위 스크린샷은 Visual Studio 2013 영문판

깔끔하게 링크를 추가해주면 WSAStartup() 함수와 같은 윈도우즈 소켓 함수들이 무리없이 실행될 것이다.

 다시 코드로 돌아가자. WSAStartup() 함수를 통해 우리는 운영체제에게 윈속을 생성해달라고 요청하였다. 우리가 만드는 것이 아니다. 윈도우즈가 우리에게 윈속을 쓸 수 있게 만들어주었으면 우리는 감사히 잘 쓰고 나서 해당 자원을 반납해야 한다. 공손하게 윈속을 반납하는 함수가 바로 WSACleanup() 함수이다.  결국 이 두 함수는 운영체제에게 자원을 요청하고 반납하는 세트이다. 중간에 메세지 박스를 만드는 윈도우즈 시스템 함수도 있다. 인자로 전달한 문자열 앞에 왜 L이 붙어있는지 궁금한 사람들은 SBCS, MBCS, WBCS를 검색해보자. 지금은 윈도우즈 네트워크 프로그래밍에 집중하도록 하자.

 결국, 윈속 정보를 저장할 구조체를 선언하고, 윈속을 생성한 후 초기화 데이터를 그 구조체에 저장한 다음 소켓을 종료하였다. 이것이 전부이다. 간단하다.


소켓 생성과 닫기

 WSAStartup() 함수로 윈속을 초기화하여 소켓을 생성할 준비를 완료하였다. 이제 소켓을 생성하도록 하자. 소켓을 이용하여 통신하기 위해서는 통신 양단이 동일한 프로토콜을 사용해야 한다. TCP를 쓸 것인지, UDP를 쓸 것인지 정해야 한다는 의미이다. socket() 함수는 바로 사용자가 요청한 프로토콜을 이용하여 통신할 수 있도록 내부적으로 리소스를 할당하고, 이에 접근할 수 있는 일종의 핸들값(Handle)을 반환한다. 핸들을 모르는 사람은 검색해보도록 하자. 간단하게 말하자면 운영체제가 생성한 자원에 접근할 수 있는 수단으로 생각할 수 있다. 여튼 socket() 함수는 우리가 어떤 프로토콜을 사용할지 결정하는 역할을 한다. 정확하게 말하면 socket() 함수를 통해 주소체계, 소켓 타입, 프로토콜을 지정할 수 있다. 이것들은 모두 socket() 함수의 인자로 전달된다. 함수는 다음과 같다. 

  • 주소 체계: 통신을 하기 위해서는 통신 상대를 유일하게 식별할 수 있는 주소가 필요하다. A가 B에게 이미지 파일을 보내려고 한다. B를 유일하게 식별할 수 있는 주소가 없다면 어떻게 전달하겠는가? 우리가 흔히 말하는 IP 주소도 바로 이런 주소 체계 중 하나이다. 그리고 주소 체계는 네트워크 프로토콜의 종류에 따라 달라진다.
  • 소켓 타입: 이 또한 네트워크 프로토콜의 종류에 따라 달라진다. 가장 많이 사용되는 TCP의 경우 소켓 타입이 SOCK_STREAM이다. TCP의 특성대로 이 소켓은 신뢰성 있는 데이터 전송 기능을 제공하고, 연결지향 프로토콜이다. 네트워크에 대한 이론적인 배경이 있는 사람이라면 고개를 끄덕일 것이다. UDP의 경우 소켓 타입이 SOCK_DGRAM이다. 비신뢰적인 데이터 전송 기능을 제공하고, 비연결지향 프로토콜이다.
  • 프로토콜: 주소 체계와 소켓 타입을 가지고도 어떤 프로토콜을 사용할지 결정할 수 있다. 하지만 주소 체계와 소켓 타입이 같은 프로토콜이 두 개 이상 존재할 수도 있다. 이런 경우 프로토콜을 명시적으로 지정하기 위한 매개변수이다.
 결국 socket() 함수는 주소 체계와 소켓 타입을 통해, 필요하다면 명시적으로 프로토콜을 지정하여, 사용자가 어떤 프로토콜을 이용하여 통신을 있도록 내부적을 리소스를 할당하는 기능을 한다. 예를 들면 다음과 같다. 주소 체계 AF_INET, 소켓 타입 SOCK_STREAM으로 지정하면, 이는 TCP를 사용하겠다는 것을 의미한다. 프로토콜을 명시적으로 IPROTOCOL_TCP로 표현하여 전달할 수 있지만 주소 체계와 소켓 타입만으로 구별할 수 있는 경우는 세 번째 인자는 0으로 전달해도 된다. 참고로 AF_INET, SOCK_DGRAM으로 인자를 전달하면 프로토콜은 UDP가 된다.

 리소스를 할당받았으면 반환해야 한다. 소켓을 닫는 함수는 closesocket()이다. socket() 함수로 반환한 소켓의 핸들값을 인자로 전달하면 된다. 단순하므로 따로 살펴보지는 않겠다. 그럼 소켓을 생성하고 닫는 것까지 코드로 살펴보자.


정리하면

  •  WSAStartup() 함수로 윈속을 초기화하고, WSACleanup() 함수로 윈속을 종료한다.
  • 통신을 하기위해서는 통신의 양단이 동일한 프로토콜을 사용해야 한다. 프로토콜을 지정하는 함수가 socket() 함수이다. closesocket() 함수로 소켓을 닫는다.
  • socket() 함수의 인자로 주소 체계, 소켓 타입, 프로토콜을 전달한다.

댓글 없음:

댓글 쓰기