소켓 생성과 닫기

bolee·2022년 4월 4일
0

(2-5 그림)

이번에는 소켓을 생성하고 닫는 방법을 알아볼 것이다.

소켓 생성

소켓을 사용해 통신을 하기 위한 기본 요건은 통신 양단이 같은 프로토콜을 사용하는 것이다.

socket() 함수는 사용자가 요청한 프로토콜을 사용해 통신할 수 있도록 내부적으로 리소스를 할당하고, 접근할 수 있는 일종의 핸들 값(SOCKET 타입, 32비트 정수)을 리턴한다. 이 값을 소켓 디스크립터(socket descriptor)라고 하고, 각종 소켓 함수를 호출할 때 인자로 전달하여 사용한다.

// 성공: 새로운 소켓, 실패: INVALID_SOCKET
SOCKET socket(
	int af,
    int type,
    int protocol
)
  • af: 주소 체계 지정
  • type: 소켓 타입 지정
  • protocol: 사용할 프로토콜 지정

주소 체계 (address family)

통신을 하기 위해 통신 상태를 유일하게 지정할 수 있는 주소가 필요하다. 주소 체계(address family)란 이러한 주소 지정 방법을 지칭하는 용어이다. 주소 체계는 네트워크 프로토콜의 종류에 따라 달라진다.

winsock2.h 또는 ws2def.h 파일을 찾아보면 다음과 같이 AF_로 시작하는 상수를 찾을 수 있다. 자신이 사용할 프로토콜에 대응하는 값을 선택하여 socket()함수에 사용하면 된다. 예를 들어 IPv4 기반 TCP나 UDP 프로토콜을 사용하려면 AF_INET 값을 선택하면 된다.

...
#define AF_INET		2		// Internetwork: UDP, TCP, etc.
...
#define AF_INET6	23		// Internetwork Version 6
...
#define AF_IRDA		26		// IrDA
...
#define AF_BTH		32		// Blutooth RFCOMM/L2CAP protocols
...

소켓 타입 (socket type)

소켓 타입은 사용할 프로토콜의 특성을 나타내는 값이다. 자주 사용하는 소켓 타입을 요약하면 아래와 같다.

소켓 타입특성
SOCK_STREAM신뢰성 있는 데이터 전송 기능 제공, 연결형 프로토콜
SOCK_DGRAM신뢰성 없는 데이터 전송 기능 제공, 비연결형 프로토콜

소켓 타입은 네트워크 프로토콜의 종류에 따라 달라진다. 주로 사용되는 TCP나 UDP 프로토콜을 사용하려면 주소 체계와 소켓 타입을 아래 같이 설정하면 된다.

사용할 프로토콜주소 체계소켓 타입
TCPAF_INET 또는 AF_INET6SOCK_STREAM
UDPAF_INET 또는 AF_INET6SOCK_DGRAM

AF_INETPF_INET
간혹 주소체계로 AF_ 대신 PF_(Protocol Family)로 시작하는 상수를 사용하는 코드를 볼 수 있다. 이런 코드는 초기 버클리 유닉스에서 소켓 시스템 콜을 설계할 당시 socket() 함수에는 PF_*상수를 사용하고, 소켓 주소 구조체에는 AF_*상수를 사용하도록 정의했다는 사실에서 유래한 것이다. 그러나 PF_*AF_* 상수의 실제 정의가 같고, 현실적으로 AF_* 형태를 많이 사용한다.

프로토콜 (protocol)

위의 표와 같이 주소 체계와 소켓 타입만으로 프로토콜을 결정할 수 있는 경우가 있다. 그러나 일반적으로 주소 체계와 소켓 타입이 같아도 이에 해당하는 프로토콜이 2개 이상 존재할 수 있다. 이 경우 프로토콜을 명시적으로 지정해줘야 한다.
주로 사용되는 TCP나 UDP 프로토콜을 사용하려면 주소 체계와 소켓 타입 그리고 프로토콜을 아리와 같이 설정할 수 있다.

사용할 프로토콜주소 체계소켓 타입프로토콜
TCPAF_INET 또는 AF_INET6SOCK_STREAMIPPROTO_TCP
UDPAF_INET 또는 AF_INET6SOCK_DGRAMIPPROTO_UDP

그러나 TCP나 UDP 프로토콜의 경우 주소 체계나 소켓 타입만으로도 프로토콜을 결정할 수 있기 때문에 일반적으로 프로토콜 부분에 0을 사용한다.

소켓 닫기

소켓을 사용한 통신을 마치면 관련 리소스를 반환해야 한다. closesocket() 함수는 해당 소켓을 닫고 관련 리소스를 반환한다.

// 성공: 0, 실패: SOCKET_ERROR
int closesocket(
	SOCKET s
);

소켓 생성과 닫기

아래 코드는 위에서 살펴본 것을 활용해 소켓을 생성하고 닫는 예제이다.

#pragma comment(lib, "ws2_32")
#include <winsock2.h>

// 소켓 함수 오류 출력 후 종료
void err_quit(char *msg)
{
	LPVOID lpMsgBuf;
    FormatMessage(
    	FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMATE_MESSAGE_FROM_SYSTEM,
        NULL, WSAGetLastError(),
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR) &lpMsgBuf, 0, NULL);
	MessageBox(NULL, (LPCTSTR) lpMsgBuf, msg, MB_ICONERROR);
    LocalFree(lpMsgBuf);
    exit(1);
}

int main(int argc, char *argv[])
{
	// 윈속 초기화
    WSADATA wsa;
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
    	return 1;
    MessageBox(NULL, "윈속 초기화 성공", "알림", MB_OK);
    
    // socket() - 소켓 생성
    SOCKET tcp_sock = socket(AF_INET, SOCK_STREAM, 0);
    if (tcp_sock == INVALID_SOCKET)	err_quit("socket()");
    MessageBox(NULL, "TCP 소켓 생성 성공", "알림", MB_OK);
    
    // closesocket() - 소켓 닫기
    closesocket(tcp_socket);
    
    // 윈속 종료
    WSACleanup();
    return 0;
}

캄피일, 링크 후 실행 결과는 다음과 같다. 소켓 생성이 성공했음을 알 수 있다.

참고 자료
김성우 저, "TCP/IP 윈도우 소켓 프로그래밍", 한빛아카데미, 2018

0개의 댓글