TCP-IP 윈도우 소켓 프로그래밍

[TCP/IP] 4장. TCP 서버- 클라이언트

민돌v 2021. 4. 9. 17:52

TCP 서버-클라이언트 구조

1. 동작과 개념

  1. 서버는 먼저 실행하여 클라이언트가 접속하기를 기다린다. (listen)
  2. 클라이언트는 서버에 접속(connect)하여 데이터를 보낸다. (send)
  3. 서버는 클라이언트 접속을 수용하고(accept) 클라이너트가 보낸 데이터를 바당서(recv)처리한다.
  4. 서버는  처리한 데이터를 클라이언트에 보낸다. (send)
  5. 클라이언트는 서버가 보낸 데이터를 받아서(recv) 처리한다.
  6. 데이터를 주고받는 과정을 모두 마치면 접속을 끊는다. (closesocket)

2. 동작 원리

1) 서버는 소켓을 생성한 후 클라이언트가 접속하기를 기다린다.

  • 이때 서버가 사용하는 소켓은 특정 포트번호(ex 9000번)와 결합되어 있어서 이 포트 번호로 접속하는 클라이언트만 수용할 수 있다.

2) 클라이언트가 서버에 접속한다.

  • 이때 TCP프로토콜 수준에서 연결 설정을 위한 패킷교환이 일어난다.

3) TCP 프로토콜 연결 절차가 끝나면, 서버는 접속한 클라이언트와 통신할 수 있는 새로운 소켓을 생성한다.

  • 이 소켓을 이용해 클라이언트와 데이터를 주고받는다.
  • 기존 소켓은 새로운 클라이언트 젒공르 수용하는 용도로 계속 사용된다.

4) 두클라이언트가 접속한 후의 상태를 나타낸 것이다.

  • 서버에는 소켓이 총 3개 존재하며, 이 중 두 소켓을 접속한 클라이언트와 통신하는 용도로 사용한다.

 

ex) 서버 1개와 n개의 TCP 클라언트의 통신

 

서버 1개와 n개의 TCP 클라언트의 통신

 


TCP 서버-클라이언트 분석

1. 동작과 개념

  • 응용 프로그램의 관점에서 소켓은 운영체제의 TCP/IP 구현에서 제공하는 데이터 구조체를 참조하기 위한 메개체다.
  • 응용프로그램이 통신하려면 다음과 같은 요소가 결정되어야 한다.
    1. 프로토콜 : 통신규약, 소켓을 생성할 때 결정
    2. 지역(local) IP주소와 지역 포트 번호 : 서버 또는 클라인트 자신의 주소이다.
    3. 원격(remote) IP주소와 원격 포트 번호 : 서버 또는 클라이언트가 통신하는 상대의 주소이다.

소켓 데이터 구조체


2. 소켓 함수

  • 지역 주소와 원격 주소를 결정하고 TCP상태를 변경하기 위한 일련의 절차

1) TCP 서버 함수   : 일반적으로 TCP서버는 아래와 같은 순서로 소켓 함수를 호출한다.

 

  1. socket() 함수로 소켓을 생성함으로써 사용할 프초토콜을 결정한다.
  2. bind() 함수로 지역 IP주소와 지역 포트번호를 결정한다. (바인딩)
  3. listen() 함수로 TCP를 Listening 상태로 변경한다. (기다림)
  4. accept() 함수로 자신에게 접속한 클라이언트와 통신할 수 있는 새로운 소켓을 생성한다.
    • 이때 원격 IP주소와 원격 포트번호가 결정된다.
  5. send(), recv() 등의 데이터 전송 함수로 클라이언트와 통신을 수행한 후, closesocket() 함수로 소켓을 닫는다.
  6. 새로운 클라이언트 접속이 들어올 때마다 4~5과정을 반복한다.

2) bind() 함수

   :  bind() 함수는 소켓의 지역 IP주소와 지역 포트 번호를 결정한다.

int bind (
    SOCKET s,
    const struct sockaddr *name,
    int namelen
) ;
  1.  S : 생성된 소켓/ 아닉 지역 IP주소와 지역 포트번호가 결정되지 않은 상태
  2. name : 소켓 주소 구조체를 지역 IP주소와 지역 포트번호로 초기화하여 전달
  3. namelen : 소켓 주소 구조체의 길이

 


3)  listen() 함수

   :  listen() 함수는 소켓의 TCP 포트 상태를 LISTENNING으로 바꾼다. 이는 클라이언트 접속을 받아들일 수 있는 상태를 의미한다.

int listen (
    SOCKET s,
    int backlog
) ;
  1.  S : bind() 함수로 지역 IP주소와 지역 포트 번호를 설정한 상태
  2. backlog : 서버가 당장 처리하지 않더라도 접속 가능한 클라이언트의 개수 ( accept() 함수를 호출하지 않음을 의미)

