Linux下的网络socket编程---------------多路复用(poll)

本文对比了poll和select两种多路复用技术的异同,详细解析了poll函数的原型和内部机制,通过代码示例展示了如何使用poll进行高效的网络编程。

1.select和poll的异同

select()和poll()系统调用的本质一样,前者在BSD UNIX中引入的,后者在System V中引入的。poll()的机制与 select() 类 似,与 select() 在本质上没有多大差别,管理多个描述符也是进行轮询,根据描述符的状态进行处理,但是 poll() 没有最大文件 描述符数量的限制(但是数量过大后性能也是会下降)。poll() 和 select() 同样存在一个缺点就是,包含大量文件描述符的数组 被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增 大。

2.函数原型及解析

函数原型:

#include <poll.h>
struct pollfd 
{   
     int     fd;         /* 文件描述符 */   
     short   events;     /* 等待的事件 */   
     short   revents;    /* 实际发生了的事件 */
} ;
int poll(struct pollfd *fds, nfds_t nfds, int timeout);

解析:

  1. 第一个参数用来指向一个struct pollfd类型的数组,每一个pollfd结构体指定了一个被监视的文件描述符,指示poll()监视多个文 件描述符。每个结构体的events域是监视该文件描述符的事件掩码,由用户来设置这个域。revents域是文件描述符的操作结果 事件掩码,内核在调用返回时设置这个域,events域中请求的任何事件都可能在revents域中返回。下表列出指定 events 标志以 及测试 revents 标志的一些常值:
    在这里插入图片描述
    POLLIN | POLLPRI等价于select()的读事件。
    POLLOUT |POLLWRBAND等价于select()的写事件。 POLLIN等价于 POLLRDNORM |POLLRDBAND。POLLOUT则等价于POLLWRNORM。例如,要同时监视一个文件描述符是否可读和可 写,我们可以设置 events为POLLIN |POLLOUT。在poll返回时,我们可以检查revents中的标志,对应于文件描述符请求的 events结构体。如果POLLIN事件被设置,则文件描述符可以被读取而不阻塞。如果POLLOUT被设置,则文件描述符可以写入而 不导致阻塞。这些标志并不是互斥的:它们可能被同时设置,表示这个文件描述符的读取和写入操作都会正常返回而不阻塞。
  2. 第二个参数 nfds 指定数组中监听的元素个数;
  3. 第三个参数 timeout指定等待的毫秒数,无论I/O是否准备好,poll都会返回。timeout指定为负数值表示无限超时,使poll() 一直挂起直到一个指定事件发生;timeout为0指示poll调用立即返回并列出准备好I/O的文件描述符,但并不等待其它的事件。 这种情况下,poll()就像它的名字那样,一旦选举出来,立即返回。
    该函数成功调用时,poll()返回结构体中revents域不为0的文件描述符个数;如果在超时前没有任何事件发生,poll()返回0; 失败时,poll()返回-1,并设置errno为下列值之一:
  • EBADF   一个或多个结构体中指定的文件描述符无效。
  • EFAULTfds   指针指向的地址超出进程的地址空间
  • EINTR     请求的事件之前产生一个信号,调用可以重新发起。
  • EINVALnfds  参数超出PLIMIT_NOFILE值。
  • ENOMEM   可用内存不足,无法完成请求。
