网站的logo在百度怎么显示不出来,自建app平台,珠海蓝迪装饰设计工程有限公司,百度搜索引擎排行榜一.I/O多路复用
I/O多路复用是一种用于提高系统性能的 I/O 处理机制。 它允许一个进程#xff08;或线程#xff09;同时监视多个文件描述符#xff08;可以是套接字、管道、终端设备等#xff09;#xff0c;等待这些文件描述符中出现读、写或异常状态。一旦有满足条件的…一.I/O多路复用
I/O多路复用是一种用于提高系统性能的 I/O 处理机制。 它允许一个进程或线程同时监视多个文件描述符可以是套接字、管道、终端设备等等待这些文件描述符中出现读、写或异常状态。一旦有满足条件的文件描述符状态发生变化进程或线程就能立即得到通知并进行相应的 I/O 操作处理。 在socket编程阶段,未接触IO多路复用前服务器需要阻塞在accept函数上直到等待客户端通过connect发起连接请求才执行接下来的逻辑。 而使用IO多路复用可以先监听是否有客户端的连接操作再执行accept从而不再使服务器阻塞在accept上。还可监视客户端的读写交互操作以及异常操作执行对应逻辑。
二.select
1.功能 select去监听客户端连接(lfd)当有客户端进行连接时 它会让server去调用accept(当有连接时才去立即调用而不是一直阻塞等待)accept返回一个用于通信的cfd将其加入select监听集合使其监管着lfd(监听socket)和所有cfd(通信socket)。 2.函数解析 int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeva l *timeout); 加以解释 select(监听最大文件描述符1读监听fd集合写监听fd集合异常监听fd集合等待时长) // ● nfds监听的所有文件描述符中最大文件描述符1 ● readfdsfd_set类型读 文件描述符监听集合。传入传出参数传入的fd会被监听传出时返回实际发生事件的fd未发生对应事件的被剔除。 ● writefdsfd_set类型写 文件描述符监听集合。传入传出参数传入传出同上通常传NULL ● exceptfdsfd_set类型异常 文件描述符监听集合。传入传出参数传入传出同上通常传NULL ● timeout大于0表示设置监听时长NULL表示阻塞监听0表示非阻塞监听 while轮询 // 返回值: ● 大于0所有监听集合(读、写、异常)中满足对应事件的总数 ● 0没有满足监听条件的文件描述符 ● -1error // fd_set:本质是位图通过对应操作加入删除查询文件描述符 监听集合对应函数
void FD_ZERO(fd_set *set); –清空一个文件描述符集合 fd_set rset FD_ZERO(rset) //将rset集合清空void FD_SET(int fd, fd_set *set); –将待监听的文件描述符添加到监听集合中 FD_SET(3,rset)FD_SET(5,rset) //将文件描述符3和5加到rset集合中void FD_CLR(int fd, fd_set *set); –将一个文件描述符从监听集合中移除int FD_ISSET(int fd, fd_set *set); –判断一个文件描述符是否在该集合中存在返回1不存在返回0
3.select函数和fd_set底层原理 文件描述符表前三个默认被系统占用 fd_set集合传入的是文件描述符传出所有监听集合(读、写、异常)中满足对应事件的总数 fd_set集合的本质位图(二进制位存放文件描述符的状态)默认都为0若发生变化就置1 4.select实现IO多路复用服务器思路及代码实现、 思路
代码实现:
#include stdio.h
#include ctype.h
#include sys/types.h
#include sys/socket.h
#include arpa/inet.h
#include stdlib.h
#include string.h
#include unistd.h
#include errno.h
#include sys/time.h
#include sys/select.h#define SERVE_PORT 9527int main()
{int lfd, cfd, ret, len, j, i;char buf[BUFSIZ];//地址结构体struct sockaddr_in serve_addr, client_addr;socklen_t client_addr_len;bzero(serve_addr,sizeof(serve_addr)); //结构体清空serve_addr.sin_family AF_INET;serve_addr.sin_port htons(SERVE_PORT);serve_addr.sin_addr.s_addr htonl(INADDR_ANY);//创建socketlfd socket(AF_INET,SOCK_STREAM,0);int maxfd lfd; //最大的文件描述符if(lfd-1){perror(socket error);exit(1);}//绑定ip和端口bind(lfd,(struct sockaddr *)serve_addr,sizeof(serve_addr));//设置上限listen(lfd,128);fd_set rset, allset; //设置监听的集合FD_ZERO(allset); //清空集合FD_SET(lfd,allset); //将lfd加入到监听集合while(1){rset allset;ret select(maxfd1,rset,NULL,NULL,NULL); if(ret0){perror(select error);exit(1);}if(FD_ISSET(lfd,rset)) //如果lfd在传出的rset中表示有客户端要进行连接{client_addr_len sizeof(client_addr);cfd accept(lfd,(struct sockaddr *)client_addr,client_addr_len);FD_SET(cfd,allset); //将cfd加入到监听集合if(maxfdcfd)maxfd cfd; //更新最大的文件描述符//说明select只返回了lfd这一个后续指令无需执行,continue跳出本次继续whileif(ret1) continue;}//如果ret!1 说明还监听到了其他描述符的read事件for(ilfd1; imaxfd; i){if(FD_ISSET(i,rset)){ //找到满足读事件的那个描述符len read(i,buf,sizeof(buf));if(len0) //检测到客户端关闭了连接{close(i); //关闭该描述符FD_CLR(i,allset); //将该描述符从监听集合中移除}//如果len不为0就表示有数据循环更改数据for(j0; jlen; j)buf[j] toupper(buf[j]);//写回更改后的数据write(i,buf,len);write(STDOUT_FILENO,buf,len);}}}close(lfd); return 0;
}关于rset和allset的解析 因为select函数返回出来的是满足条件的如果我们将新的cfd也加入到rset中那么等到下次循环时如果刚刚加入的cfd没有读写数据发生时就会被踢出rset此时我们需要用allset来存储新进来的cfd每次进while循环后就赋值给rset
三.poll
相对select没有太大改进属于半成品了解即可 1.函数解析 int poll(struct pollfd *fds, nfds_t nfds, int timeout); 参数解释 struct pollfd* fds: 监听文件数组包括描述符fd监听事件events(读POLLIN,POLLOUT,POLLERR写异常)返回事件revents(读写异常) nfds_t nfds:实际监听的文件描述符最大个数
int timeout: 等待时间 大于0 : 设置超时时长 -1 阻塞等待 0不阻塞等待
返回值返回 监听到实际发生动作的描述符总数量
2.对比select的优化
①自带结构体数组(不必再使用FD_SET位图表示读写异常监听集合而是在结构体内部利用POLL_IN等宏表示)。 ②可以将监听事件集合和返回事件集合 分离。 ③拓展 监听上限 超出文件描述符最大1024的限制。
3.poll实现的多路io复用服务器
1./* server.c */
2.#include stdio.h
3.#include stdlib.h
4.#include string.h
5.#include netinet/in.h
6.#include arpa/inet.h
7.#include poll.h
8.#include errno.h
9.#include wrap.h
10.
11.#define MAXLINE 80
12.#define SERV_PORT 6666
13.#define OPEN_MAX 1024
14.
15.int main(int argc, char *argv[])
16.{
17. int i, j, maxi, listenfd, connfd, sockfd;
18. int nready;
19. ssize_t n;
20. char buf[MAXLINE], str[INET_ADDRSTRLEN];
21. socklen_t clilen;
22. struct pollfd client[OPEN_MAX];
23. struct sockaddr_in cliaddr, servaddr;
24.
25. listenfd Socket(AF_INET, SOCK_STREAM, 0);
26.
27. bzero(servaddr, sizeof(servaddr));
28. servaddr.sin_family AF_INET;
29. servaddr.sin_addr.s_addr htonl(INADDR_ANY);
30. servaddr.sin_port htons(SERV_PORT);
31.
32. Bind(listenfd, (struct sockaddr *)servaddr, sizeof(servaddr));
33.
34. Listen(listenfd, 20);
35.
36. client[0].fd listenfd;
37. client[0].events POLLRDNORM; /* listenfd监听普通读事件 */
38.
39. for (i 1; i OPEN_MAX; i)
40. client[i].fd -1; /* 用-1初始化client[]里剩下元素 */
41. maxi 0; /* client[]数组有效元素中最大元素下标 */
42.
43. for ( ; ; ) {
44. nready poll(client, maxi1, -1); /* 阻塞 */
45. if (client[0].revents POLLRDNORM) { /* 有客户端链接请求 */
46. clilen sizeof(cliaddr);
47. connfd Accept(listenfd, (struct sockaddr *)cliaddr, clilen);
48. printf(received from %s at PORT %d\n,
49. inet_ntop(AF_INET, cliaddr.sin_addr, str, sizeof(str)),
50. ntohs(cliaddr.sin_port));
51. for (i 1; i OPEN_MAX; i) {
52. if (client[i].fd 0) {
53. client[i].fd connfd; /* 找到client[]中空闲的位置存放accept返回的connfd */
54. break;
55. }
56. }
57.
58. if (i OPEN_MAX)
59. perr_exit(too many clients);
60.
61. client[i].events POLLRDNORM; /* 设置刚刚返回的connfd监控读事件 */
62. if (i maxi)
63. maxi i; /* 更新client[]中最大元素下标 */
64. if (--nready 0)
65. continue; /* 没有更多就绪事件时,继续回到poll阻塞 */
66. }
67. for (i 1; i maxi; i) { /* 检测client[] */
68. if ((sockfd client[i].fd) 0)
69. continue;
70. if (client[i].revents (POLLRDNORM | POLLERR)) {
71. if ((n Read(sockfd, buf, MAXLINE)) 0) {
72. if (errno ECONNRESET) { /* 当收到 RST标志时 */
73. /* connection reset by client */
74. printf(client[%d] aborted connection\n, i);
75. Close(sockfd);
76. client[i].fd -1;
77. } else {
78. perr_exit(read error);
79. }
80. } else if (n 0) {
81. /* connection closed by client */
82. printf(client[%d] closed connection\n, i);
83. Close(sockfd);
84. client[i].fd -1;
85. } else {
86. for (j 0; j n; j)
87. buf[j] toupper(buf[j]);
88. Writen(sockfd, buf, n);
89. }
90. if (--nready 0)
91. break; /* no more readable descriptors */
92. }
93. }
94. }
95. return 0;
96.} 四.优缺点总结 更改单进程打开1024文件限制 的方法: 可以通过 /proc/sys/fs/file-max 查看当前系统最大打开文件个数 可以通过ulimit -a 查看单个进程打开的最大文件个数 修改方法修改配置文件 改写 /etc/security/limits.conf如下图