네트워크 프로그래밍04 TCP기반 서버/ 클라이언트1

zh025700·2022년 4월 5일
0

네트워크 프로그래밍


4. TCP 기반 서버/ 클라이언트 1

tcp는 연결 지향 방식이다.

TCP 서버에서 기본적 함수 호출 순서

  1. 소켓생성 socket
  2. 소켓 주소할당 bind
  3. 연결요청 대기상태 listen
  4. 연결 허용 accept
  5. 데이터 송수신(read,write)
  6. 연결 종료 close

socket 함수를 이용해 소켓을 생성한다
생성된 소켓에 bind 함수를 통해 주소를 할당한다

bind 함수

int bind(int 소켓 디스크립터, struct sockaddr* 구조체 변수의 주소값, socklen_t 구조체 변수의 길이정보);
  • 초기화를한 구조체를 소켓에 할당해 소켓에 주소를 할당한다.
  • 이후 연결 요청 대기상태(listen을 사용)로 진입해야한다.

BUT 만약 서버가 bind를 하지 않으면, 커널은 소켓에 대한 일시적인 포트를 선택한다

클라이언트는 bind를 하지 않는다.
그래서 클라이언트가 호스트에서 사용 가능한 모든 인터페이스에서 응답할 수 있다.
어플리케이션에서 특별히 포트번호를 요청하지 않는다면 클라이언트는 임시의 포트번호를 커널이 선택하게 나둔다.
서버는 시작할때 잘 알려진 포트번호를 bind한다.
만약 서버가 bind를 하지 않으면, 커널은 소켓에 대한 일시적인 포트를 선택한다.
근데 이럴일은 별로 없음..

listen 함수

int listen(int 소켓 파일디스크립터, int 연결 요청 받을 개수);
  • listen 함수를 호출해 주소가 할당된 소켓에 연결요청을 대기할 수 있게 한다.
  • 즉 passive 소켓으로 만든다!
  • 연결 요청 대기란 클라이언트가 연결 요청을 했을 떄 연결이 수락될 때까지 연결 요청을 대기시킬 수 있는 상태이다.
  • 오직 TCP서버에서만 사용된다
  • 보통 소켓을 만들고 bind를 한 후 accept 함수를 호출하기 전에 사용한다!

이제 연결 요청이 들어오면 수락을 해야한다.

  • backlog: 커널은 2개의 큐를 가진다
    • incomplete connection queue: 각 entry마다 TCP 3-way handshake를 기다리는 클라이언트의 SYN(synchronize sequence numbers)을 가지고 있다
      • 3-way가 완료되면 completed 큐로 넘어감!
    • completed connection queue: TCP 3-way가 완료된 클라이언트를 지니고 있다. > queue의 크기는 backlog를 넘길 수 없다!!

      3-way handshake가 끝나면 accept를 받을 수 있다.

연결 요청을 받을 소켓을 생성한다.
즉 서버에서 생성한 소켓을 연결 요청을 받을 소켓으로 만든다
그럼 클라이언트의 active 소켓이  이 소켓에 연결을 요청해
이 서버의 연결요청 소켓이 연결을 받아들인다.
그럼 연결이 되면 클라이언트의 소켓을 이용해 통신을 한다

Accept 함수

수락을 한다는 것은 클라이언트와 서버가 데이터를 주고받을 수 있는 상태임을 뜻한다.

즉 소켓이 필요하다.

 accept 함수는 이 클라이언트와 통신을 할 수 있게해주는 소켓을 생성해준다.
complete connection 큐에서 대기 중인 클라이언트의 연결 요청을 수락하는 기능을 한다
호출 성공시 통신에 사용할 소켓을 생성하고 소켓의 파일디스크립터를 반환한다.

completed connection 큐의 소켓의 연결요청을 수락한다
=>수락해서 소켓 생성

만약 completed queue가 비엇으면 process는 sleep status로 간다.

int accept(int 서버소켓의 파일 디스크립터,struct sockaddr* addr, socklen_t* addrlen);

-addr : 클라이언트의 소켓 주소 정보를 담을 구조체 주소
-addrlen : addr에 전달된 주소의 변수 크기, 단 크기를 sizeof()로 정한 후 주소로 넘긴다.

accept 함수는 서버와 클라이언트의 통신을 수락해주고 이에 사용할 소켓을 넘겨준다.
accept의 리턴값 = 클라이언트 소켓 디스크립터!! 실패하면 -1

accept을 하면 새로운 소켓이랑 연결이 된다.
기존 연결된 소켓도 여전히 연결이 유지된다.

이제 통신을 진행하면 된다.

그렇다면 클라이언트는 어떻게 진행이 될까?

