linux网络编程之selectpollepoll

Posted 为了维护世界和平_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux网络编程之selectpollepoll相关的知识,希望对你有一定的参考价值。

目录

select

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

	nfds:监听的所有文件描述符中,最大文件描述符+1

	readfds: 读文件描述符监听集合。
	writefds:写文件描述符监听集合。
	exceptfds:异常文件描述符监听集合

	timeout: 	> 0: 	设置监听超时时长。
			NULL:	阻塞监听
			0:	非阻塞监听,轮询
	返回值:
		> 0:	所有监听集合(3个)中, 满足对应事件的总数。
		0:	没有满足监听条件的文件描述符
		-1: 	errno

select 模型

	int maxfd = 0;
	lfd = socket() ;			
	maxfd = lfd;
	bind();					
	listen();				
	fd_set rset, allset;
	FD_ZERO(&allset);			
	FD_SET(lfd, &allset);//将 lfd 添加至读集合中。
	
	while1
		rset = allset;//			保存监听集合
		ret  = select(lfd+1&rset, NULLNULLNULL);
		if(ret > 0							
			if (FD_ISSET(lfd, &rset)) 			// 1 在。 0不在。
				cfd = accept();				//建立连接,返回cfd
				maxfd = cfd;
				FD_SET(cfd, &allset);			//添加cfd到描述符集合中。
			

			for (i = lfd+1; i <= maxfd ; i++
				FD_ISSET(i, &rset)	//有read、write事件
				read()/write();
				
		
	

select源码实例

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <ctype.h>

#define SERV_PORT 6666

static void sys_error(const char *str)

	perror(str);
	exit(1);


int main(int argc, char *argv[])

    int i, j, n, maxi;

    int nready, client[FD_SETSIZE];                
    int maxfd, listenfd, connfd, sockfd;
    char buf[BUFSIZ], str[INET_ADDRSTRLEN];        

    struct sockaddr_in clie_addr, serv_addr;
    socklen_t clie_addr_len;
    fd_set rset, allset;                          

    listenfd = socket(AF_INET, SOCK_STREAM, 0);

    int opt = 1;
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    bzero(&serv_addr, sizeof(serv_addr));
    serv_addr.sin_family= AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port= htons(SERV_PORT);

    bind(listenfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
    listen(listenfd, 128);

    maxfd = listenfd;                                        
    maxi = -1;      
    for (i = 0; i < FD_SETSIZE; i++)
        client[i] = -1;   

    FD_ZERO(&allset);
    FD_SET(listenfd, &allset);                              

    while (1)    
        rset = allset;                                        
        nready = select(maxfd+1, &rset, NULL, NULL, NULL);  
        if (nready < 0)
            sys_error("select error");

        if (FD_ISSET(listenfd, &rset))                
            clie_addr_len = sizeof(clie_addr);
            connfd = accept(listenfd, (struct sockaddr *)&clie_addr, &clie_addr_len);       
            printf("received from %s at PORT %d\\n",
                    inet_ntop(AF_INET, &clie_addr.sin_addr, str, sizeof(str)),
                    ntohs(clie_addr.sin_port));

            for (i = 0; i < FD_SETSIZE; i++)
                if (client[i] < 0)                           
                    client[i] = connfd;                        
                    break;
                

            if (i == FD_SETSIZE)                             
                fputs("too many clients\\n", stderr);
                exit(1);
            

            FD_SET(connfd, &allset);                         
            if (connfd > maxfd)
                maxfd = connfd; 
            if (i > maxi)
                maxi = i;                      

            if (--nready == 0)
                continue;
         

        for (i = 0; i <= maxi; i++)     
            if ((sockfd = client[i]) < 0)
                continue;
            if (FD_ISSET(sockfd, &rset)) 

                if ((n = read(sockfd, buf, sizeof(buf))) == 0)   
                    close(sockfd);
                    FD_CLR(sockfd, &allset); 
                    client[i] = -1;
                 else if (n > 0) 
		            write(sockfd, buf, n);//将数据回写
                
                if (--nready == 0)
                    break;                                     
            
        
    
    close(listenfd);
    return 0;

poll

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

	    fds:监听的文件描述符【数组】
		struct pollfd 
			int fd:	待监听的文件描述符
			short events:	监听事件:POLLIN、POLLOUT、POLLERR
			short revnets:	传入时, 给0。如果满足对应事件的话, 返回 非0 --> POLLIN、POLLOUT、POLLERR
		

	nfds: 监听数组的,实际有效监听个数。
	timeout:  > 0:  超时时长。单位:毫秒。
		  -1:	阻塞等待
		  0:  不阻塞
	返回值:返回满足对应监听事件的文件描述符 总个数。

poll实例源码

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <errno.h>
#include <ctype.h>
#include<poll.h>

#define MAXLINE 8192
#define SERV_PORT 7777 

#define OPEN_MAX 5000

static void sys_error(const char *str)

	perror(str);
	exit(1);


int main(int argc, char *argv[])

    int i, maxi,listenfd, connfd, sockfd;
    int  n, num = 0;
    ssize_t nready, efd, res;
    char buf[MAXLINE], str[INET_ADDRSTRLEN];
    socklen_t clilen;

    struct sockaddr_in cliaddr, servaddr;
    struct pollfd client[OPEN_MAX];

    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    int opt = 1;
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));     
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERV_PORT);
    bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr));
    listen(listenfd, 128);

    client[0].fd = listenfd;
    client[0].events = POLLIN;

    for(i = 1;i<OPEN_MAX;i++)
    
	    client[i].fd = -1;
    

    maxi = 0;
    for(;;)
	nready = poll(client,maxi+1,-1);
	if(client[0].revents & POLLIN)
	
		clilen = sizeof(cliaddr);
		connfd=accept(listenfd,(struct sockaddr *)&cliaddr,&clilen);

                printf("received from %s at PORT %d\\n", 
                        inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)), 
                        ntohs(cliaddr.sin_port));

		for(i = 1;i<OPEN_MAX;i++)
		
			if(client[i].fd < 0)
				client[i].fd = connfd;
				break;
			
		

		if(i == OPEN_MAX)
			sys_error("too many clients");
		client[i].events = POLLIN;
		
		if(i > maxi)
			maxi = i;
		if(--nready <=0)
			continue;
	

	for(i = 1;i<=maxi;i++)
	
		if((sockfd = client[i].fd) < 0)
			continue;
		if(client[i].revents & POLLIN)
			if((n = read(sockfd,buf,MAXLINE))<0)
				if(errno == ECONNRESET)
					printf("client[%d] abort connection\\n",i);
					close(sockfd);
					client[i].fd = -1;
				else
					sys_error("read error");
			
			else if(n == 0)
			
				printf("client[%d] closed connection\\n",i);
				close(sockfd);
				client[i].fd = -1;
			
			else
			
				printf("buf=%s\\n",buf);
				write(sockfd,buf,n);
			
			if(--nready <=0)
				break;
		
	

    
    return 0;


