728x90
반응형

TCP 서버는 조금 복잡하며 크게 두 소켓의 흐름으로 구성됩니다. 하나는 대기 소켓의 흐름이며 하나는 통신 소켓의 흐름입니다. 대기 소켓은 클라이언트의 접속을 대기하고 클라이언트 요청을 수락하며 통신 소켓을 생성하는 역할을 담당합니다. 통신 소켓은 대기 소켓에 의해 자동으로 생성되며 클라이언트 소켓과 통신을 담당합니다.

 

TCP 클라이언트는 소켓을 생성하고 서버에 접속을 요청하여 요청이 받아들여지면 자동으로 통신할 수 있는 소켓으로 변화하며 이 소켓을 사용하여 서버와 통신합니다.

 

서버와 클라이언트가 통신하는 흐름

socket() 함수

소켓 프로그램에서 가장 먼저 해야 할 일은 소켓을 생성하는 것 입니다. 소켓은 두 종단 시스템이 통신하기 위한 연결점입니다. 윈도우 소켓은 통신에 사용되는 정보 집합이며 핸들을 사용하여 관리됩니다.

SOCKET socket( int af, int type, int protocal);

af는 주소 체계로 프로토콜 마다 주소 체계를 지정하는 방법이 달라지며 TCP, UDP 프로토콜을 사용한다면 AF_INET을 지정합니다.

 

type은 데이터 통신을 위한 프로토콜 유형으로 SOCKET_STREAM, SOCK_DGRAM, SOCK_RAW를 지정합니다. SOCKET_STREAM은 연결형 프로토콜을 SOCK_DGRAM은 비연결형 프로토콜을 나타냅니다. SOCK_RAW는 애플리케이션 수준에서 프로토콜 헤더를 직접 다를 수 있는 소켓을 생성합니다.

 

protocal은 프로토콜을 결정합니다. TCP 프로토콜은 IPPROTO_TCP, UDP 프로토콜은 IPPROTO_UDP를 지정합니다. 만약 주소 체계와 소켓 타입이 정확히 결정되면 protocal 인수는 0으로 생략할 수 있습니다.

 

반환은 소켓의 핸들이며 실패 시 INVALID_SOCKET을 반환합니다.

 

bind() 함수

bind() 함수는 대기 소켓의 로컬 IP 주소와 로컬 포트 번호를 결정합니다.

int bind( SOCKET s, const struct sockaddr* name, int namelen);

s는 socket() 함수로 생성한 소켓의 핸들이며 이 소켓의 로컬 IP와 포트를 할당합니다.

name은 소켓 구조체를 사용하여 로컬 IP와 포트를 할당합니다.

namelen은 소켓 구조체의 크기입니다.

 

성공시 0을 반환하며 실패 시 SOCKET_ERROR를 반환합니다.

 

listen() 함수

listen() 함수가 호출되면 대기 소켓은 클라이언트의 접속을 받을 수 있는 상태가 되며 클라이언트의 정보를 저장하는 접속 대기 큐(connection queue)가 만들어집니다.

int listen( SOCKET s, int backlog); 

s는 대기 소켓의 핸들입니다.

backlog는 동시에 접속 가능한 클라이언트의 개수로 접속 대기 큐의 크기입니다. 서버가 클라이언트의 접속을 처리하지 않더라도 접속 대기 큐의 크기만큼 클라이언트 접속이 성공합니다. 최대값으로 SOMAXCONN값을 사용합니다.

 

반환값은 성공 시 0이며 실패 시 SOCKET_ERROR입니다.

 

accept() 함수

accept() 함수는 접속 대기 큐를 확인하여 클라이언트가 접속하면 클라이언트와 통신할 수 있는 새로운 통신 소켓을 반환합니다. out parameter 인수로 접속한 클라이언트의 IP 주소와 포트 번호를 반환받을 수 있습니다.

SOCKET accept( SOCKET s, struct sockaddr* addr, int* addrlen);

s는 대기 소켓의 핸들입니다.

addr은 클라이언트의 IP주소와 포트 번호를 받아오는 sockaddr 구조체 out parameter 변수입니다. 소켓의 핸들로 언제든지 클라이언트 정보를 확인할 수 있으므로 바로 클라이언트 정보를 반환받고 싶지 않다면 NULL로 지정하면 됩니다.

