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 添加至读集合中。
while(1)
rset = allset;// 保存监听集合
ret = select(lfd+1, &rset, NULL, NULL, NULL);
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
SOCKET编程之一个端口如何建立多个TCP连接?(用fork子进程selectpollepoll都是可以的)一个端口最大支持建立多少个tcp连接?