[네트워크] 서버의 시스템 콜( bind, listen, accept )

minji·2021년 8월 13일
1

지난 글에서 serv_addr 구조체에 값을 채워넣음으로써 소켓의 ip주소와 port번호를 지정해주었다.

다음으로 이루어지는 과정은 bind -> listen -> accept로,
이 세가지 함수에 대해 순서대로 알아보려고 한다.

1. 소켓에 주소를 할당하는 bind 함수

//함수 원형
int bind(int sockfd, struct sockaddr *addr, socklen_t addrlen);
//실제 사용
if(bind(serv_sock,(struct sockaddr*) &serv_addr,sizeof(serv_addr))==-1){,,,}  
        

bind() 함수에 3가지 인자를 전달함으로써 비로소 소켓에 주소를 할당할 수 있다.

즉, 우리가 앞서 socket() 함수로 받아온 디스크립터 sockfd가 존재하는데, 이 디스크립터 파일에 해당하는 소켓에 serv_addr 주소를 할당하겠다는 의미이다.

- sockfd : socket() 함수를 통해 배정받은 디스크립터 번호 serv_sock
- *addr : IP주소와 PORT번호를 지정한 serv_addr 구조체 (serv_addr은sockaddr_in 이므로 sockaddr 구조체로 변환해준다.)
- addrlen : 주소정보를 담은 변수의 길이

bind() 함수는 성공시 0, 실패시 -1을 반환한다.

2. 연결요청을 대기하는 listen 함수

bind를 통해 하나의 소켓에 ip주소와 port번호까지 할당했으니, 이제 클라이언트가 해당 소켓에 연결할 수 있도록 그 요청을 대기하는 상태로 만들어주어야 한다.

이 과정을 담당하는 함수가 listen함수이다.
즉, listen함수가 호출된 후 부터 클라이언트에서 connect(연결을 요청하는 함수)를 호출할 수 있게된다.

//함수 원형
int listen(int sock, int backlog);
//실제 사용
if(listen(serv_sock,5)==-1){,,,}

- sock : 소켓 디스크립터 번호
- backlog : 연결요청을 대기하는 큐의 크기

즉, 지정한 디스크립터의 소켓이 리스닝소켓이 되며, backlog 만큼의 큐 공간을 갖는다.

  • 연결요청을 대기하는 큐(Queue)란?
    우선 '연결요청을 대기'한다는 것은 클라이언트가 연결을 요청했을 때, 그 요청을 대기시킬 수 있음을 의미한다. 그리고 큐는 쉽게말해 대기실이라고 이해할 수 있는데, 시스템에서 순서대로(tcp인경우) 클라이언트를 연결시킬 수 있도록 큐에 모아놓는 것이다.

위의 예시에서는 큐의 크기를 5로 설정했으며, 이는 5개까지의 클라이언트 연결 요청을 대기시킬 수 있음을 의미한다.

3. 연결 요청을 수락하는 accept 함수

이제 마지막으로, 대기중인 클라이언트의 요청을 차례로 수락함으로써 데이터를 주고받을 수 있게 된다.
accpet 함수가 바로 연결요청을 수락하는 함수이다.

//함수 원형
int accept(int sock, struct sockaddr*addr, socklen_t *addrlen);
//실제 사용
clnt_addr_size=sizeof(clnt_addr);
clnt_sock=accept(serv_sock,(struct sockaddr*)&clnt_addr,&clnt_addr_size);

여기서 주의해야 할 점은, accept의 반환값은 성공/실패에 대한 정수값이 아닌, 새로운 디스크립터 번호라는 점이다.

즉, 우리가 앞서 이용하던 서버소켓(리스닝소켓)은 연결요청을 대기시키는 과정까지를 담당하며, accept() 함수를 통해 새로 할당받은 소켓을 이용해 데이터 송수신을 할 수 있는 것이다.

- sock : 서버소켓(리스닝소켓)의 디스크립터 번호
- addr : 대기 큐를 참조해 얻은 클라이언트의 주소정보
- addrlen : addr변수의 크기

cf) clnt_sock에 새로운 소켓 디스크립터를 반환할 때에는 대기 큐에서 첫번째로 대기중인 연결요청을 참조한다. 이 때, 대기 큐가 비어있는 상황이라면 새로운 요청이 올 때까지 accept값은 반환되지 않고 대기(blocking)한다.

4. 데이터 송수신 및 연결해제

앞선 순서대로 bind() , listen() , accept() 세가지 함수를 순차적으로 호출함으로써 서버측에서의 데이터 송수신 준비를 마칠 수 있다.

최종적으로는 write() 함수를 통해 실제 데이터를 출력할 수 있으며(데이터 수신도 함께 한다면 read() 함수 이용),
데이터 송수신을 완료한 후, 이용한 소켓을 완전히 소멸시킬 때는 close() 시스템콜을 호출해준다. 그러면 커널이 해당 소켓의 자원을 모두 시스템에서 제거한다.

profile
SW Engineer

0개의 댓글