TCP协议+IO多路复用 --- select,poll,epoll分析

举报
王建峰 发表于 2021/11/19 03:22:13 2021/11/19
【摘要】   select、poll、epoll select,poll,epoll都是IO多路复用的机制。I/O多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件...

 

select、poll、epoll

select,poll,epoll都是IO多路复用的机制。I/O多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。

 

1.select

select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点。select的一 个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,可以通过修改宏定义甚至重新编译内核的方式提升这一限制,但 是这样也会造成效率的降低。

2.poll

不同与select使用三个位图来表示三个fdset的方式,poll使用一个 pollfd的指针实现。pollfd结构包含了要监视的event和发生的event,不再使用select“参数-值”传递的方式。同时,pollfd并没有最大数量限制(但是数量过大后性能也是会下降)。 和select函数一样,poll返回后,需要轮询pollfd来获取就绪的描述符。

3.epoll

epoll是在2.6内核中提出的,是之前的select和poll的增强版本。相对于select和poll来说,epoll更加灵活,没有描述符限制。epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次。

参考来源:

Linux中select poll和epoll的区别
IO多路复用之select总结
IO多路复用之poll总结
IO多路复用之epoll总结


下面关于三种方法的C语言实例。

 

客户端代码采用多路复用的思路实现。


      //客户端代码,可分别实现三种多路复用的形式 select、poll、epoll ;
      //通过宏来实现
      #include <stdio.h>
      #include <string.h>
      #include <unistd.h>
      #include <errno.h>
      #include <stdlib.h>
      #include <strings.h>
      #include <sys/types.h> /* See NOTES */
      #include <sys/socket.h>
      #include <netinet/in.h>
      #include <netinet/ip.h> /* superset of previous */
      #include <poll.h>
      #include <sys/epoll.h>
      #define SERV_PORT 5001
      #define SERV_IP_ADDR "127.0.0.1"
      #define BACKLOG 5
      #define QUIT_STR "quit"
      #define SELECT 0
      #define POLL 0
      #define EPOLL 1
      void handle_select(int fd);
      void handle_poll(int fd);
      void handle_epoll(int fd);
      void add_event(int epollfd,int fd,int state);
      void handle_events(int epollfd,struct epoll_event *events,int num,int sockfd,char *buf);
      int main(int argc, const char *argv[])
      {
     	int fd;
     	struct sockaddr_in sin;
     	bzero(&sin,sizeof(sin));
      	sin.sin_family = AF_INET;
     	inet_pton(AF_INET,SERV_IP_ADDR,&sin.sin_addr);
      	sin.sin_port = htons(SERV_PORT);
     	if( -1 == (fd = socket(AF_INET,SOCK_STREAM,0)) )
      	{
     		perror("socket failed");
     		return -1;
      	}
     	if( -1 == connect(fd,(struct sockaddr*)&sin,sizeof(sin)) )
      	{
     		perror("connect failed");
     		return -1;
      	}
      #if SELECT
     	handle_select(fd);
      #endif
      #if POLL
     	handle_poll(fd);
      #endif
      #if EPOLL
     	handle_epoll(fd);
      #endif
     	close(fd);
     	return 0;
      }
      void handle_select(int fd)
      {
     	int real_read;
     	char buf[64];
      	fd_set fdset;
     	int maxfd = -1;
     	struct timeval tout;
     	while(1)
      	{
     		FD_ZERO(&fdset);
     		FD_SET(0,&fdset);
     		FD_SET(fd,&fdset);
      		maxfd = fd;
      		tout.tv_sec = 5;
      		tout.tv_usec = 0;
     		select(maxfd+1,&fdset,NULL,NULL,&tout);
     		if( FD_ISSET(0,&fdset) )
      		{
     			bzero(buf,sizeof(buf));
     			do{
      				real_read = read(0,buf,sizeof(buf));
      			}while( real_read<0 && EINTR == errno );
     			if(real_read < 0)
      			{
     				perror("read");
     				continue;
      			}
     			else if( 0 == real_read )
      			{
     				continue;
      			}
     			write(fd,buf,strlen(buf)-1);
     			if( 0 == (strncasecmp(buf,QUIT_STR,strlen(QUIT_STR))) )
      			{
     				printf("client quit\n");
     				break;
      			}
      		}
     		if( FD_ISSET(fd,&fdset) )
      		{
     			bzero(buf,sizeof(buf));
     			do{
      				real_read = read(fd,buf,sizeof(buf));
      			}while( real_read<0 && EINTR == errno );
     			if(real_read < 0)
      			{
     				perror("read");
     				continue;
      			}
     			else if( 0 == real_read )
      			{
     				break;
      			}
     			printf("Received: %s\n",buf);
     			if( 0 == (strncasecmp(buf,QUIT_STR,strlen(QUIT_STR))) )
      			{
     				printf("client quit\n");
     				break;
      			}
      		}
      	}
      }
      void handle_poll(int fd)
      {
     	char buf[64];
     	int maxfdp;
     	struct pollfd pfds[2];
     	int real_read;
      	pfds[0].fd = fd;
      	pfds[0].events = POLLIN;
      	pfds[1].fd = 0;
      	pfds[1].events = POLLIN;
      	maxfdp = 2;
     	while(1)
      	{
     		poll(pfds,maxfdp,5);
     		if (pfds[0].revents & POLLIN)
      		{
     			bzero(buf,sizeof(buf));
     			do{
      				real_read = read(fd,buf,sizeof(buf));
      			}while( real_read<0 && EINTR == errno );
     			if(real_read < 0)
      			{
     				perror("read");
     				continue;
      			}
     			else if( 0 == real_read )
      			{
     				break;
      			}
     			printf("Received: %s\n",buf);
     			if( 0 == (strncasecmp(buf,QUIT_STR,strlen(QUIT_STR))) )
      			{
     				printf("client quit\n");
     				break;
      			}
      		}
     		if (pfds[1].revents & POLLIN)
      		{
     			bzero(buf,sizeof(buf));
     			do{
      				real_read = read(0,buf,sizeof(buf));
      			}while( real_read<0 && EINTR == errno );
     			if(real_read < 0)
      			{
     				perror("read");
     				continue;
      			}
     			else if( 0 == real_read )
      			{
     				continue;
      			}
     			write(fd,buf,strlen(buf)-1);
     			if( 0 == (strncasecmp(buf,QUIT_STR,strlen(QUIT_STR))) )
      			{
     				printf("client quit\n");
     				break;
      			}
      		}
      	}
      }
      void handle_epoll(int sockfd)
      {
     	int epollfd;
     	struct epoll_event events[100];
     	char buf[64];
     	int ret;
      	epollfd = epoll_create(1000);
     	add_event(epollfd,0,EPOLLIN);
     	add_event(epollfd,sockfd,EPOLLIN);
     	while(1)
      	{
      		ret = epoll_wait(epollfd,events,100,5);
     		handle_events(epollfd,events,ret,sockfd,buf);
      	}
     	close(epollfd);
      }
      void add_event(int epollfd,int fd,int state)
      {
     	struct epoll_event ev;
      	ev.events = state;
      	ev.data.fd = fd;
     	epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&ev);
      }
      void
      handle_events(int epollfd,struct epoll_event *events,int num,int sockfd,char *buf)
      {
     	int fd;
     	int i;
     	int real_read;
     	for (i = 0;i < num;i++)
      	{
      		fd = events[i].data.fd;
     		if (events[i].events & EPOLLIN)
      		{
     			if( fd == sockfd )
      			{//判断就绪fd为连接套接字sockfd
     				bzero(buf,64);
     				do{
      					real_read = read(sockfd,buf,64);
      				}while( real_read<0 && EINTR == errno );
     				if(real_read < 0)
      				{
     					perror("read");
     					continue;
      				}
     				else if( 0 == real_read )
      				{
     					break;
      				}
     				printf("Received: %s\n",buf);
     				if( 0 == (strncasecmp(buf,QUIT_STR,strlen(QUIT_STR))) )
      				{
     					printf("client quit\n");
     					break;
      				}
      			}
     			else if( 0 == fd )
      			{//那么判断就绪fd 为标准输入
     				bzero(buf,64);
     				do{
      					real_read = read(0,buf,64);
      				}while( real_read<0 && EINTR == errno );
     				if(real_read < 0)
      				{
     					perror("read");
     					continue;
      				}
     				else if( 0 == real_read )
      				{
     					continue;
      				}
     				write(sockfd,buf,strlen(buf)-1);
     				if( 0 == (strncasecmp(buf,QUIT_STR,strlen(QUIT_STR))) )
      				{
     					printf("client quit\n");
     					break;
      				}
      			}
      		}
      	}
      }
  
 

 

