博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
linux--select
阅读量:4154 次
发布时间:2019-05-25

本文共 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, NULLNULL,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             if0 == (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             if0 == (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, NULL10);
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 
#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;}
用select实现服务器的好处是,当客户与其建立连接之后,客户可能要过段时间才发送消息,如果之前普通的处理,服务器只能堵塞在recv,不能有其他操作;而用select监控输入文件描述符和socket描述符,只要有一个准备好,select就返回,譬如,select堵塞时,只要服务器输入消息,并回车发送时,select检测到输入文件描述符,就立马返回,将消息发送给客户端;

客户端程序:

#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;}
编译用如下命令:
gcc -Wall async-server.c -o server
gcc -Wall async-client.c -o client
运行用如下命令:
./server 7838 1
./client 127.0.0.1 7838

下面还是一个简单的并发处理的服务器,但是仅仅是能多路复用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/

你可能感兴趣的文章
我对C++ string和length方法的一个长期误解------从protobuf序列化说起(没处理好会引起数据丢失、反序列化失败哦!)
查看>>
一起来看看protobuf中容易引起bug的一个细节
查看>>
无protobuf协议情况下的反序列化------貌似无解, 其实有解!
查看>>
make -n(仅列出命令, 但不会执行)用于调试makefile
查看>>
makefile中“-“符号的使用
查看>>
go语言如何从终端逐行读取数据?------用bufio包
查看>>
go的值类型和引用类型------重要的概念
查看>>
求二叉树中结点的最大值(所有结点的值都是正整数)
查看>>
用go的flag包来解析命令行参数
查看>>
来玩下go的http get
查看>>
队列和栈的本质区别
查看>>
matlab中inline的用法
查看>>
如何用matlab求函数的最值?
查看>>
Git从入门到放弃
查看>>
java8采用stream对集合的常用操作
查看>>
EasySwift/YXJOnePixelLine 极其方便的画出真正的一个像素的线
查看>>
Ubuntu系统上安装Nginx服务器的简单方法
查看>>
Ubuntu Linux系统下apt-get命令详解
查看>>
ubuntu 16.04 下重置 MySQL 5.7 的密码(忘记密码)
查看>>
Ubuntu Navicat for MySQL安装以及破解方案
查看>>