fork()
를 통해 클라이언트에 대응 프로세스를 실행하여 여러 클라이언트들의 접속을 다루는 방식.단일 프로세스로 연결을 진행하는 방식이 IO 멀티플렉싱 서버
select
함수의 이해와 서버의 구현select
함수를 이용하면 여러 개의 파일 디스크립터들을 관찰할 수 있다.
를 볼 수 있고, 상황이 발생한 경우를 가리켜 "이벤트" 가 발생했다고 한다.
위 세 항목(수신, 전송, 예외)에 따라서 구분하여 발생하는 이벤트에 따라 우리는 클라이언트에게 어떤 반응을 해줄것인지를 서버에서 세팅하면 되는 것.
이러한 select()
는 다음 과정을 거친다.
파일 디스크립터의 설정 | 검사의 범위 지정 | 타임 아웃 설정
-> select 함수 호출 -> 호출 결과 확인.
관찰 대상인 파일 디스크립터를 설정해주는 부분이 필요하다.
fd_set
자료형을 이용하여 해당 파일 디스크립터가 관찰대상임을 설정해주면 된다.
다음 함수들을 이용하여 fd_set형 변수의 비트를 다룰 수 있다.
#include <sys/select.h>
#include <sys/time.h>
int select(int maxfd, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval * timeout);
select()
를 통해 함수 사용법을 바탕으로 멀티플렉싱 서버를 구현.
앞서 에코 서버를 구성했던 내용에서, listen()
이후 select()
를 사용하여 어떤 fd가 입력을 받는지 이벤트 발생을 확인하고, listen()
중인 fd에 발생 시, 새로 소켓 연결(accept()
)을 진행해주고, 그 외에 클라이언트 파일 디스크립터라면 메시지를 읽어오는 형식으로 구현하면 된다.
// 앞서 서버 소켓의 생성, 세팅 진행
...
if (bind(serv_sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr)) == -1)
error("bind() error");
if (listen(serv_sock, 5) == -1)
error("listen() error");
FD_ZERO(&reads);
FD_SET(serv_sock, &reads);
fd_max=serv_sock;
while(1)
{
cpy_reads=reads; // fd_set copy. 원본 유지를 위해 진행하는 select의 사용법 일부.
timeout.tv_sec=5;
timeout.tv_usec=5000;
if((fd_num=select(fd_max+1, &cpy_reads, 0, 0, &timeout))==-1)
break;
if(fd_num==0)
continue;
for (i=0; i<fd_max+1; i++)
{
if (FD_ISSET(i, &cpy_reads))
{
if(i==serv_sock) // connect request!
{
adr_sz=sizeof(clnt_adr);
clnt_sock= accept(serv_sock, (struct sockaddr*)&clnt_adr, &adr_sz);
FD_SET(clnt_sock, &reads);
}
if (fd_max < clnt_sock)
fd_max=clnt_sock;
printf("Connect client: %d \n", clnt_sock);
}
else
{
std_len = read(i, buf, BUF_SIZE);
if(str_len==0) // close request
{
FD_CLR(i, &reads);
close(i);
printf("closed client: %d\n", i);
}
else
{
write(i, buf, str_len);
}
}
}
}
close(serv_sock);
...
12장에서는 IO 멀티플렉싱 서버를 구성하는 방식을 살펴보았다. 10, 11장을 건너 뛴 이유는 ft_irc 자체에서 IO 멀티플렉싱 서버를 구현할 것을 요구하고 있었기에, 급한 부분 먼저 확인하여 읽어보았다.
서브젝트 내에서도 poll(), epoll(), kqueue() 등 여러 함수들 중 하나를 선택하게 되어있었는데, 책에서는 select를 다루고 있었다. 하지만 전반적인 동작은 다른 함수들과 동일하다. 이벤트가 발생하는 파일 디스크립터를 찾고 그 디스크립터에 발생한 이벤트를 확인하여 처리하는 과정은 내가 프로젝트를 진행하면서 구현했던 과정과 동일하여 특히 재밌는 장이었다.
추후 epoll()에 대한 내용이 책에 다루어지는 것 같았는데, 무슨 차이가 있는지 확인하고 kqueue나 poll 등 멀티플렉싱을 돕는 함수들이 어떤 차이가 있는지 확인해봐야겠다.