4)  accept() 함수

   :  접속한 클라이언트와 통신할 수 있도록 새로운 소켓을 생성해서 리턴한다.

      또한 접속한 클라이언트의 주소정보도 알려준다.

SOCKET accept (
    SOCKET s,
    struct sockaddr *addr,
    int *addrlen
) ;
  1.  S : bind() 함수로 지역 IP주소와 지역 포트 번호를 설정하고 listen()함수로 TCP 상태를 listenning으로 변경한 상태
  2.  addr : 소켓 주소 구조체를 전달하면 접속한 클라이언트의 주소 정보로 채워진다.
  3. addrlen : 정수형 변수를 addr이 가리키는 소켓 주소 구조체의 크기로 초기화한 후 전달한다.

3. TCP 클라이언트 함수

  • 일반적으로 클라이언트는 아래의 그림과 같은 순서로 소켓 함수를 호출한다.

  1. socket() 함수로 소켓을 생성함으로써 사용할 프로토콜을 결정
  2. connect() 함수로 서버에 접속, 이때 원격 IP주소와 원격 포트 번호는 물론, 지역 IP주소와 지역 포트버호도 결정
    • (연결요청)
  3. send(), recv() 등의 데이터 전송 함수로 서버와 통신한 후, closesocket()함수로 소켓을 닫는다.

1) connect() 함수

   :  connect() 함수는 TCP 프로토콜 수준에서 서버와 논리적 연결을 설정한다.

int connect (
    SOCKET s,
    const struct sockaddr *name,
    int namelen
) ;
  1.  S : 서버와 통신할 목적으로 생성된 소켓
  2. name : 소켓 주소 구조체를 서버 주소로 초기화하여 전달
  3. namelen : 소켓 주소 구조체의 길이

2) 소켓 버퍼

  • 송신버퍼와 수신버퍼를 통틀어 소켓 버퍼라 한다.
  • send() 와 recv()함수는 소켓 버퍼에 접근할 수 있게 만든 함수이다.


3) send() 함수

  • send() 함수는 응용 프로그램 데이터를 운영체제의 송신버퍼에 복사함으로써 데이터를 전송한다.
  • send() 함수는 복사에 성공하면 곧바로 리턴한다.
  • 따라서 send()함수가 리턴했다고 실제 데이터가 전송된것이 아니며, 일정시간이 지나야만 하부 프로토콜을 통해 전송된다.
int send (
    SOCKET s,
    const char *buf,
    int len,
    int flags
);
  1.  S : 통신할 대상과 연결된 소켓
  2. buf : 보낼 데이터를 담고 있는 응용 프로그램 버퍼의 주소
  3. len : 보낼 데이터 크기
  4. flags : send()함수의 동작을 바꾸는 옵션으로, 대부분 0사용

Send() 함수는 첫 번째 인자로 전달하는 소켓의 특성에 따라 다음과 같이 두 종류의 성공적인 리턴을 할 수 있다.

  1. 블로킹(blocking) 소켓
    1. 지금까지 생성한 소켓은 모두 블로킹 소켓이다
  2. 넌블로킹 소켓
    1. ioctlsocket() 함수를 이용하면 블로킹 소켓을 넌블로킹 소켓으로 바꿀 수 있다.
    2. 송신 버퍼의 여유 공간만큼 데이터를 복사한 후 실제 복사한 바이트 수를 리턴한다.

4) recv() 함수

  • recv() 함수는 운영체제의 수신 버퍼에 도착한 데이터를 응용 프로그램 버퍼에 복사한다.
int recv (
    SOCKET s,
    char *buf,
    int len,
    int flags
);
  1.  S : 통신할 대상과 연결된 소켓
  2. buf : 받은 데이터를 담고 있는 응용 프로그램 버퍼의 주소
  3. len : 운영체제의 수신 버퍼로부터 복사할 최대 데이터 크기이다. 이 값은 buf가 가리키는 으용 프로그램 버퍼보다 크지 않아야 한다.
  4. flags : recv()함수의 동작을 바꾸는 옵션, 대부분 0

recv() 함수는  두 종류의 성공적인 리턴을 할 수 있다.

  1. 수신 버퍼에 데이터가 도달한경우
    • recv()함수의 세 번째 인자인 len보다 크지 않은 범위에서 가능하면 많은 데이터를 응용 프로그램 버퍼에 복사한 후 실제 복사한 바이트 수를 리턴한다.
    • 이 경우 recv()함수의 리턴 값은 최소1, 최대 len이다.
  2. 접속이 정상 종료한 경우
    • 상대편 으용 프로그램이 closesocket() 함수를 호출해 접속을 종료하면,
    • TCP 프로토콜 수준에서 접속 종료를 위한 패킷 교환 절차가 일어난다.
    • 이 경우 0을 리턴한다. (정상 종료)