웹 서버 구현기 2-2 TCP/IP: 소켓을 통해 연결 및 통신하기

Plato·2023년 6월 10일
0

웹서버 구현기

목록 보기
8/11

서론

이전 글에서는 서버 소켓에 ip주소와 포트를 할당하는 방법에 대해 알아보았다. 이번 글을 통해, 연결하여 통신하는 방법을 알아보자.

본론

전반적인 과정

소켓을 통한 통신은 다음과 같은 과정을 거친다.

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

소켓 생성과 소켓 주소할당은 이전 글을 통해 알아봤으니 연결 요청 대기상태부터 보겠다.

연결

소켓을 생성과 주소할당이 완료되면 클라이언트의 연결 요청에 반응해야한다. 이에 앞서, 연결 요청에 반응할 수 있도록 큐의 크기를 설정해줘야한다. 이는 listen 함수를 통해 할 수 있는데 프로토타입은 아래와 같다.

int listen(int sock, int backlog);

sock는 소켓의 fd 그리고 backlog는 '연결요청 대기 큐'의 크기를 나타낸다. 여러 클라이언트가 서버에게 연결 요청을 할 수 있다. 한정된 자원에 여러 대상이 몰리면 우리는 어떻게 해야할까? 줄을 세우면 된다. 클라이언트가 서버의 ip주소와 포트를 통해 연결 요청을 보내면, '연결요청 대기 큐'에 어느 클라이언트가 연결 요청을 보냈는지 알잘딱깔센으로다가 담긴다. 서버가 연결 요청에 응답할 준비가 됐을 때, 서버가 accept 함수를 호출하면, queue의 제일 앞에 담긴 즉 제일 오래된 연결 요청을 처리한다.

여기에서 주의해야할 부분은, accept는 blocking 함수라는 점이다. queue에 담긴 연결 요청이 없을 때, accept 함수를 호출하면 새로운 연결 요청이 올 때 까지 block된다. 또한, accept는 새로운 소켓을 생성한 뒤 이 새로운 소켓의 fd를 반환한다. TCP 소켓은 크게 1. 클라이언트에서 보내는 연결 요청을 받는 소켓과(이 시리즈에서는 서버 소켓이라 칭할 것이다) 2. 연결이 생성된 뒤 통신을 위해 사용하는 소켓으로 나뉘게 된다. 서버가 socket() 호출을 통해 생성한 소켓은 서버 소켓이고 accept 함수가 반환한 소켓이 실제 통신에 쓰일 소켓인 것이다.

데이터 경계

TCP는 데이터의 경계가 존재하지 않는다. 데이터의 경계가 존재하지 않는다는 것은, 데이터가 보내졌을 때 어디부터 어디까지를 하나의 단위로 취급할지 프로토콜이 강요하지 않는다는 것이다. 예시를 통해 자세히 살펴보자. 클라이언트가 서버에 전화번호부를 보내고 서버가 이를 읽는 시나리오를 상상해보자. 전화번호부에 3 개의 전화번호가 있다고 가정해보자. 클라이언트와 서버가 서로에게 보낼 데이터에는 논리적인 단위가 존재한다. 이 경우에는 하나의 전화번호가 하나의 논리 단위를 형성하는 것이 직관적일 것이다. 이러한 논리 단위를 데이터 블록이라 칭해보자. 클라이언트가 하나의 전화번호를 하나의 데이터 블록으로 본다고 할지라도, 데이터의 경계가 존재하지 않기 때문에, 서버는 이를 알아낼 방법이 없다. 그렇기에, 서버와 클라이언트간에 프로토콜을 추가적으로 정의하여, 데이터의 경계를 명확히 할 필요가 있다. 이러한 프로토콜을 애플리케이션 프로토콜이라 부른다.

0개의 댓글