网络IO复用select/poll/epoll

1.select

(1).函数原型:

        int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout); 

(2).返回值:返回客户端连接的数量

(3).参数:nfds:最大的fd号,客户端fd会从4开始,0为stdin, 1为stdout, 2为stderr, 3为listen;

                readfds:可读端口的集合, fd_set为长度1024的bit位;

                writefds:可写端口的集合;

                exceptfds:出错端口的集合;

                timeout:每次检测的时间间隔。

(4).4个FD函数:

                FD_ZERO(fd_set* a):将a置0;

                FD_SET(index, fd_set* a):将a中index位置1;

                FD_CLR(index, fd_set* a):将a中index位置0;

                FD_ISSET(index, fd_set* a): 判断a中index位是否为1;

(5).代码     

#include<stdio.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <unistd.h>
#include <sys/select.h>


int main()
{
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	
	struct sockaddr_in servaddr;
	
	memset(&servaddr, 0, sizeof(struct sockaddr_in));
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);  //0.0.0.0
	servaddr.sin_port = htons(2048);
	
	if(-1 == bind(sockfd, (struct sockaddr*)&servaddr, sizeof(struct sockaddr)))
	{
		perror("bind failed!");
	}
	
	listen(sockfd, 10);   
	
	fd_set nfds, rset;
	FD_ZERO(&nfds);
	FD_SET(sockfd, &nfds);
	
	int maxfd = sockfd; 
	int clientfd = 0;
	struct sockaddr_in clientaddr;
	socklen_t len = sizeof(clientaddr);
	
	while(1)
	{
		rset = nfds;
		int nready = select(maxfd+1, &rset, NULL, NULL, NULL);
		
		if(FD_ISSET(sockfd, &rset))
		{
			clientfd = accept(sockfd, (struct sockaddr*)&clientaddr, &len);
			
			FD_SET(clientfd, &nfds);
			
			if(clientfd > maxfd)
				maxfd = clientfd;
			
			printf("clientfd: %d\n", clientfd);
		}
		
		int i = 0;
		for(i = sockfd+1; i <= maxfd; i++)
		{
			if(FD_ISSET(i, &rset))
			{
				char buf[128] = {0};
				int count = recv(i, buf, 128, 0);
				if(count == 0)
				{
					FD_CLR(i, &nfds);
					close(i);
					break;
				}
				printf("clientfd: %d, count: %d, recv: %s\n", i, count, buf);
				send(i, buf, count, 0);
			}
			
		}
	}
	
	
	getchar();
	close(sockfd);
}

(6).总结:

        <1>.参数太多;<2>.每次都要将待检测的io拷贝进内核;<3>.fd_set 决定了io数量有限

2.poll

(1).函数:

        int poll(struct pollfd *fds, nfds_t nfds, int timeout);
(2).返回值:返回连接的io数量, 超时返回0;

(3).fds:存储连接的客户端, nfds最大的io的fd

(4).struct pollfd结构体:包含三个成员,fd、events、revents,fd和events在poll函数签传入fds,revents为返回事件,可根据代码理解。

(5).代码:

#include<stdio.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <unistd.h>
#include <sys/select.h>
#include <poll.h>



int main()
{
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	
	struct sockaddr_in servaddr;
	
	memset(&servaddr, 0, sizeof(struct sockaddr_in));
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);  //0.0.0.0
	servaddr.sin_port = htons(2048);
	
	if(-1 == bind(sockfd, (struct sockaddr*)&servaddr, sizeof(struct sockaddr)))
	{
		perror("bind failed!");
		printf("bind failed!");
	}
	
	listen(sockfd, 10);   
	
	struct sockaddr_in* clientaddr;
	socklen_t len = sizeof(clientaddr);
	
	printf("111\n");
	
	struct pollfd fds[1024] = {0};
	fds[sockfd].fd = sockfd;
	fds[sockfd].events = POLLIN;
	
	int maxfd = sockfd;
	int clientfd = 0;
	
	while(1)
	{
		int nready = poll(fds, maxfd+1, -1);
		printf("nready:%d\n", nready);
		if(fds[sockfd].revents & POLLIN)
		{		 
			clientfd = accept(sockfd, (struct sockaddr*)&clientaddr, &len);

			fds[clientfd].fd = clientfd;
			fds[clientfd].events = POLLIN;

			if(maxfd < clientfd)
				maxfd = clientfd;	
		}
		

		int i = 0;
		for (i = sockfd+1;i <= maxfd;i ++) {

			if (fds[i].revents & POLLIN) {

				char buffer[128] = {0};
				int ret = recv(i, buffer, 128, 0);
				if (ret == 0) {
					fds[i].fd = -1;
					fds[i].events = 0;
					
					close(i);
					break; 
				}

				printf("clientfd: %d, ret: %d, buffer: %s\n",clientfd, ret, buffer);

				send(i, buffer, ret, 0);

			}

		}
	}
	
	
	getchar();
	close(sockfd);
}

总结:poll和select并无太多差异,只是在参数上有所改善。

3.epoll

(1).三个API

        int epoll_create(1)

        创建一个epoll,参数大于0即可,没有实际意义;

       int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

       将fd加入到epoll或者从epoll中删除fd;

        第一个参数为epfd;op为控制fd的模式,添加或者删除;event为当前fd对应的事件
       int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

       等待就绪的fd,第一个参数为epfd; 第二个参数为epoll对应的所有events;第三个参数是可供连接的最大fd;最后一个参数为耗时操作,-1就是一直等待。

	int epfd = epoll_create(1);
	
	struct epoll_event ev;
	ev.events = EPOLLIN;
	ev.data.fd = sockfd;
	
	if(epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev) == -1)
	{
		perror("epoll_ctl: listen_sock");
	}
	
	struct epoll_event events[1024] = {0};
	
	
	while(1)
	{
		int nready = epoll_wait(epfd, events, 1024, -1);
		
		int i = 0;
		for(i = 0; i < nready; i++)
		{
			
			int connfd = events[i].data.fd;
			if(sockfd == connfd)
			{
				struct sockaddr_in clientaddr;
				socklen_t len = sizeof(clientaddr);
				
				int clientfd = accept(sockfd, (struct sockaddr*)&clientaddr, &len);
				if(clientfd<=0)
				{
					continue;
				}
				printf(" clientfd: %d\n", clientfd);
				//printf(" clientfd: %d\n", clientfd);
				ev.events = EPOLLIN | EPOLLET;
				ev.data.fd = clientfd;
				epoll_ctl(epfd, EPOLL_CTL_ADD, clientfd, &ev);
						
			}
			else if(events[i].events & EPOLLIN)
			{
				char buf[10] = {0};
				short len = 0;
				recv(connfd, &len, 2, 0);
				len = ntohs(len);
				
				int n = recv(connfd, buf, 10, 0);
				if(n == 0)
				{
					epoll_ctl(epfd, EPOLL_CTL_DEL, connfd, NULL);
					close(connfd);
				}
				else
				{
					send(connfd, buf, n, 0);
					printf("recv : %s\n", buf);
				}
				
			}
			
		}
		
	}

        

 分享一个学习链接,有需要的同学可以看一下:

https://xxetb.xetslk.com/s/3yNycZ

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值