addrlen은 addr의 크기를 초기화하여 인수로 하면 실제 초기화된 구조체의 크기를 반환하는 in, out parameter입니다.

 

반환값은 새로운 통신 소켓의 핸들이며 실패 시 INVALID_SOCKET을 반환합니다.

 

send() 함수

send() 함수는 애플리케이션 버퍼의 데이터를 TCP 프로토콜의 송신 버퍼로 복사합니다. 데이터를 송신 버퍼로 모두 복사한 후 실제 복사한 크기를 반환합니다. send() 함수는 블로킹 소켓과 넌블로킹 소켓에 따라 다르게 동작하며 블로킹 소켓일 때 send() 함수는 애플리케이션 버퍼의 데이터를 모두 TCP 프로토콜의 송신 버퍼에 복사할 때 까지 블록 상태가 되며 모두 복사가 완료한 후에 반환합니다. 넌블로킹 소켓일 때 send() 함수는 애플리케이션 버퍼의 데이터가 송신 버퍼의 크기보다 커서 모두 복사할 수 없다면 송신 버퍼의 남은 크기만큼만 복사하고 바로 반환하며 실제 복사한 크기값을 반환합니다.

int send(SOCKET s, const char* buf, int len, int flags);

s는 통신 소켓의 핸들입니다.

buf는 애플리케이션 버퍼의 주소입니다.

len은 송신 버퍼에 쓸 크기입니다.

flags는 보낼 데이터의 소켓 옵션을 지정할 수 있으며 대부분 0입니다. MSG_DONTROUTE나 MSG_OOB 등을 지정할 수 있으며 거의 사용되지 않습니다.

 

반환값은 실제 보낸 크기이며 실패 시 SOCKET_ERROR를 반환합니다.

 

recv() 함수

recv() 함수는 TCP 프로토콜 수신 버퍼의 데이터를 애플리케이션 버퍼로 복사합니다. recv() 함수도 블로킹 소켓과 넌블로킹 소켓에 따라 다르게 동작하며 블로킹 소켓이라면 recv() 함수는 수신 버퍼에 데이터가 도착할 때까지 블로킹됩니다. 넌블로킹 소켓이라면 recv() 함수는 수신 버퍼에 데이터가 없으면 에러값으로 반환되며 수신 버퍼에 데이터가 있으면 애플리케이션 버퍼에 데이터를 복사하고 반환됩니다.

int recv( SOCKET s, char* buf, int len, int flags);

s는 통신 소켓의 핸들입니다.

buf는 수신 버퍼의 데이터를 복사할 애플리케이션 버퍼의 주소입니다.

len은 수신 버퍼에서 애플리케이션 버퍼로 복사할 최대 데이터 크기입니다. 반환값은 len보다 작거나 같습니다.

flags는 받는 데이터의 옵션을 설정할 수 있으며 대부분 0입니다. MSG_PEEK나 MSG_OOB를 사용할 수 있으며 거의 사용되지 않습니다.

 

반환값은 성공시 두 가지이며 데이터가 수신 버퍼에 도착하여 데이터를 애플리케이션 버퍼로 복사했다면 복사한 데이터의 크기값을 반환합니다. 또 연결된 소켓 closesocket() 함수를 호출하여 접속을 종료하면 TCP 프로토콜은 접속 종료를 위한 절차에 들어가며 이때 recv() 함수는 0을 반환합니다.

 

connect() 함수

connetct() 함수는 클라이언트가 서버에 접속을 요청하며 TCP 프로토콜 수준의 접속을 위한 절차에 들어갑니다. 이때 서버는 listen() 함수가 호출되어 있어야 접속이 성공합니다. 접속이 성공하면 connect() 함수 호출 이후의 소켓은 통신이 가능한 통신 소켓이 됩니다.

int connect(SOCKET s, const struct sockaddr* name, int namelen);

s는 서버와 통신할 통신 소켓의 핸들입니다.

name은 서버의 원격 IP와 원격 포트 번호를 설정한 sockaddr 구조체 주소입니다.

namelen은 name(소켓 주소 구조체) 의 크기를 지정합니다.

 

반환값은 성공 시 0이며 실패 시 SOCKET_ERROR를 반환합니다.

728x90
반응형
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기