Computer Science 기본 지식/소켓 프로그래밍

[TCP/IP 소켓 프로그래밍] (1) 소켓 생성

로파이 2021. 3. 26. 15:29

출처 : 열혈 TCP/IP 소켓 프로그래밍 윤성우 저

 

윈도우 기반의 소켓 프로그래밍입니다.

본 편에서는 기본적인 소켓 생성 과정과 기본 인터페이스에 대해 정리합니다.

 

소켓 이란

네트워크를 연결하기 위해 사용되는 인터페이스. 소켓에 주소체계, IP, 포트번호 등을 할당하여 사용한다.

 

IP 주소와 포트번호를 할당하는 이유

 

IP 주소는 네트워크를 식별하고 목적지 호스트에게 전달하기 위해 사용한다. IP 주소만으로 호스트를 특정할 수 있으며 위와 같은 전달 역할은 네트워크의 라우터가 수행한다.

 

하지만, 같은 호스트 내에서도 여러 어플리케이션이 동작할 수 있다. 따라서 각 어플리케이션은 독립적인 포트번호를 할당받아 수신된 메세지가 자신의 어플리케이션에 도달할 수 있도록 한다. 위와 같은 포트 번호를 구분하여 메세지를 전달하는 역할은 운영체제가 담당한다.

 

소켓 생성 (winsock)

 

- 준비

 

헤더 파일 <winsock2.h>과 ws2_32.lib 라이브러리 링크

 

- 초기화

int WSAStartup(WORD wVersionRequested, LPWSDATA lpWSAData);
  • wVersionRequested: 윈속의 버전 정보
  • lpWSAData: 해당 라이브러리 정보를 담기 위해 전달하는 WSADATA라는 구조체 변수의 주소 값

- 해제

int WSACleanup();
  • 성공 시 0, 실패 시 SOCKET_ERROR 반환

상위 8비트에 부 버전, 하위 8비트에 주 버전을 표시

ex) 1.2 -> 0x0201, 2.1 -> 0x0102

매크로 MAKEWORD(주 버전, 부버전) -> MAKEWORD(1,2) == 1.2 버전

int main(int argc, char* argv[])
{

	// 윈속 라이브러리 초기화
	WSADATA wsaData;
	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
		ErrorHandling("WSAStartup() error");
	
	// 1. 소켓 생성
	// 2. 소켓 주소 할당
	// 3. 소켓 연결 수락 가능 상태
	// 4. 소켓 연결 수락
    
	// 윈속 라이브러리 해제
	WSACleanup();
	return 0;
}

 

1. 소켓 생성 

SOCKET socket(int af, int type, int protocol);
  • 성공 시 소켓 핸들, 실패 시 INVALID_SOCKET 반환

- 서버 소켓 생성

SOCKET hServSock = socket(PF_INET, SOCK_STREAM, 0);
if (hServSock == INVALID_SOCKET)
	ErrorHandling("socket() error");

 

2. 소켓에 IP 주소와 PORT 번호를 할당

int bind(SOCKET s, const struct sockaddr* name, int namelen);
  • 성공 시 0, 실패 시 SOCKET_ERROR 반환

- 소켓에 주소와 포트 번호를 할당

memset(&servAddr, 0, sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
servAddr.sin_port = htons(atoi(argv[1]));

if (bind(hServSock, (SOCKADDR*)&servAddr, sizeof(servAddr)) == SOCKET_ERROR)
	ErrorHandling("bind() error");

 

3. 연결 요청 가능 상태 설정

int listen(SOCKET s, int backlog);
  • 성공 시 0, 실패 시 SOCKET_ERROR 반환

- 연결 요청 상태

if (listen(hServSock, 5) == SOCKET_ERROR)
	ErrorHandling("listen() error");

 

4. 서버에서 클라이언트의 연결 요청을 수락

SOCKET accept(SOCKET s, stuct sockaddr* addr, int* addrlen);
  • 성공 시 소켓 핸들, 실패 시 INVALID_SOCEKT 반환

- 클라이언트 요청 수락

SOCKADDR_IN clntAddr;
int szClntAddr = sizeof(clntAddr);
SOCKET hClntSock = accept(hServSock, (SOCKADDR*)&clntAddr, &szClntAddr);
if (hClntSock == INVALID_SOCKET)
	ErrorHandling("accept() error");

 

소켓을 통한 데이터 송수신

 

- 데이터 전송

int send(SOCKET s, const char* buf, int len, int flags);
  • 성공 시 전송된 바이트 수, 실패 시 SOCKET_ERROR 반환
  • s: 데이터 전송 대상 소켓의 핸들
  • buf: 전송할 데이터를 저장하고 있는 버퍼의 주소 값
  • len: 전송할 바이트 수 전달
  • flags: 데이터 전송 시 적용할 다양한 옵션 정보 전달

 

- 데이터 수신

int recv(SOCKET s, const char* buf, int len, int flags);
  • 성공 시 수신된 바이트 수, 실패 시 SOCKET_ERROR 반환
  • s: 데이터 수신 대상 소켓의 핸들
  • buf:  수신 데이터를 저장할 버퍼의 주소 값
  • len: 수신할 수 있는 최대 바이트 수 전달
  • flags: 데이터 수신 시 적용할 다양한 옵션 정보 전달

 

참고: 윤성우 저 TCP/IP 소켓 프로그래밍