TCP 클라이언트의 기본적 함수 호출 순서

  1. 소켓생성 socket
  2. 연결 요청 connect
  3. 데이터 송수신 read/write
  4. 연결 종료 close

서버와 차이점은 2. 연결 요청 bind 부분이다.
서버가 listen을 통해 연결 요청을 받을 수 있는 상태 이후, 클라이언트는 연결을 요청할 수 있다.

connect 함수

int connect(int sockfd, const struct sockaddr* servaddr,socklen_t addrlen);

함수 실행에 성공하면 리턴 0 실패하면 -1

  • sock: 클라이언트 소켓의 파일 디스크립터
  • servaddr: 연결 요청할 서버의 주소 정보를 담은 변수의 주소값, 함수가 실행되면 여기엔 클라이언트의 주소 정보가 채워진다.
  • addrlen: servaddr에 전달된 주소의 변수 크기를 바이트 단위로 전달, 이것도 크기를 정한 후 변수의 주소값을 전달, 함수가 실행되면 클라이언트의 주소정보 길이가 저장됨.

connect는 생성된 소켓의 파일 디스크립터를 반환!

- connect함수가 실행되고 두가지 상황이 실행되어야 함수가 반환됨.

  1. 서버에서 연결요청 접수(0)
  2. 오류로 인한 연결 요청 중단(-1)

**연결 요청은 서버의 accept를 의미하는게 아니다. 서버에 연결 대기를 걸어 놓는 것임**
=> connect가 반환이 되어도 바로 연결이 되지 않을 수도 있다.

클라이언트 소켓의 주소 정보 저장?

서버는 직접 소켓에 IP와 PORT를 할당하였다.
반면 클라이언트는 주소할당 과정이 없었다.
통신을 할때는 주소정보가 필수적이다, 그렇다면 언제 할당이 되지??

  • connect가 호출이 될때 연결할 서버의 주소 정보도 함께 전달
  • 커널에서 IP는 컴퓨터에 할당된 것으로, PORT는 임의로

=> bind를 통해 소켓에 직접 당하지 않아도 자동으로 할당이 된다.

close() 함수

  • 소켓을 close할때 사용, 통신 연결을 종료할 떄 사용한다

int close(int sockfd);

성공하면 0을 리턴하고 실패하면 -1을 리턴한다

close의 뜻

  1. 연결을 끊는다.
  2. 소켓이 사용했던 포트 번호를 다시 사용할 수 있게 free한다

TCP 통신 정리

  1. 서버에서 소켓을 생성한다.
  2. bind를 통해 소켓에 주소정보를 입력한다.
  3. listen을 통해 연결 대기상태로 진입
  4. 클라이언트에서 이 전에 소켓을 생성 후 3. 이후 connect를 통해 연결을 요청한다.
  5. 서버가 accept을 해 연결 된다.
  6. 데이터 송수신
  7. 연결 종료

위의 과정으로 진행이 된다.

서버는 클라이언트가 connect을 하기 전 accept를 실행할 수 있다.
단 이때는 connect 요청이 올 때까지 accept를 호출한 위치에서 서버가 blocking 된다.

왜?? accept을 했는데 completed connection 큐에 아무것도 없으면 프로세스가 sleep 되니깐 서버가 block댐

그렇다면 여러 클라이언트에 대한 요청은??

  1. 반복문을 사용해 accept 함수를 반복호출 하면 된다.
  2. 클라이언트에서의 소켓을 close 해줌으로써 상대 소켓에 EOF를 전송한다.
  3. 이를 통해 연결을 끊는다.

그렇다면 보내는 문자열이 긴 경우는??

  • read와 write의 호출을 통해 데이터를 전송하는 경우 호출 시기에 따라 전송이 된다고 가정할 수 있다.

  • TCP는 버퍼를 사용해 한번에 전송을 한다고 했다. 그렇다면 하나의 데이터에 write를 여러번 호출한 데이터들, 즉 섞인 데이터가 있다면 어떻게 될까?

연결이 끊어졌을 때 서버에서 끊김을 감지하는 법은?

  • read함수는 실행이 되지 않는다면 -1을 반환한다.
    이는 서버가 없어서 연결이 되지 않는 것을 의미한다.

서버와 클라이언트가 송수신을 하다가 한 쪽의 소켓이 죽으면 나머지 쪽에서의 read 함수의 반환 값은 0이 된다,

그래서 서버에서 연결이 끊어짐을 감지하려면
read함수의 반환 값이 0임을 처리하면 된다.

profile
정리

5개의 댓글

comment-user-thumbnail
2022년 4월 9일

서버라..
제가 프라하에서
파스타 서빙을 하던 그 때가 기억나네요..

1개의 답글