3.代码示例
  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 #include<string.h>
  5 #include<errno.h>
  6 #include<ctype.h>
  7 #include<time.h>
  8 #include<pthread.h>
  9 #include<getopt.h>
 10 #include<libgen.h>
 11 #include<sys/types.h>
 12 #include<sys/socket.h>
 13 #include<netinet/in.h>
 14 #include<poll.h>
 15 #include<arpa/inet.h>
 16 
 17 #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
 18 
 19 static inline void print_usage(char *progname);
 20 int socket_server_init(char *listen_ip, int listen_port);
 21 
 22 int main(int argc, char **argv)
 23 {
 24         int               listenfd, connfd;
 25         int               serv_port = 0;
 26         int               daemon_run = 0;
 27         int               opt;
 28         char              *progname = NULL;
 29         int               rv, i, j;
 30         int               found;
 31         int               max;
 32         char              buf[1024];
 33         struct pollfd     fds_array[1024];
 34 
 35         struct option        long_options[] =
 36         {
 37                 {"daemon", no_argument, NULL, 'b'},
 38                 {"port", required_argument, NULL, 'p'},
 39                 {"help", no_argument, NULL, 'h'},
 40                 {NULL, 0, NULL, 0}
 41         };
 42 
 43         progname = basename(argv[0]);
 44 
 45         while((opt = getopt_long(argc,argv,"dp:h",long_options,NULL))!=-1)
 46         {
 47                 switch(opt)
 48                 {
 49                         case 'b':
 50                                 daemon_run = 1;
 51                                 break;
 52 
 53                         case 'p':
 54                                 serv_port = atoi(optarg);
 55                                 break;
 56 
 57                         case 'h':
 58                                 print_usage(progname);
 59                                 return EXIT_SUCCESS;
 60 
 61                         default:
 62                                 break;
 63 
 64                 }
 65         }
 66 
 67         if(!serv_port)
 68         {
 69                 print_usage(progname);
 70                 return -1;
 71         }
 72 
 73         if((listenfd = socket_server_init(NULL, serv_port)) < 0)
 74         {
 75                 printf("ERROR:%s server listen on port%d failture\n",argv[0],serv_port);
 76                 return -2;
 77         }
 78         printf("%s progname start to on port %d\n", argv[0], serv_port);
 79 
 80         if(daemon_run)
 81         {
 82                 daemon(0,0);
 83         }
 84 
 85         for(i=0; i<ARRAY_SIZE(fds_array); i++)
 86         {
 87                 fds_array[i].fd = -1;
 88         }
 89         fds_array[0].fd = listenfd;
 90         fds_array[0].events = POLLIN;
 91 
 92         max = 0;
 93 
 94         for(;;)
 95         {
 96                 rv = poll(fds_array, max+1, -1);
 97                 if(rv<0)
 98                 {
 99                         printf("select failture:%s\n", strerror(errno));
100                         break;
101                 }
102                 else if(rv = 0)
103                 {
104                         printf("select get timeout");
105                         continue;
106                 }
107 
108                 if(fds_array[0].events & POLLIN)
109                 {
110                         if((connfd=accept(listenfd, (struct sockaddr *)NULL, NULL))<0)
111                         {
112                                 printf("accept new client failture:%s\n",
113                                                 strerror(errno));
114                                 continue;
115                         }
116                         found = 0;
117                         for(i = 1; i < ARRAY_SIZE(fds_array); i++)
118                         {
119                                 if(fds_array[i].fd<0)
120                                 {
121                                         printf("accept new client[%d]andadditintoarr\n"
122                                                         ,connfd);
123                                         fds_array[i].fd = connfd;
124                                         fds_array[i].events = POLLIN;
125                                         found = 1;
126                                         break;
127                                 }
128                         }
129 
130                         if(!found)
131                         {
132                                 printf("accept new client[%d] but full, so refuse it\n"
133                                                 ,connfd);
134                                 close(connfd);
135                                 continue;
136                         }
137 
138                         max = i > max ? i : max;
139                         if(--rv <= 0)
140                                 continue;
141                 }
142                 else
143                 {
144                         for(i=1; i<ARRAY_SIZE(fds_array); i++)
145                         {
146                                 if(fds_array[i].fd < 0)
147                                         continue;
148                                 if((rv=read(fds_array[i].fd, buf, sizeof(buf)))<=0)
149                                 {
150         printf("socket[%d] read failture or get disconncet.\n",fds_array[i].fd);
151                                         close(fds_array[i].fd);
152                                         fds_array[i].fd = -1;
153                                 }
154                                 else
155                                 {
156                                         printf("socket[%d] read get %d bytes data\n",
157                                                         fds_array[i].fd,rv);
158 
159                                         for(j=0; j<rv; j++)
160                                                 buf[j]=toupper(buf[j]);
161 
162                                         if(write(fds_array[i].fd, buf, rv)<0)
163                                         {
164                                                 printf("socket[%d] write failture:%s\n",
165                                 fds_array[i].fd,strerror(errno));
166                                                 close(fds_array[i].fd);
167                                                 fds_array[i].fd = -1;
168                                         }
169                                 }
170                         }
171                 }
172 
173         }
174 Cleanup:
175         close(listenfd);
176         return 0;
177 }
178 
179 static inline void print_usage(char *progname)
180 {
181         printf("Usage:%s [OPTION]...\n", progname);
182         //printf("%s is a socket server progname, which used to verify client and echo back string from it\n", progname);
183         return;
184 }
185 
186 int socket_server_init(char *listen_ip, int listen_port)
187 {
188         struct sockaddr_in          servaddr;
189         int                         rv=0;
190         int                         on=1;
191         int                         listenfd;
192 
193         if((listenfd = socket(AF_INET,SOCK_STREAM,0))<0)
194         {
195                 printf("Use socket() to create a TCP socket failture:%s\n", strerror(errno));
196                 return -1;
197         }
198 
199         setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
200 
201         memset(&servaddr, 0, sizeof(servaddr));
202         servaddr.sin_family = AF_INET;
203         servaddr.sin_port = htons(listen_port);
204 
205         if(!listen_ip)
206         {
207                 servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
208         }
209         else
210         {
211                 if(inet_pton(AF_INET, listen_ip,&servaddr.sin_addr)<=0)
212                 {
213                         printf("inet_pton() set listen IP address failture.\n");
214                         rv = -2;
215                         goto Cleanup;
216                 }
217         }
218 
219         if(bind(listenfd, (struct sockaddr *) &servaddr,sizeof(servaddr))<0)
220         {
221                 printf("Use bind() to bind the TCP socket failture:%s\n",strerror(errno));
222                 rv = -4;
223                 goto Cleanup;
224         }
225 
226 Cleanup:
227         if(rv<0)
228                 close(listenfd);
229         else
230                 rv= listenfd;
231 
232         return rv;
233 }

poll的流程图和select的流程图基本相同,这里就不做阐述。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值