지난 글에서 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 만큼의 큐 공간을 갖는다.
위의 예시에서는 큐의 크기를 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()
시스템콜을 호출해준다. 그러면 커널이 해당 소켓의 자원을 모두 시스템에서 제거한다.