服务端使用多线程的思想


      //循环里处理主监听套接字listenfd,accept监听请求连接。然后将每一个accept连接成功 返回的连接套接
      //字connf分配给一个线程处理。。。
      #include <stdio.h>
      #include <string.h>
      #include <unistd.h>
      #include <errno.h>
      #include <stdlib.h>
      #include <strings.h>
      #include <sys/types.h> /* See NOTES */
      #include <sys/socket.h>
      #include <netinet/in.h>
      #include <netinet/ip.h> /* superset of previous */
      #include <poll.h>
      #include <sys/epoll.h>
      #define SERV_PORT 5001
      #define SERV_IP_ADDR "127.0.0.1"
      #define BACKLOG 5
      #define QUIT_STR "quit"
      void *cli_data_handle(void* arg);
      int main(int argc, const char *argv[])
      {
     	int listenfd,connfd;
     	struct sockaddr_in servaddr,cliaddr;
     	socklen_t peerlen;
     	char buf[64];
     	pthread_t thread;
     	bzero(&servaddr,sizeof(servaddr));
      	servaddr.sin_family = AF_INET;
     	inet_pton(AF_INET,SERV_IP_ADDR,(void *)&servaddr.sin_addr);
      	servaddr.sin_port = htons(SERV_PORT);
     	if( -1 == (listenfd = socket(AF_INET,SOCK_STREAM,0)) )
      	{
     		perror("socket failed");
     		return -1;
      	}
     	printf("socket create success....\n");
     	int b_reuse = 1;
     	setsockopt (listenfd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof (int));
     	if(-1 == bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr)) )
      	{
     		perror("bind failed");
     		return -1;
      	}
     	printf("bind success....\n");
     	if( -1 == listen(listenfd,10) )
      	{
     		perror("listen failed");
     		return -1;
      	}
     	printf("listen success....\n");
     	while(1)
      	{
     		bzero(&cliaddr,sizeof(cliaddr));
      		peerlen = sizeof(cliaddr);
     		if( (connfd = accept(listenfd,(struct sockaddr *)&cliaddr,&peerlen)) < 0)
      		{
     			perror("accept");
     			return -1;
      		}
     		if( 0 != pthread_create(&thread,NULL,cli_data_handle,(void *)&connfd) )
      		{
     			perror("pthread_create");
     			return -1;
      		}
      	}
     	close(listenfd);
     	return 0;
      }
      void *cli_data_handle(void* arg)
      {
     	char buf[64];
     	int connfd = *(int *)arg;
     	int real_read =0;
     	while(1)
      	{
     			bzero(buf,sizeof(buf));
     			do{
      				real_read = read(connfd,buf,sizeof(buf));
      			}while( real_read<0 && EINTR == errno );
     			if(real_read < 0)
      			{
     				perror("read");
     				continue;
      			}
     			else if( 0 == real_read )
      			{
     				break;
      			}
     			printf("Received:%s\n",buf);
     			write(connfd,buf,strlen(buf));
     			if( 0 == (strncasecmp(buf,QUIT_STR,strlen(QUIT_STR))) )
      			{
     				printf("client quit\n");
     				break;
      			}
      	}
     	close(connfd);
      }
  
 

 

结果验证

文章来源: blog.csdn.net,作者:hinzer,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/feit2417/article/details/82528105

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。