Windows Socket Programming (1) - Winsock 시작하기, 소켓 생성

Dosoon·2023년 5월 11일
0

Winsock이란?

UNIX 소켓을 기반으로 윈도우 환경에서 사용할 수 있게 만든 네트워크 프로그래밍 인터페이스.

UNIX 소켓과는 다르게...

  • 윈도우 소켓은 DLL을 통해 사용 가능하다. 따라서 초기화(WSAStartup()) 및 종료(WSACleanup()) 작업을 위한 함수가 필요하다.
  • 윈도우는 대개 GUI 기반이므로 이를 위한 확장 함수가 존재한다.
  • 윈도우는 멀티스레드를 OS에서 지원하므로, 멀티스레드 환경에서 안정적인 구조와 함수가 필요하다.
  • 유닉스 소켓과의 코드 호환성과 이식성이 뛰어나다.

그러나, 유의사항

  • 응용 레벨의 프로토콜은 프로그래머가 설계해야 한다.
  • 호스트 간 Byte Ordering이 다르거나 처리 비트 단위가 다른 경우 응용 레벨에서 처리해야 한다.

Winsock을 사용하는 응용 프로그램의 흐름도는 Winsock 초기화소켓 생성통신소켓 닫기Winsock 종료 와 같다.

Winsock을 사용하려면 먼저 사용할 Winsock 버전을 요청하여 Winsock 라이브러리를 초기화해야한다. 이때 사용하는 것이 WSAStartup() 함수이다.

WSAStartup()

예시 코드

// int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData)

WSADATA wsa;
WSAStartup(MAKEWORD(3, 2), &wsa != 0)

Winsock 초기화에는 요청 버전WSADATA 구조체를 파라미터로 전달한다.

WSADATA에서는 Winsock 버전, 시스템이 지원하는 최상위 Winsock 버전 등에 대한 정보를 얻을 수 있다. 응용 레벨에서 그다지 쓸 일은 없다.

반환 값

성공 시 0, 실패 시 오류 코드 (MSDN 참고!)를 반환한다.

💡 WSAStartup() 은 직접 오류 코드를 반환한다!

일반적으로 소켓 함수의 에러 코드를 얻을 때에는 WSAGetLastError() 함수를 사용한다. 그러나 이 함수는 이름 그대로 호출 스레드의 가장 마지막 에러를 반환하기 때문에 부정확할 수 있다. 따라서 WSAStartup() 은 스스로 오류 코드를 반환한다.

💡 비동기 함수의 오류 코드 확인을 위해 WSAGetLastError() 를 사용해서는 안된다.

아직 비동기 모델까지는 포스팅하지 않았지만. 위의 이유와 마찬가지로 그러하다.
공식 문서에도 명시되어 있다. 비동기 함수의 경우에는 lParam에 오류 코드 값이 전달된다.


WSACleanup()

예시 코드

// int WSACleanup(void)

WSACleanup();

별도의 매개변수 없이 호출한다. 이 함수를 호출하면 OS에게 사용이 끝났음을 알리고 리소스를 반환한다.

반환 값

성공 시 0, 실패 시 SOCKET_ERROR
자세한 에러 코드는 WSAGetLastError() 함수를 호출해 파악할 수 있다.


socket()

예시 코드

// SOCKET socket(int af, int type, int protocol)

SOCKET TCP_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
SOCKET UDP_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

AF(Address Family) 는 주소 체계, Type 는 소켓 타입, Protocol 은 프로토콜을 의미한다.

주소 체계

상수명용도
AF_INETIPv4 기반 TCP, UDP
AF_INET6IPv6 기반 TCP, UDP
AF_IRDAIrDA
AF_BTH블루투스

소켓 타입

상수명설명
SOCK_STREAMTCP 프로토콜 기반 - 신뢰성, 연결형
SOCK_DGRAMUDP 프로토콜 기반 - 비신뢰성, 비연결형

프로토콜

상수명설명
IPPROTO_TCPTCP 프로토콜
IPPROTO_UDPUDP 프로토콜

반환 값

성공 시 SOCKET, 실패 시 INVALID_SOCKET
성공해서 받아온 SOCKET 핸들 값을 사용해 소켓 함수를 호출한다.


closesocket()

예시 코드

// int closesocket(SOCKET s)

int closeRet = closesocket(socket);

이 함수를 호출하면 해당 소켓을 닫고 리소스를 반환한다. 이때의 동작은 Linger 옵션에 따라 달라질 수 있다.

💡 Linger 옵션에 따른 closesocket() 동작 방식 차이

일단 한 번 FIN을 보내면 4-way-handshake로의 진입(graceful shutdown) 을 막을 수 없다.
이 경우 FIN을 보낸 쪽(먼저 연결을 종료한 쪽)에 TIME_WAIT이 남게 되어 부하가 발생한다. 이를 위해 Linger 옵션을 조정하여 RST를 날리고, 연결을 강제 종료할 수 있다. (→ TIME_WAIT이 남지 않는다.)


시간이 되면 closesocket() 과 shutdown() 의 차이, Linger 옵션에 따른 TCP State 변화에 대해서도 기록하기!

profile
Server Programmer

0개의 댓글