网络编程服务器基础模型二(select模型)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <netinet/in.h> #include <sys/socket.h> #include <sys/types.h> #include <errno.h> #include <sys/select.h> #define BUF_LEN 1024 #define SERV_PORT 5358 #define FD_SIZE 513 ssize_t writen(int fd, const void *vptr, size_t n) { size_t nleft; ssize_t nwriten; const char *ptr; ptr = vptr; nleft = n; while(nleft>0) { if((nwriten = write(fd, ptr, nleft)) <= 0) { if(nwriten < 0 && errno == EINTR) { nwriten = 0; /*interrupt by signal*/ } else { return -1; } } nleft -= nwriten; ptr += nwriten; } return n; } int main(int argc, char **argv) { int listenfd, connfd, sockfd, maxfd, maxi, i; int nready, client[FD_SIZE]; ssize_t n; fd_set rset, allset; char buf[BUF_LEN] = {0}; socklen_t clilen; struct sockaddr_in servaddr, cliaddr; if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { printf("socket error: %s\n", strerror(errno)); return 0; } bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(SERV_PORT); if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) { printf("bind error: %s\n", strerror(errno)); return 0; } if(listen(listenfd, 256)) { printf("listen error: %s\n", strerror(errno)); } maxfd = listenfd; maxi = -1; for(i=0;i<FD_SIZE;i++) { client[i] = -1; } FD_ZERO(&allset); FD_SET(listenfd, &allset); while(1) { rset = allset; nready = select(maxfd+1, &rset, NULL, NULL, NULL); if(FD_ISSET(listenfd, &rset)) { clilen = sizeof(cliaddr); if((connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen)) == -1) { printf("accept error: %s\n", strerror(errno)); } for(i=0;i<FD_SIZE;i++) { if(client[i] < 0) { client[i] = connfd; break; } } if(i == FD_SIZE) { printf("too many fd!\n"); exit(0); } FD_SET(connfd, &allset); if (connfd > maxfd) maxfd = connfd; if (i > maxi) maxi = i; if (--nready <= 0) continue; } for(i=0;i<=maxi;i++) { if((sockfd = client[i]) < 0) continue; if(FD_ISSET(sockfd, &rset)) { if((n = read(sockfd, buf, BUF_LEN)) ==0) { close(sockfd); FD_CLR(sockfd, &allset); client[i] = -1; }else { fputs(buf, stdout); writen(sockfd, buf, n); bzero(buf, BUF_LEN); } if(--nready <= 0) break; } } } return 0; }
优点:
采用了select模型,避免了为每一个连接开启一个线程,因为线程的开销是很大的。
不足:
unp上说程序存在一个巨大的问题,当某个客户端发送一个字节的数据后,客户挂起,也就是说客户既不发送换行符,也不发送EOF,也不断开连接,这是就会导致read函数一直阻塞(这点没搞明白,试验了一下,发现并没有这个问题)。
注:发现问题了,这里的问题还有可能出现在writen函数上,当内核buffer满的时候,writen函数完全有可能阻塞起来,导致整个服务器无法提供服务。
解决这一问题有三种办法:
采用非阻塞的I/O模型 让每个客户由单个线程处理 对I/O操作设置超时