小新pro更改网站设置,cms 免费,做网站 创业 流程,python wordpress采集器http://blog.csdn.net/chenxun_2010/article/details/50489577 一、用select实现的并发服务器#xff0c;能达到的并发数#xff0c;受两方面限制 1、一个进程能打开的最大文件描述符限制。这可以通过调整内核参数。可以通过ulimit -n来调整或者使用setrlimit函数设置#x…http://blog.csdn.net/chenxun_2010/article/details/50489577 一、用select实现的并发服务器能达到的并发数受两方面限制 1、一个进程能打开的最大文件描述符限制。这可以通过调整内核参数。可以通过ulimit -n来调整或者使用setrlimit函数设置 但一个系统所能打开的最大数也是有限的跟内存大小有关可以通过cat /proc/sys/fs/file-max 查看 2、select中的fd_set集合容量的限制FD_SETSIZE一般为1024 这需要重新编译内核。 可以写个测试程序只建立连接看看最多能够建立多少个连接客户端程序如下 [cpp] view plaincopy #include sys/types.h #include sys/socket.h #include netinet/in.h #include arpa/inet.h #include signal.h #include stdlib.h #include stdio.h #include errno.h #include string.h #define ERR_EXIT(m) \ do \ { \ perror(m); \ exit(EXIT_FAILURE); \ } while(0) int main(void) { int count 0; while(1) { int sock; if ((sock socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) 0) { sleep(4); ERR_EXIT(socket); } struct sockaddr_in servaddr; memset(servaddr, 0, sizeof(servaddr)); servaddr.sin_family AF_INET; servaddr.sin_port htons(5188); servaddr.sin_addr.s_addr inet_addr(127.0.0.1); if (connect(sock, (struct sockaddr *)servaddr, sizeof(servaddr)) 0) ERR_EXIT(connect); struct sockaddr_in localaddr; socklen_t addrlen sizeof(localaddr); if (getsockname(sock, (struct sockaddr *)localaddr, addrlen) 0) ERR_EXIT(getsockname); printf(ip%s port%d\n, inet_ntoa(localaddr.sin_addr), ntohs(localaddr.sin_port)); printf(count %d\n, count); } return 0; } 服务器的代码serv.c[cpp] view plaincopy #includestdio.h #includesys/types.h #includesys/socket.h #includeunistd.h #includestdlib.h #includeerrno.h #includearpa/inet.h #includenetinet/in.h #includestring.h #includesignal.h #includesys/wait.h #define ERR_EXIT(m) \ do { \ perror(m); \ exit(EXIT_FAILURE); \ } while (0) int main(void) { signal(SIGPIPE, SIG_IGN); int listenfd; //被动套接字(文件描述符即只可以accept, 监听套接字 if ((listenfd socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) 0) // listenfd socket(AF_INET, SOCK_STREAM, 0) ERR_EXIT(socket error); struct sockaddr_in servaddr; memset(servaddr, 0, sizeof(servaddr)); servaddr.sin_family AF_INET; servaddr.sin_port htons(5188); servaddr.sin_addr.s_addr htonl(INADDR_ANY); /* servaddr.sin_addr.s_addr inet_addr(127.0.0.1); */ /* inet_aton(127.0.0.1, servaddr.sin_addr); */ int on 1; if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, on, sizeof(on)) 0) ERR_EXIT(setsockopt error); if (bind(listenfd, (struct sockaddr*)servaddr,sizeof(servaddr)) 0) ERR_EXIT(bind error); if (listen(listenfd, SOMAXCONN) 0) //listen应在socket和bind之后而在accept之前 ERR_EXIT(listen error); struct sockaddr_in peeraddr; //传出参数 socklen_t peerlen sizeof(peeraddr); //传入传出参数必须有初始值 int conn; // 已连接套接字(变为主动套接字即可以主动connect) int i; int client[FD_SETSIZE]; int maxi 0; // client数组中最大不空闲位置的下标 for (i 0; i FD_SETSIZE; i) client[i] -1; int nready; int maxfd listenfd; fd_set rset; fd_set allset; FD_ZERO(rset); FD_ZERO(allset); FD_SET(listenfd, allset); int count 0; while (1) { rset allset; nready select(maxfd 1, rset, NULL, NULL, NULL); if (nready -1) { if (errno EINTR) continue; ERR_EXIT(select error); } if (nready 0) continue; if (FD_ISSET(listenfd, rset)) { conn accept(listenfd, (struct sockaddr*)peeraddr, peerlen); //accept不再阻塞 if (conn -1) ERR_EXIT(accept error); printf(count %d\n, count); for (i 0; i FD_SETSIZE; i) { if (client[i] 0) { client[i] conn; if (i maxi) maxi i; break; } } if (i FD_SETSIZE) { fprintf(stderr, too many clients\n); exit(EXIT_FAILURE); } printf(recv connect ip%s port%d\n, inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port)); FD_SET(conn, allset); if (conn maxfd) maxfd conn; if (--nready 0) continue; } for (i 0; i maxi; i) { conn client[i]; if (conn -1) continue; if (FD_ISSET(conn, rset)) { char recvbuf[1024] {0}; int ret read(conn, recvbuf, 1024); if (ret -1) ERR_EXIT(read error); else if (ret 0) { //客户端关闭 printf(client close \n); FD_CLR(conn, allset); client[i] -1; close(conn); } fputs(recvbuf, stdout); write(conn, recvbuf, strlen(recvbuf)); if (--nready 0) break; } } } return 0; } /* select所能承受的最大并发数受 * 1.一个进程所能打开的最大文件描述符数可以通过ulimit -n来调整 * 但一个系统所能打开的最大数也是有限的跟内存有关可以通过cat /proc/sys/fs/file-max 查看 * 2.FD_SETSIZE(fd_set)的限制这个需要重新编译内核 */ huangchengubuntu:~$ ./serv count 1 recv connect ip127.0.0.1 port48370 count 2 recv connect ip127.0.0.1 port48371 count 3 recv connect ip127.0.0.1 port48372 count 4 recv connect ip127.0.0.1 port48373 .................................... recv connect ip127.0.0.1 port49389 count 1020 recv connect ip127.0.0.1 port49390 accept error: Too many open files [cpp] view plaincopyprint? huangchengubuntu:~$ ./cli ip127.0.0.1 port46327 count 1 ip127.0.0.1 port46328 count 2 ip127.0.0.1 port46329 count 3 ip127.0.0.1 port46330 count 4 ip127.0.0.1 port46331 count 5 ip127.0.0.1 port46332 count 6 ip127.0.0.1 port46333 ....................... ip127.0.0.1 port47345 count 1020 ip127.0.0.1 port47346 count 1021 socket: Too many open files 输出太多条目上面只截取最后几条从中可以看出对于客户端最多只能开启1021个连接套接字因为总共是1024个还得除去0、1、2。而服务器端只能accept 返回1020个已连接套接字因为除了0、1、2之外还有一个监听套接字客户端某一个套接字不一定是最后一个虽然已经建立了连接在已完成连接队列中但accept 返回时达到最大描述符限制返回错误打印提示信息。 也许有人会注意到上面有一行 sleep(4)当客户端调用socket准备创建第1022个套接字时如上所示也会提示错误此时socket函数返回-1出错如果没有睡眠4s后再退出进程会有什么问题呢如果直接退出进程会将客户端所打开的所有套接字关闭掉即向服务器端发送了很多FIN段而此时也许服务器端还一直在accept 即还在从已连接队列中返回已连接套接字此时服务器端除了关心监听套接字的可读事件也开始关心前面已建立连接的套接字的可读事件read 返回0所以会有很多 client close 字段 参杂在条目的输出中还有个问题就是因为read 返回0服务器端会将自身的已连接套接字关闭掉那么也许刚才说的客户端某一个连接会被accept 返回即测试不出服务器端真正的并发容量。 huangchengubuntu:~$ ./serv count 1 recv connect ip127.0.0.1 port50413 count 2 .................................... client close client close client close client close ................................... recv connect ip127.0.0.1 port51433 client close count 1021 recv connect ip127.0.0.1 port51364 client close client close 可以看到输出参杂着client close且这次的count 达到了1021原因就是服务器端前面已经有些套接字关闭了所以accept 创建套接字不会出错服务器进程也不会因为出错而退出可以看到最后接收到的一个连接端口是51364即不一定是客户端的最后一个连接。二、poll 函数应用举例 [cpp] view plaincopy #include poll.h int poll(struct pollfd *fds, nfds_t nfds, int timeout); 参数1结构体数组指针[cpp] view plaincopy struct pollfd { int fd; /* file descriptor */ short events; /* requested events */ short revents; /* returned events */ }; 结构体中的fd 即套接字描述符events 即感兴趣的事件如下图所示revents 即返回的事件。 参数2结构体数组的成员个数即文件描述符个数。 参数3即超时时间若为-1表示永不超时。 poll 跟 select 还是很相似的比较重要的区别在于poll 所能并发的个数跟FD_SETSIZE无关只跟一个进程所能打开的文件描述符个数有关可以在select 程序的基础上修改成poll 程序在运行服务器端程序之前使用ulimit -n 2048 将限制改成2048个注意在运行客户端进程的终端也需更改因为客户端也会有所限制这只是临时性的更改因为子进程会继承这个环境参数而我们是在bash命令行启动程序的故在进程运行期间文件描述符的限制为2048个。 使用poll 函数的服务器端程序如下 [cpp] view plaincopy #includestdio.h #includesys/types.h #includesys/socket.h #includeunistd.h #includestdlib.h #includeerrno.h #includearpa/inet.h #includenetinet/in.h #includestring.h #includesignal.h #includesys/wait.h #includepoll.h #define ERR_EXIT(m) \ do { \ perror(m); \ exit(EXIT_FAILURE); \ } while (0) int main(void) { int count 0; signal(SIGPIPE, SIG_IGN); int listenfd; //被动套接字(文件描述符即只可以accept, 监听套接字 if ((listenfd socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) 0) // listenfd socket(AF_INET, SOCK_STREAM, 0) ERR_EXIT(socket error); struct sockaddr_in servaddr; memset(servaddr, 0, sizeof(servaddr)); servaddr.sin_family AF_INET; servaddr.sin_port htons(5188); servaddr.sin_addr.s_addr htonl(INADDR_ANY); /* servaddr.sin_addr.s_addr inet_addr(127.0.0.1); */ /* inet_aton(127.0.0.1, servaddr.sin_addr); */ int on 1; if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, on, sizeof(on)) 0) ERR_EXIT(setsockopt error); if (bind(listenfd, (struct sockaddr *)servaddr, sizeof(servaddr)) 0) ERR_EXIT(bind error); if (listen(listenfd, SOMAXCONN) 0) //listen应在socket和bind之后而在accept之前 ERR_EXIT(listen error); struct sockaddr_in peeraddr; //传出参数 socklen_t peerlen sizeof(peeraddr); //传入传出参数必须有初始值 int conn; // 已连接套接字(变为主动套接字即可以主动connect) int i; struct pollfd client[2048]; int maxi 0; //client[i]最大不空闲位置的下标 for (i 0; i 2048; i) client[i].fd -1; int nready; client[0].fd listenfd; client[0].events POLLIN; while (1) { /* poll检测[0, maxi 1) */ nready poll(client, maxi 1, -1); if (nready -1) { if (errno EINTR) continue; ERR_EXIT(poll error); } if (nready 0) continue; if (client[0].revents POLLIN) { conn accept(listenfd, (struct sockaddr *)peeraddr, peerlen); //accept不再阻塞 if (conn -1) ERR_EXIT(accept error); for (i 1; i 2048; i) { if (client[i].fd 0) { client[i].fd conn; if (i maxi) maxi i; break; } } if (i 2048) { fprintf(stderr, too many clients\n); exit(EXIT_FAILURE); } printf(count %d\n, count); printf(recv connect ip%s port%d\n, inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port)); client[i].events POLLIN; if (--nready 0) continue; } for (i 1; i maxi; i) { conn client[i].fd; if (conn -1) continue; if (client[i].revents POLLIN) { char recvbuf[1024] {0}; int ret read(conn, recvbuf, 1024); if (ret -1) ERR_EXIT(readline error); else if (ret 0) //客户端关闭 { printf(client close \n); client[i].fd -1; close(conn); } fputs(recvbuf, stdout); write(conn, recvbuf, strlen(recvbuf)); if (--nready 0) break; } } } return 0; } /* poll 只受一个进程所能打开的最大文件描述符限制这个可以使用ulimit -n调整 */ 参照前面对
select 函数
的解释不难理解上面的程序就不再赘述了。来看一下输出[cpp] view plaincopy rootubuntu:/home/huangcheng# ulimit -n 2048 rootubuntu:/home/huangcheng# su - huangcheng huangchengubuntu:~$ ulimit -n 2048 huangchengubuntu:~$ ./serv ........................... count 2042 recv connect ip127.0.0.1 port54499 count 2043 recv connect ip127.0.0.1 port54500 count 2044 recv connect ip127.0.0.1 port54501 accept error: Too many open files [cpp] view plaincopy rootubuntu:/home/huangcheng# ulimit -n 2048 rootubuntu:/home/huangcheng# su - huangcheng huangchengubuntu:~$ ulimit -n 2048 huangchengubuntu:~$./cli .......................... ip127.0.0.1 port54499 count 2043 ip127.0.0.1 port54500 count 2044 ip127.0.0.1 port54501 count 2045 socket: Too many open files 可以看到现在最大的连接数已经是2045个了虽然服务器端有某个连接没有accept 返回。即poll 比 select 能够承受更多的并发连接只受一个进程所能打开的最大文件描述符个数限制。可以通过ulimit -n 修改但一个系统所能打开的文件描述符个数也是有限的这跟系统的内存大小有关系所以说也不是可以无限地并
发可以查看一下本机的容量[cpp] view plaincopy huangchengubuntu:~$ cat /proc/sys/fs/file-max 101598 本机是虚拟机内存2G能够打开的文件描述符个数大约在10w个左右。