epoll

epoll 事件触发方式

ET模式:
	边沿触发:缓冲区剩余未读尽的数据不会导致 epoll_wait 返回。 只有新的事件,才会触发。
    struct epoll_event event;
	event.events = EPOLLIN | EPOLLET;
LT模式:
	水平触发 -- 默认采用模式。
	缓冲区剩余未读尽的数据会导致 epoll_wait 返回。

epoll多路IO程序结构

lfd = socket();	
bind();
listen();
int epfd = epoll_create(1024);				//epfd, 监听红黑树的树根。
struct epoll_event tep, ep[1024];			//tep, 用来设置单个fd属性, ep 是 epoll_wait() 传出的满足监听事件的数组。

tep.events = EPOLLIN;					//初始化  lfd的监听属性。
tep.data.fd = lfd
epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &tep);		//将 lfd 添加到监听红黑树上。
while (1) 
	ret = epoll_wait(epfd, ep,1024, -1);			//实施监听
	for (i = 0; i < ret; i++) 
		if (ep[i].data.fd == lfd) 				// lfd 满足读事件,有新的客户端发起连接请求
			cfd = accept();
			tep.events = EPOLLIN;				//初始化  cfd的监听属性。
			tep.data.fd = cfd;
			epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &tep);
		 else 						//满足读事件, 有客户端写数据来。
			n = read(ep[i].data.fd, buf, sizeof(buf));
			if ( n == 0) 
				close(ep[i].data.fd);
				epoll_ctl(epfd, EPOLL_CTL_DEL, ep[i].data.fd , NULL);	// 将关闭的cfd,从监听树上摘下。
			 else if (n > 0) 
				write(ep[i].data.fd, buf, n);
			
		
	


epoll 实例源码

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <errno.h>
#include <ctype.h>

#define MAXLINE 8192
#define SERV_PORT 6666 
#define OPEN_MAX 5000

static void sys_error(const char *str)

	perror(str);
	exit(1);


int main(int argc, char *argv[])

    int i, listenfd, connfd, sockfd;
    int  n, num = 0;
    ssize_t nready, efd, res;
    char buf[MAXLINE], str[INET_ADDRSTRLEN];
    socklen_t clilen;

    struct sockaddr_in cliaddr, servaddr;

    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    int opt = 1;
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));     
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERV_PORT);
    bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr));
    listen(listenfd, 20);

    efd = epoll_create(OPEN_MAX);  
    if (efd == -1)
        sys_error("epoll_create error");

    struct epoll_event tep, ep[OPEN_MAX];//tep: epoll_ctl参数  ep[] :epoll_wait参数

    tep.events = EPOLLIN; 
    tep.data.fd = listenfd;           //指定lfd的监听"读"事件

    res = epoll_ctl(efd, EPOLL_CTL_ADD, listenfd, &tep);
    if (res == -1)
        sys_error("epoll_ctl error");

    while(1)
        /*epoll为server阻塞监听事件, ep为struct epoll_event类型数组, -1表永久阻塞*/
        nready = epoll_wait(efd, ep, OPEN_MAX, -1); 
        if (nready == -1)
            sys_error("epoll_wait error");

        for (i = 0; i < nready; i++) 
            if (!(ep[i].events & EPOLLIN)) 
                continue;

            if (ep[i].data.fd == listenfd)            
                clilen = sizeof(cliaddr);
                connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen); 
                printf("received from %s at PORT %d\\n", 
                        inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)), 
                        ntohs(cliaddr.sin_port));
                printf("cfd %d---client %d\\n", connfd, ++num);

                tep.events = EPOLLIN; tep.data.fd = connfd;
                res = epoll_ctl(efd, EPOLL_CTL_ADD, connfd, &tep); //connfd事件
                if (res == -1)
                    sys_error("epoll_ctl error");

             else                                            

以上是关于linux网络编程之selectpollepoll的主要内容,如果未能解决你的问题,请参考以下文章

#导入Word文档图片# Linux下IO多路复用: Selectpollepoll

linux基础编程—selectpollepoll

SOCKET编程之一个端口如何建立多个TCP连接?(用fork子进程selectpollepoll都是可以的)一个端口最大支持建立多少个tcp连接?

IO多路复用之selectpollepoll

I/O多路复用之selectpollepoll

Linux IO模式及 selectpollepoll详解