select() 대신 poll() 사용
poll() API는 Single Unix Specification 및 UNIX 95/98 표준의 일부입니다. poll() API는 기존 select() API와 같은 API 기능을 수행합니다. 이들 두 API 간의 유일한 차이점은 호출자에게 제공되는 인터페이스입니다.
select() API는 애플리케이션이 비트의 배열을 전달하도록 요구하며, 하나의 비트는 각 설명자 번호를 나타내는 데 사용됩니다. 설명자 번호가 매우 큰 경우에는 할당된 메모리 크기인 30KB를 오버플로우하여 프로세스가 여러 번 반복되도록 할 수 있습니다. 이 오버헤드는 성능에 악영향을 줄 수 있습니다.
poll() API는 애플리케이션이 비트 배열이 아니라 구조 배열을 전달할 수 있도록 합니다. 각 pollfd 구조는 최대 8바이트를 포함할 수 있으므로, 애플리케이션은 설명자 번호가 매우 크더라도 각 설명자에 대해 하나의 구조만 전달하면 됩니다.
소켓 이벤트 흐름: poll()을 사용하는 서버
이 예제는 다음 호출을 사용합니다.
- socket() API가 종료점을 나타내는 소켓 설명자를 리턴합니다. 이 명령문은 또한 이 소켓에 TCP 전송(SOCK_STREAM)을 사용하는 AE_INET6(Internet Protocol version 6) 주소 계열이 사용됨을 식별합니다.
- setsockopt() API는 필수 대기 시간이 만료되기 전에 서버가 다시 시작되면 애플리케이션이 로컬 주소를 다시 사용할 수 있도록 합니다.
- ioctl() API가 소켓을 비차단으로 설정합니다. 수신 연결에 대한 모든 소켓은 청취 소켓으로부터 해당 상태를 상속하므로 이들 또한 비차단입니다.
- 소켓 설명자가 작성된 후에는 bind() API가 소켓의 고유 이름을 가져옵니다.
- listen() API 호출은 서버가 수신 클라이언트 연결을 승인할 수 있도록 합니다.
- poll() API는 프로세스가 이벤트가 발생하기를 기다리고, 이벤트가 발생하면 프로세스를 웨이크업할 수 있도록 합니다. poll() API는 다음 값 중 하나를 리턴합니다.
- 0
- 프로세스가 제한시간을 초과했음을 표시합니다. 이 예제에서는 제한시간이 3분(밀리초 단위)으로 설정되어 있습니다.
- -1
- 프로세스가 실패했음을 표시합니다.
- 1
- 하나의 설명자만 처리할 준비가 되었음을 표시하며, 이 설명자는 청취 소켓인 경우에만 처리됩니다.
- 1++
- 복수 설명자가 처리되기를 기다리고 있음을 표시합니다. poll() API는 청취 소켓의 큐에 있는 모든 설명자에 대한 동시 연결을 허용합니다.
- accept() 및 recv() API는 EWOULDBLOCK이 리턴될 때 완료됩니다.
- send() API가 이 데이터를 클라이언트에 다시 송신합니다.
- close() API가 모든 열린 소켓 설명자를 닫습니다.
참고: 예제를 사용하는 것은 코드 라이센스 및 면책사항 정보의 이용 약관에 동의함을 의미합니다.
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <errno.h>
#define SERVER_PORT 12345
#define TRUE 1
#define FALSE 0
main (int argc, char *argv[])
{
int len, rc, on = 1;
int listen_sd = -1, new_sd = -1;
int desc_ready, end_server = FALSE, compress_array = FALSE;
int close_conn;
char buffer[80];
struct sockaddr_in6 addr;
int timeout;
struct pollfd fds[200];
int nfds = 1, current_size = 0, i, j;
/*************************************************************/
/* Create an AF_INET6 stream socket to receive incoming */
/* connections on */
/*************************************************************/
listen_sd = socket(AF_INET6, SOCK_STREAM, 0);
if (listen_sd < 0)
{
perror("socket() failed");
exit(-1);
}
/*************************************************************/
/* Allow socket descriptor to be reuseable */
/*************************************************************/
rc = setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR,
(char *)&on, sizeof(on));
if (rc < 0)
{
perror("setsockopt() failed");
close(listen_sd);
exit(-1);
}
/*************************************************************/
/* Set socket to be nonblocking. All of the sockets for */
/* the incoming connections will also be nonblocking since */
/* they will inherit that state from the listening socket. */
/*************************************************************/
rc = ioctl(listen_sd, FIONBIO, (char *)&on);
if (rc < 0)
{
perror("ioctl() failed");
close(listen_sd);
exit(-1);
}
/*************************************************************/
/* Bind the socket */
/*************************************************************/
memset(&addr, 0, sizeof(addr));
addr.sin6_family = AF_INET6;
memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any));
addr.sin6_port = htons(SERVER_PORT);
rc = bind(listen_sd,
(struct sockaddr *)&addr, sizeof(addr));
if (rc < 0)
{
perror("bind() failed");
close(listen_sd);
exit(-1);
}
/*************************************************************/
/* Set the listen back log */
/*************************************************************/
rc = listen(listen_sd, 32);
if (rc < 0)
{
perror("listen() failed");
close(listen_sd);
exit(-1);
}
/*************************************************************/
/* Initialize the pollfd structure */
/*************************************************************/
memset(fds, 0 , sizeof(fds));
/*************************************************************/
/* Set up the initial listening socket */
/*************************************************************/
fds[0].fd = listen_sd;
fds[0].events = POLLIN;
/*************************************************************/
/* Initialize the timeout to 3 minutes. If no */
/* activity after 3 minutes this program will end. */
/* timeout value is based on milliseconds. */
/*************************************************************/
timeout = (3 * 60 * 1000);
/*************************************************************/
/* Loop waiting for incoming connects or for incoming data */
/* on any of the connected sockets. */
/*************************************************************/
do
{
/***********************************************************/
/* Call poll() and wait 3 minutes for it to complete. */
/***********************************************************/
printf("Waiting on poll()...\n");
rc = poll(fds, nfds, timeout);
/***********************************************************/
/* Check to see if the poll call failed. */
/***********************************************************/
if (rc < 0)
{
perror(" poll() failed");
break;
}
/***********************************************************/
/* Check to see if the 3 minute time out expired. */
/***********************************************************/
if (rc == 0)
{
printf(" poll() timed out. End program.\n");
break;
}
/***********************************************************/
/* One or more descriptors are readable. Need to */
/* determine which ones they are. */
/***********************************************************/
current_size = nfds;
for (i = 0; i < current_size; i++)
{
/*********************************************************/
/* Loop through to find the descriptors that returned */
/* POLLIN and determine whether it's the listening */
/* or the active connection. */
/*********************************************************/
if(fds[i].revents == 0)
continue;
/*********************************************************/
/* If revents is not POLLIN, it's an unexpected result, */
/* log and end the server. */
/*********************************************************/
if(fds[i].revents != POLLIN)
{
printf(" Error! revents = %d\n", fds[i].revents);
end_server = TRUE;
break;
}
if (fds[i].fd == listen_sd)
{
/*******************************************************/
/* Listening descriptor is readable. */
/*******************************************************/
printf(" Listening socket is readable\n");
/*******************************************************/
/* Accept all incoming connections that are */
/* queued up on the listening socket before we */
/* loop back and call poll again. */
/*******************************************************/
do
{
/*****************************************************/
/* Accept each incoming connection. If */
/* accept fails with EWOULDBLOCK, then we */
/* have accepted all of them. Any other */
/* failure on accept will cause us to end the */
/* server. */
/*****************************************************/
new_sd = accept(listen_sd, NULL, NULL);
if (new_sd < 0)
{
if (errno != EWOULDBLOCK)
{
perror(" accept() failed");
end_server = TRUE;
}
break;
}
/*****************************************************/
/* Add the new incoming connection to the */
/* pollfd structure */
/*****************************************************/
printf(" New incoming connection - %d\n", new_sd);
fds[nfds].fd = new_sd;
fds[nfds].events = POLLIN;
nfds++;
/*****************************************************/
/* Loop back up and accept another incoming */
/* connection */
/*****************************************************/
} while (new_sd != -1);
}
/*********************************************************/
/* This is not the listening socket, therefore an */
/* existing connection must be readable */
/*********************************************************/
else
{
printf(" Descriptor %d is readable\n", fds[i].fd);
close_conn = FALSE;
/*******************************************************/
/* Receive all incoming data on this socket */
/* before we loop back and call poll again. */
/*******************************************************/
do
{
/*****************************************************/
/* Receive data on this connection until the */
/* recv fails with EWOULDBLOCK. If any other */
/* failure occurs, we will close the */
/* connection. */
/*****************************************************/
rc = recv(fds[i].fd, buffer, sizeof(buffer), 0);
if (rc < 0)
{
if (errno != EWOULDBLOCK)
{
perror(" recv() failed");
close_conn = TRUE;
}
break;
}
/*****************************************************/
/* Check to see if the connection has been */
/* closed by the client */
/*****************************************************/
if (rc == 0)
{
printf(" Connection closed\n");
close_conn = TRUE;
break;
}
/*****************************************************/
/* Data was received */
/*****************************************************/
len = rc;
printf(" %d bytes received\n", len);
/*****************************************************/
/* Echo the data back to the client */
/*****************************************************/
rc = send(fds[i].fd, buffer, len, 0);
if (rc < 0)
{
perror(" send() failed");
close_conn = TRUE;
break;
}
} while(TRUE);
/*******************************************************/
/* If the close_conn flag was turned on, we need */
/* to clean up this active connection. This */
/* clean up process includes removing the */
/* descriptor. */
/*******************************************************/
if (close_conn)
{
close(fds[i].fd);
fds[i].fd = -1;
compress_array = TRUE;
}
} /* End of existing connection is readable */
} /* End of loop through pollable descriptors */
/***********************************************************/
/* If the compress_array flag was turned on, we need */
/* to squeeze together the array and decrement the number */
/* of file descriptors. We do not need to move back the */
/* events and revents fields because the events will always*/
/* be POLLIN in this case, and revents is output. */
/***********************************************************/
if (compress_array)
{
compress_array = FALSE;
for (i = 0; i < nfds; i++)
{
if (fds[i].fd == -1)
{
for(j = i; j < nfds; j++)
{
fds[j].fd = fds[j+1].fd;
}
i--;
nfds--;
}
}
}
} while (end_server == FALSE); /* End of serving running. */
/*************************************************************/
/* Clean up all of the sockets that are open */
/*************************************************************/
for (i = 0; i < nfds; i++)
{
if(fds[i].fd >= 0)
close(fds[i].fd);
}
}