本文共 16964 字,大约阅读时间需要 56 分钟。
在linux中的I/O都是用文件描述符表示的,文件描述符就是一个int,比如0是标准输入,1是标准输出,2是标准错误输出。同理,socket也是返回一个int文件描述符。
select系统调用是用来让我们的程序监视多个文件描述符(file descriptor)的状态变化的。程序会停在select这里等待,直到被监视的文件描述符有某一个或多个发生了状态改变。
只要有一个描述符状态变为可用,select会停止堵塞,继续往下执行,这时我们就需要用轮询的方法查出是哪个文件描述符可用。
理解select模型的关键在于理解fd_set,为说明方便,取fd_set长度为1字节,fd_set中的每一bit可以对应一个文件描述符fd。则1字节长的fd_set最大可以对应8个fd。
(1)执行fd_set set; FD_ZERO(&set);则set用位表示是0000,0000。 (2)若fd=5,执行FD_SET(fd,&set);后set变为00000100(第5位置为1) (3)若再加入fd=2,fd=1,则set变为01100100 (4)执行select(6,&set,0,0,0)阻塞等待(第一个参数是最大的文件描述符+1) (5)若fd=1,fd=2上都发生可读事件,则select返回,此时set变为01100000。注意:没有事件发生的fd=5被清空。注意第五条中的fd=5被清空,说明只要是不活跃的文件描述符都会在select的阻塞被结束后从原有的文件描述集合fd_set中被去除。因此在socket使用select的场景下,是可以在while轮询中使用FD_ISSET宏来不断监听服务器端生成的socket文件描述符是否活跃。
FD_ZERO(fd_set *fdset):清空fdset与所有文件句柄的联系。
FD_SET(int fd, fd_set *fdset):建立文件句柄fd与fdset的联系。
FD_CLR(int fd, fd_set *fdset):清除文件句柄fd与fdset的联系。
FD_ISSET(int fd, fdset *fdset):检查fdset联系的文件句柄fd是否可读写,>0表示可读写。
可以采用select实现 并发服务器:多路复用I/O
下面是服务器select并发版本:
select模型属于网络的I/O复用模型,比纯粹的阻塞I/O模型更具有实用性,因为可以同时等待多个描述字的就绪。
当年学习C/C++的时候,很少碰到底层以数字标示的描述字,只在写文件系统的去尝试各种情况,以获得最佳效率的时候实际尝试使用过一次,一直觉得那种open,write,read的文件操作方式,实在是比fopen一族函数还要低级的方式-_-!平时没有必要使用。但是等到网络编程的时候,才发现。。。。原来这么底层的东西,竟然也有一定的通用性,文件的描述字和网络的描述字竟然是一致的-_-!不管是谁设计的,还是挺佩服的。。。。。。
这里仅仅是为了学习Select模型而写的学习例子,作用是在服务器端输出连接上的客户端的IP(仅以数字形式),然后将客户端的IP以字符串的形式返回,客户端连接服务器,并接受由服务器端返回的IP地址,然后输出转换为字符串形式的IP地址和数字形式的IP地址,为了区别select到正确的不同listen套接字,这里用了不同的端口,并且不同的两个套接字响应时以echo 1,echo 2区别。功能是很简单的,仅仅用于学习,所以其中很多地方本来可以抽出来称为函数的,都贪简单,直接复制了(-_-!这里本来习惯想说Ctrl-C Ctrl-V的。。。但是发现自己实在Ubuntu下用vim复制的,好像和实际情况不符。。。。)
另外。。。。由于用的是《Unix Network Programming》一书,所以编程风格都变得有点像书中了。。。。服务器端全是自己写的,客户端代码由书中的daytime客户端改过来的,并且发现书中客户端代码都不关闭套接字,都交由退出进程的时候由系统关闭,不知道这种风格好不好。由于学习。。。写的是ANSI C程序,用gcc编译-_-!
unp.h是《Unix Network Programming》源代码中的公用头文件,makefile可能也得注意一下,为了图省事,我用了其源代码中的Make.defines,因为这样比自己写简单多了:),makefile就不贴了,没有什么学习意义。
运行效果如下:
客户端运行:
./TestSelectCli 127.0.0.1 1000
Conncet OK
127.0.0.1:16777343 Echo 1.
laptop:~/unpv1/unpv13e/MyTest$ ./TestSelectCli 127.0.0.1 1001
Conncet OK
127.0.0.1:16777343 Echo 2.
laptop:~/unpv1/unpv13e/MyTest$ ./TestSelectCli 192.168.0.138 1000
Conncet OK
192.168.0.138:2315299008 Echo 1.
laptop:~/unpv1/unpv13e/MyTest$ ./TestSelectCli 192.168.0.138 1001
Conncet OK
192.168.0.138:2315299008 Echo 2.
服务器端输出:
2315299008 Echo 1.
16777343 Echo 1.
16777343 Echo 2.
2315299008 Echo 1.
2315299008 Echo 2.
服务器端源代码:
1 #include "unp.h"
2 3 4 void str_echo1(int connfd); 5 void str_echo2(int connfd); 6 7 int main(int argc, char **argv) 8 { 9 struct sockaddr_in cliaddr; 10 pid_t childpid; 11 12 /* Bind 1000 port to listen socket 1 */ 13 int listenfd1 = Socket(AF_INET, SOCK_STREAM, 0); 14 15 struct sockaddr_in servaddr; 16 bzero(&servaddr, sizeof(servaddr)); 17 servaddr.sin_family = AF_INET; 18 servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 19 servaddr.sin_port = htons(1000); 20 21 Bind(listenfd1, (SA *)&servaddr, sizeof(servaddr)); 22 23 Listen(listenfd1, LISTENQ); 24 25 /* Bind 1001 port to listen socket 2*/ 26 int listenfd2 = Socket(AF_INET, SOCK_STREAM, 0); 27 28 struct sockaddr_in servaddr2; 29 bzero(&servaddr2, sizeof(servaddr2)); 30 servaddr2.sin_family = AF_INET; 31 servaddr2.sin_addr.s_addr = htonl(INADDR_ANY); 32 servaddr2.sin_port = htons(1001); 33 34 Bind(listenfd2, (SA *)&servaddr2, sizeof(servaddr2)); 35 36 Listen(listenfd2, LISTENQ); 37 38 /* Initialize fd_set struct */ 39 int maxfdp1 = max(listenfd1, listenfd2) + 1; 40 fd_set rset; 41 FD_ZERO(&rset); 42 43 /* Select from this two listen socket */ 44 for( ; ; ) 45 { 46 FD_SET(listenfd1, &rset); 47 FD_SET(listenfd2, &rset); 48 49 int nready = -1; 50 if( (nready = select(maxfdp1, &rset, NULL, NULL,NULL)) < 0)//只要有一个客户连接,select就停止堵塞 51 { 52 if(EINTR == errno) 53 { 54 continue; 55 } 56 else 57 { 58 err_sys("Select error."); 59 } 60 } 61 62 /* some one listening socket is readable.*/ 63 if(FD_ISSET(listenfd1, &rset)) 64 { 65 socklen_t len = sizeof(cliaddr); 66 int connfd = Accept(listenfd1, (SA *)&cliaddr, &len); 67 68 if( 0 == (childpid = Fork()) ) 69 { 70 /* child process */ 71 Close(listenfd1); 72 73 str_echo1(connfd); 74 exit(0); 75 } 76 77 /* parent process */ 78 Close(connfd); 79 80 } 81 82 83 if(FD_ISSET(listenfd2, &rset)) 84 { 85 socklen_t len = sizeof(cliaddr); 86 int connfd = Accept(listenfd2, (SA *)&cliaddr, &len); 87 88 if( 0 == (childpid = Fork()) ) 89 { 90 /* child process */ 91 Close(listenfd2); 92 93 str_echo2(connfd); 94 exit(0); 95 } 96 97 /* parent process */ 98 Close(connfd); 99 100 } 101 102 } 103 104 exit(0); 105 } 106 107 void str_echo1(int connfd) 108 { 109 struct sockaddr_in clientAddr; 110 socklen_t len = sizeof(clientAddr); 111 112 if(getpeername(connfd, (SA*) &clientAddr, &len) < 0) 113 { 114 return; 115 } 116 117 char lcBuffer[MAXLINE] = { 0}; 118 sprintf(lcBuffer, "%u Echo 1.", clientAddr.sin_addr.s_addr); 119 120 printf("%s/n", lcBuffer); 121 122 Write(connfd, lcBuffer, MAXLINE); 123 } 124 125 126 void str_echo2(int connfd) 127 { 128 struct sockaddr_in clientAddr; 129 socklen_t len = sizeof(clientAddr); 130 131 if(getpeername(connfd, (SA*) &clientAddr, &len) < 0) 132 { 133 return; 134 } 135 136 137 char lcBuffer[MAXLINE] = { 0}; 138 sprintf(lcBuffer, "%u Echo 2.", clientAddr.sin_addr.s_addr); 139 140 printf("%s/n", lcBuffer); 141 142 Write(connfd, lcBuffer, MAXLINE); 143 } 144 145
客户端源代码:
1 #include "unp.h"
2 3 int main(int argc, char **argv) 4 { 5 int sockfd, n; 6 char recvline[MAXLINE + 1]; 7 struct sockaddr_in servaddr; 8 9 if (argc != 3) 10 err_quit("usage: a.out <IPaddress> <IPPort>"); 11 12 int port = atoi(argv[2]); 13 14 if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) 15 err_sys("socket error"); 16 17 bzero(&servaddr, sizeof(servaddr)); 18 servaddr.sin_family = AF_INET; 19 servaddr.sin_port = htons(port); 20 if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <=0) 21 err_quit("inet_pton error for %s", argv[1]); 22 23 if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) < 0) 24 err_sys("connect error"); 25 26 printf("Conncet OK/n"); 27 28 while ( (n = read(sockfd, recvline, MAXLINE)) > 0) { 29 recvline[n] = 0; /* null terminate */ 30 31 /* change number string to number and to ip string */ 32 struct in_addr svraddr; 33 svraddr.s_addr = strtoul(recvline, NULL, 10); 34 char *pszsvraddr = inet_ntoa(svraddr); 35 36 printf("%s:%s/n", pszsvraddr, recvline); 37 } 38 if (n < 0) 39 err_sys("read error"); 40 41 exit(0); 42 }下面更简洁的代码会加深理解
我们要监控readfd文件描述符数组
int use_select(int *readfd,int n)
{
fd_set my_readfd;
int maxfd;
int i;
maxfd=readfd[0];
for(i=1;i <n;++i) if(readfd[i]>maxfd) maxfd=readfd[i];
while(1)
{
/* 将所有的文件描述符加入 */
FD_ZERO(&my_readfd);
for(i=0;i <n;++i) FD_SET(readfd[i],*my_readfd);
/* 进程阻塞 */
select(maxfd+1,& my_readfd,NULL,NULL,NULL);
/* 有东西可以读了 */
for(i=0;i <n;++i) if(FD_ISSET(readfd[i],&my_readfd))
{
/* 原来是我可以读了 */
we_read(readfd[i]);
}
}
}
select监控的是一个文件描述符数组,一旦至少一个文件描述符可以用了,select停止堵塞,这时程序需要一个轮询确定具体哪个文件可以用了。
下面是一个 用select实现的异步聊天程序:
服务器端:
#include用select实现服务器的好处是,当客户与其建立连接之后,客户可能要过段时间才发送消息,如果之前普通的处理,服务器只能堵塞在recv,不能有其他操作;而用select监控输入文件描述符和socket描述符,只要有一个准备好,select就返回,譬如,select堵塞时,只要服务器输入消息,并回车发送时,select检测到输入文件描述符,就立马返回,将消息发送给客户端;#include #include #include #include #include #include #include #include #include #include #include #define MAXBUF 1024/************关于本文档*********************************************filename: async-server.c*purpose: 演示网络异步通讯,这是服务器端程序*wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言*date time:2007-01-25 21:22*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途* 但请遵循GPL*Thanks to: Google.com*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!*********************************************************************/int main(int argc, char **argv){ int sockfd, new_fd; socklen_t len; struct sockaddr_in my_addr, their_addr; unsigned int myport, lisnum; char buf[MAXBUF + 1]; fd_set rfds; struct timeval tv; int retval, maxfd = -1; if (argv[1]) myport = atoi(argv[1]); else myport = 7838; if (argv[2]) lisnum = atoi(argv[2]); else lisnum = 2; if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) { perror("socket"); exit(1); } bzero(&my_addr, sizeof(my_addr)); my_addr.sin_family = PF_INET; my_addr.sin_port = htons(myport); if (argv[3]) my_addr.sin_addr.s_addr = inet_addr(argv[3]); else my_addr.sin_addr.s_addr = INADDR_ANY; if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) == -1) { perror("bind"); exit(1); } if (listen(sockfd, lisnum) == -1) { perror("listen"); exit(1); } while (1) { printf ("\n----等待新的连接到来开始新一轮聊天……\n"); len = sizeof(struct sockaddr); if ((new_fd = accept(sockfd, (struct sockaddr *) &their_addr, &len)) == -1) { perror("accept"); exit(errno); } else printf("server: got connection from %s, port %d, socket %d\n", inet_ntoa(their_addr.sin_addr), ntohs(their_addr.sin_port), new_fd); /* 开始处理每个新连接上的数据收发 */ printf ("\n准备就绪,可以开始聊天了……直接输入消息回车即可发信息给对方\n"); while (1) { /* 把集合清空 */ FD_ZERO(&rfds); /* 把标准输入句柄0加入到集合中 */ FD_SET(0, &rfds); maxfd = 0; /* 把当前连接句柄new_fd加入到集合中 */ FD_SET(new_fd, &rfds); if (new_fd > maxfd) maxfd = new_fd; /* 设置最大等待时间 */ tv.tv_sec = 1; tv.tv_usec = 0; /* 开始等待 */ retval = select(maxfd + 1, &rfds, NULL, NULL, &tv); if (retval == -1) { printf("将退出,select出错! %s", strerror(errno)); break; } else if (retval == 0) { /* printf ("没有任何消息到来,用户也没有按键,继续等待……\n"); */ continue; } else { if (FD_ISSET(0, &rfds)) { /* 用户按键了,则读取用户输入的内容发送出去 */ bzero(buf, MAXBUF + 1); fgets(buf, MAXBUF, stdin); if (!strncasecmp(buf, "quit", 4)) { printf("自己请求终止聊天!\n"); break; } len = send(new_fd, buf, strlen(buf) - 1, 0); if (len > 0) printf ("消息:%s\t发送成功,共发送了%d个字节!\n", buf, len); else { printf ("消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n", buf, errno, strerror(errno)); break; } } if (FD_ISSET(new_fd, &rfds)) { /* 当前连接的socket上有消息到来则接收对方发过来的消息并显示 */ bzero(buf, MAXBUF + 1); /* 接收客户端的消息 */ len = recv(new_fd, buf, MAXBUF, 0); if (len > 0) printf ("接收消息成功:'%s',共%d个字节的数据\n", buf, len); else { if (len < 0) printf ("消息接收失败!错误代码是%d,错误信息是'%s'\n", errno, strerror(errno)); else printf("对方退出了,聊天终止\n"); break; } } } } close(new_fd); /* 处理每个新连接上的数据收发结束 */ printf("还要和其它连接聊天吗?(no->退出)"); fflush(stdout); bzero(buf, MAXBUF + 1); fgets(buf, MAXBUF, stdin); if (!strncasecmp(buf, "no", 2)) { printf("终止聊天!\n"); break; } } close(sockfd); return 0;}
客户端程序:
#include编译用如下命令:#include #include #include #include #include #include #include #include #include #include #define MAXBUF 1024/************关于本文档********************************************// *filename: ssync-client.c*purpose: 演示网络异步通讯,这是客户端程序*wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言*date time:2007-01-25 21:32*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途* 但请遵循GPL*Thanks to: Google.com*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!*********************************************************************/int main(int argc, char **argv){ int sockfd, len; struct sockaddr_in dest; char buffer[MAXBUF + 1]; fd_set rfds; struct timeval tv; int retval, maxfd = -1; if (argc != 3) { printf ("参数格式错误!正确用法如下:\n\t\t%s IP地址 端口\n\t比如:\t%s 127.0.0.1 80\n此程序用来从某个 IP 地址的服务器某个端口接收最多 MAXBUF 个字节的消息", argv[0], argv[0]); exit(0); } /* 创建一个 socket 用于 tcp 通信 */ if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("Socket"); exit(errno); } /* 初始化服务器端(对方)的地址和端口信息 */ bzero(&dest, sizeof(dest)); dest.sin_family = AF_INET; dest.sin_port = htons(atoi(argv[2])); if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0) { perror(argv[1]); exit(errno); } /* 连接服务器 */ if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) { perror("Connect "); exit(errno); } printf ("\n准备就绪,可以开始聊天了……直接输入消息回车即可发信息给对方\n"); while (1) { /* 把集合清空 */ FD_ZERO(&rfds); /* 把标准输入句柄0加入到集合中 */ FD_SET(0, &rfds); maxfd = 0; /* 把当前连接句柄sockfd加入到集合中 */ FD_SET(sockfd, &rfds); if (sockfd > maxfd) maxfd = sockfd; /* 设置最大等待时间 */ tv.tv_sec = 1; tv.tv_usec = 0; /* 开始等待 */ retval = select(maxfd + 1, &rfds, NULL, NULL, &tv); if (retval == -1) { printf("将退出,select出错! %s", strerror(errno)); break; } else if (retval == 0) { /* printf ("没有任何消息到来,用户也没有按键,继续等待……\n"); */ continue; } else { if (FD_ISSET(sockfd, &rfds)) { /* 连接的socket上有消息到来则接收对方发过来的消息并显示 */ bzero(buffer, MAXBUF + 1); /* 接收对方发过来的消息,最多接收 MAXBUF 个字节 */ len = recv(sockfd, buffer, MAXBUF, 0); if (len > 0) printf ("接收消息成功:'%s',共%d个字节的数据\n", buffer, len); else { if (len < 0) printf ("消息接收失败!错误代码是%d,错误信息是'%s'\n", errno, strerror(errno)); else printf("对方退出了,聊天终止!\n"); break; } } if (FD_ISSET(0, &rfds)) { /* 用户按键了,则读取用户输入的内容发送出去 */ bzero(buffer, MAXBUF + 1); fgets(buffer, MAXBUF, stdin); if (!strncasecmp(buffer, "quit", 4)) { printf("自己请求终止聊天!\n"); break; } /* 发消息给服务器 */ len = send(sockfd, buffer, strlen(buffer) - 1, 0); if (len < 0) { printf ("消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n", buffer, errno, strerror(errno)); break; } else printf ("消息:%s\t发送成功,共发送了%d个字节!\n", buffer, len); } } } /* 关闭连接 */ close(sockfd); return 0;}
下面还是一个简单的并发处理的服务器,但是仅仅是能多路复用I/O,但是不能同时处理连接请求,因为这只有一个线程
使用select函数可以以非阻塞的方式和多个socket通信。程序只是演示select函数的使用,功能非常简单,即使某个连接关闭以后也不会修改当前连接数,连接数达到最大值后会终止程序。
1. 程序使用了一个数组fd_A,通信开始后把需要通信的多个socket描述符都放入此数组。
2. 首先生成一个叫sock_fd的socket描述符,用于监听端口。
3. 将sock_fd和数组fd_A中不为0的描述符放入select将检查的集合fdsr。
4. 处理fdsr中可以接收数据的连接。如果是sock_fd,表明有新连接加入,将新加入连接的socket描述符放置到fd_A。
#include有网友提出了点改进:#include #include #include #include #include #include #include #include #define MYPORT 1234 // the port users will be connecting to#define BACKLOG 5 // how many pending connections queue will hold#define BUF_SIZE 200int fd_A[BACKLOG]; // accepted connection fdint conn_amount; // current connection amountvoid showclient(){ int i; printf("client amount: %d\n", conn_amount); for (i = 0; i < BACKLOG; i++) { printf("[%d]:%d ", i, fd_A[i]); } printf("\n\n");}int main(void){ int sock_fd, new_fd; // listen on sock_fd, new connection on new_fd struct sockaddr_in server_addr; // server address information struct sockaddr_in client_addr; // connector's address information socklen_t sin_size; int yes = 1; char buf[BUF_SIZE]; int ret; int i; if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket"); exit(1); } if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { perror("setsockopt"); exit(1); } server_addr.sin_family = AF_INET; // host byte order server_addr.sin_port = htons(MYPORT); // short, network byte order server_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP memset(server_addr.sin_zero, '\0', sizeof(server_addr.sin_zero)); if (bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) { perror("bind"); exit(1); } if (listen(sock_fd, BACKLOG) == -1) { perror("listen"); exit(1); } printf("listen port %d\n", MYPORT); fd_set fdsr; int maxsock; struct timeval tv; conn_amount = 0; sin_size = sizeof(client_addr); maxsock = sock_fd; while (1) { // initialize file descriptor set FD_ZERO(&fdsr); FD_SET(sock_fd, &fdsr); // timeout setting tv.tv_sec = 30; tv.tv_usec = 0; // add active connection to fd set for (i = 0; i < BACKLOG; i++) { if (fd_A[i] != 0) { FD_SET(fd_A[i], &fdsr); } } ret = select(maxsock + 1, &fdsr, NULL, NULL, &tv); if (ret < 0) { perror("select"); break; } else if (ret == 0) { printf("timeout\n"); continue; } // check every fd in the set for (i = 0; i < conn_amount; i++) { if (FD_ISSET(fd_A[i], &fdsr)) { ret = recv(fd_A[i], buf, sizeof(buf), 0); if (ret <= 0) { // client close printf("client[%d] close\n", i); close(fd_A[i]); FD_CLR(fd_A[i], &fdsr); fd_A[i] = 0; } else { // receive data if (ret < BUF_SIZE) memset(&buf[ret], '\0', 1); printf("client[%d] send:%s\n", i, buf); } } } // check whether a new connection comes if (FD_ISSET(sock_fd, &fdsr)) { new_fd = accept(sock_fd, (struct sockaddr *)&client_addr, &sin_size); if (new_fd <= 0) { perror("accept"); continue; } // add to fd queue if (conn_amount < BACKLOG) { fd_A[conn_amount++] = new_fd; printf("new connection client[%d] %s:%d\n", conn_amount, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); if (new_fd > maxsock) maxsock = new_fd; } else { printf("max connections arrive, exit\n"); send(new_fd, "bye", 4, 0); close(new_fd); break; } } showclient(); } // close other connections for (i = 0; i < BACKLOG; i++) { if (fd_A[i] != 0) { close(fd_A[i]); } } exit(0);}
accept函数里面fd_A[conn_amount++] = new_fd;可以稍加改进,按照楼主的意图,会出现当一个用户不断连接再断开的情况下,当连接次数超过maxconnection的时候,就会退出,因此fd_A[i]没有很好的利用,不能实现动态管理,我建议仅将conn_amount仅作为客户端连接数,而不是有连接就增加,当accept成功的时候,就加1,当recv=0的时候就减1;建议将fd_A[conn_amount++] = new_fd;这句程序改为
for(i = 0;i < MAXCLIENT;i++)
{
if(fd[i] == 0)
{
fd[i] = new_fd;
break;
}
}
conn_amount++;
这样就可以重复利用fd[i]的空间;
另外在recv返回值<=0的时候,加一句conn_amount++;
还有一点,超过最大连接数的时候break应该为continue,这样会更人性化一点,客户端太多关闭它的请求就行了,没必要自毁,这样整个系统就可以动态与客户端实现连接,
转载地址:http://hneti.baihongyu.com/