利用select和poll函数来代替fork产生子进程的方法,可以实线并发连接并且可以减少fork子进程时占用的系统资源,select函数需要一个client[]数组的数据结构来维持多用户的fd,每次有新用户连接和关闭时需要对该数据结构进行更新,而poll函数是维持一个pollfd结构,比select函数方便。
select函数版:
#include "unp.h"
int main(int argc, char const *argv[])
{
int i, maxi, maxfd, listenfd, connfd, sockfd;
int nready, client[FD_SETSIZE];
ssize_t n;
fd_set rset, allset;
char buf[MAXLINE], buff[MAXLINE];
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
Listen(listenfd, LISTENQ);
printf("---------------Waiting for connect------------\n");
maxfd = listenfd;
maxi = -1;
for (i = 0; i < FD_SETSIZE; ++i)
client[i] = -1;
FD_ZERO(&allset);
FD_SET(listenfd, &allset);
for( ; ; )
{
rset = allset;
nready = Select(maxfd + 1, &rset, NULL, NULL, NULL);
if(FD_ISSET(listenfd, &rset)) /* new client connection */
{
clilen = sizeof(cliaddr);
connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);
for(i = 0; i < FD_SETSIZE; i++)
if(client[i] < 0)
{
client[i] = connfd; /* save descriptor */
break;
}
printf("Connect from: %s port: %d\n",
Inet_ntop(AF_INET, &cliaddr.sin_addr, buff, sizeof(buff)),
ntohs(cliaddr.sin_port));
if(i == FD_SETSIZE)
err_quit("too many clients");
FD_SET(connfd, &allset); /* add new descriptor to set */
if(connfd > maxfd)
maxfd = connfd; /* for select */
if(i > maxi)
maxi = i; /* max index in client[] array */
if(--nready <= 0)
continue; /* no more readable descriptors */
}
for (i = 0; i <= maxi; ++i) /* check all clients for data */
{
if((sockfd = client[i]) < 0)
continue;
if(FD_ISSET(sockfd, &rset))
{
if((n = Read(sockfd, buf, MAXLINE)) == 0)
{ /* connection closed by client (FIN)*/
Close(sockfd);
FD_CLR(sockfd, &allset);
client[i] = -1; /* update client struct*/
}
else
Writen(sockfd, buf, n);
if(--nready <= 0)
break; /* no more readable descriptors */
}
}
}
}
poll函数版:
#include "unp.h"
#define OPEN_MAX 256
int main(int argc, char const *argv[])
{
int i, maxi, listenfd, connfd, sockfd;
int nready;
ssize_t n;
char buf[MAXLINE], buff[MAXLINE];
socklen_t clilen;
struct pollfd client[OPEN_MAX];
struct sockaddr_in cliaddr, servaddr;
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
Listen(listenfd, LISTENQ);
printf("---------------Waiting for connect------------\n");
client[0].fd = listenfd;
client[0].events = POLLRDNORM;
for(i = 1; i < OPEN_MAX; i++)
client[i].fd = -1;
maxi = 0;
for( ; ; )
{
nready = Poll(client, maxi + 1, INFTIM);
if(client[0].revents & POLLRDNORM) /* new client connection */
{
clilen = sizeof(cliaddr);
connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);
for(i = 1; i < OPEN_MAX; i++)
if(client[i].fd < 0)
{
client[i].fd = connfd; /* save descriptor */
break;
}
printf("Connect from: %s port: %d\n",
Inet_ntop(AF_INET, &cliaddr.sin_addr, buff, sizeof(buff)),
ntohs(cliaddr.sin_port));
if(i == OPEN_MAX)
err_quit("too many clients");
client[i].events = POLLRDNORM;
if(i > maxi)
maxi = i; /* max index in client[] array */
if(--nready <= 0)
continue; /* no more readable descriptors */
}
for (i = 1; i <= maxi; ++i) /* check all clients for data */
{
if((sockfd = client[i].fd) < 0)
continue;
if(client[i].revents & (POLLRDNORM | POLLERR))
{
if((n = Read(sockfd, buf, MAXLINE)) < 0)
{
if(errno == ECONNRESET)
{ /* connection reset by client */
Close(sockfd);
client[i].fd = -1; /* update client struct*/
}
else
err_sys("read error");
}
else if(n == 0) /* connection closed bt client */
{
Close(sockfd);
client[i].fd = -1;
}
else
Writen(sockfd, buf, n);
if(--nready <= 0)
break; /* no more readable descriptors */
}
}
}
}