TCP协议+IO多路复用 --- select,poll,epoll分析
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
- 点赞
- 收藏
- 关注作者
评论(0)