参考《linux高性能服务器编程》

LINUX下处理多个连接时候,仅仅使用多线程和原始socket函数,效率十分低下

于是就出现了selelct poll  epoll等IO复用函数。

这里讨论性能最优的epoll IO复用

用户将需要关注的socket连接使用IO复用函数放进一个事件表中,每当事件表中有一个或者多个SOCKET连接出现读写请求时候,则进行处理

事件表使用一个额外的文件描述符来标识。文件描述符使用 epoll_create函数创建

#inlclude <sys/epoll.h>

int epoll_create(int size); size参数目前不起作用

操作事件表使用epoll_ctl函数进行控制

操作类型有以下3中

EPOLL_CTL_ADD  EPOLL_CTL_MOD  EPOLL_CTL_DEL

在一段超时时间内等待事件表上的事件 使用epoll_wait();

函数范例如下:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <pthread.h> #define MAX_EVENT_NUMBER 1024
#define BUFFER_SIZE 10 int setnonblocking( int fd )
{
int old_option = fcntl( fd, F_GETFL );
int new_option = old_option | O_NONBLOCK;
fcntl( fd, F_SETFL, new_option );
return old_option;
} void addfd( int epollfd, int fd, bool enable_et )
{
epoll_event event;
event.data.fd = fd;
event.events = EPOLLIN;
if( enable_et )
{
event.events |= EPOLLET;
}
epoll_ctl( epollfd, EPOLL_CTL_ADD, fd, &event );
setnonblocking( fd );
} void et( epoll_event* events, int number, int epollfd, int listenfd )
{
char buf[ BUFFER_SIZE ];
for ( int i = 0; i < number; i++ )
{
int sockfd = events[i].data.fd;
if ( sockfd == listenfd )
{
struct sockaddr_in client_address;
socklen_t client_addrlength = sizeof( client_address );
int connfd = accept( listenfd, ( struct sockaddr* )&client_address, &client_addrlength );
addfd( epollfd, connfd, true );
}
else if ( events[i].events & EPOLLIN )
{
printf( "event trigger once\n" );
while( 1 )
{
memset( buf, '\0', BUFFER_SIZE );
int ret = recv( sockfd, buf, BUFFER_SIZE-1, 0 );
if( ret < 0 )
{
if( ( errno == EAGAIN ) || ( errno == EWOULDBLOCK ) )
{
printf( "read later\n" );
break;
}
close( sockfd );
break;
}
else if( ret == 0 )
{
close( sockfd );
}
else
{
printf( "get %d bytes of content: %s\n", ret, buf );
}
}
}
else
{
printf( "something else happened \n" );
}
}
} int main( int argc, char* argv[] )
{
if( argc <= 2 )
{
printf( "usage: %s ip_address port_number\n", basename( argv[0] ) );
return 1;
}
const char* ip = argv[1];
int port = atoi( argv[2] ); int ret = 0;
struct sockaddr_in address;
bzero( &address, sizeof( address ) );
address.sin_family = AF_INET;
inet_pton( AF_INET, ip, &address.sin_addr );
address.sin_port = htons( port ); int listenfd = socket( PF_INET, SOCK_STREAM, 0 );
assert( listenfd >= 0 ); ret = bind( listenfd, ( struct sockaddr* )&address, sizeof( address ) );
assert( ret != -1 ); ret = listen( listenfd, 5 );
assert( ret != -1 ); epoll_event events[ MAX_EVENT_NUMBER ];
int epollfd = epoll_create( 5 );
assert( epollfd != -1 );
addfd( epollfd, listenfd, true ); while( 1 )
{
int ret = epoll_wait( epollfd, events, MAX_EVENT_NUMBER, -1 );
if ( ret < 0 )
{
printf( "epoll failure\n" );
break;
} et( events, ret, epollfd, listenfd );
} close( listenfd );
return 0;
}

  

在尝试一个多线程代码;

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <pthread.h> #define MAX_EVENT_NUMBER 1024
#define BUFFER_SIZE 10 struct fds
{
int epollfd;
int sockfd;
}; void* worker( void* arg )
{
int sockfd = ( (fds*)arg )->sockfd;
int epollfd = ( (fds*)arg )->epollfd;
printf( "start new thread to receive data on fd: %d\n", sockfd );
char buf[ BUFFER_SIZE ];
memset( buf, '\0', BUFFER_SIZE );
while( 1 )
{
int ret = recv( sockfd, buf, BUFFER_SIZE-1, 0 );
if( ret == 0 )
{
close( sockfd );
printf( "foreiner closed the connection\n" );
break;
}
else if( ret < 0 )
{
if( errno == EAGAIN )
{
// reset_oneshot( epollfd, sockfd );
printf( "read later\n" );
break;
}
}
else
{
printf( "get content: %s\n", buf );
sleep( 5 );
}
}
printf( "end thread receiving data on fd: %d\n", sockfd );
} int setnonblocking(int fd)
{
int old_option = fcntl(fd,F_GETFL);
int new_option = old_option | O_NONBLOCK;
fcntl(fd,F_SETFL,new_option);
return old_option;
} void addfd(int epollfd,int fd,bool enable_et)
{
epoll_event event;
event.data.fd = fd;
event.events = EPOLLIN; if(enable_et){
event.events |= EPOLLET;
}
epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event);
setnonblocking(fd);
} void et(epoll_event* events,int number,int epollfd,int listenfd){
char buf[BUFFER_SIZE];
for(int i = 0; i < number;i++){
int sockfd = events[i].data.fd;
if(sockfd == listenfd){
struct sockaddr_in client_address;
socklen_t client_addrlength = sizeof(client_address);
int connfd = accept(listenfd,(struct sockaddr*)&client_address,&client_addrlength);
addfd(epollfd,connfd,true);
}else if(events[i].events & EPOLLIN){
/*
printf("event trigger once\n");
while(1){
memset(buf,'\0',BUFFER_SIZE);
int ret = recv(sockfd,buf,BUFFER_SIZE-1,0);
if(ret < 0){
if((errno == EAGAIN) || (errno == EWOULDBLOCK)){
printf("read later\n");
break;
}
close(sockfd);
break;
}else if(ret == 0){
close(sockfd);
}else{
printf("get %d bytes of content: %s \n",ret,buf);
}
}*/
pthread_t thread;
fds fds_for_new_worker;
fds_for_new_worker.epollfd = epollfd;
fds_for_new_worker.sockfd = sockfd;
pthread_create( &thread, NULL, worker, ( void* )&fds_for_new_worker );
}else{
printf("something else happened \n");
}
}
} int main()
{
int ret = 0;
struct sockaddr_in address;
bzero(&address,sizeof(address));
address.sin_family = AF_INET;
inet_pton(AF_INET,"127.0.0.1",&address.sin_addr);
address.sin_port = htons(1134); int listenfd = socket(AF_INET,SOCK_STREAM,0);
assert(listenfd >= 0); ret = bind(listenfd,(struct sockaddr*)&address,sizeof(address));
assert(ret != -1); ret = listen(listenfd,5);
assert(ret != -1); epoll_event events[MAX_EVENT_NUMBER];
int epollfd = epoll_create(5);
assert(epollfd != -1);
addfd(epollfd,listenfd,true); while(1){
int ret = epoll_wait(epollfd,events,MAX_EVENT_NUMBER,-1);
if(ret < 0){
printf("epoll failure\n");
break;
}
et(events,ret,epollfd,listenfd);
} close(listenfd);
return 0;
}

  

多线程版本中 如果我们 连续多次输入会出现一个问题

如果我们连续多次在telnet的客户端输入

服务端会开启多个线程 接受这个socket的输入

显示如下:

fd: 5, sockfdget content: dfdsf
...
start new thread to receive data on fd: 5
fd: 5, sockfdget content: sd
...
start new thread to receive data on fd: 5
fd: 5, sockfdget content: sd
...
start new thread to receive data on fd: 5
fd: 5, sockfdget content: sd
...
start new thread to receive data on fd: 5
fd: 5, sockfdget content: sd
...
start new thread to receive data on fd: 5
fd: 5, sockfdget content: sd
...
start new thread to receive data on fd: 5
fd: 5, sockfdget content: sd

如果多个线程同时操作一个socket 可能出现各种意外情况

那么我们就需要设置socket的EPOLLONESHOT标志

对于连续操作,操作系统最多触发该socket上一次读写异常事件。

代码如下:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <pthread.h> #define MAX_EVENT_NUMBER 1024
#define BUFFER_SIZE 10 struct fds
{
int epollfd;
int sockfd;
}; void reset_oneshot( int epollfd, int fd )
{
epoll_event event;
event.data.fd = fd;
event.events = EPOLLIN | EPOLLET | EPOLLONESHOT;
epoll_ctl( epollfd, EPOLL_CTL_MOD, fd, &event );
} void* worker( void* arg )
{
int sockfd = ( (fds*)arg )->sockfd;
int epollfd = ( (fds*)arg )->epollfd;
printf( "start new thread to receive data on fd: %d\n", sockfd );
char buf[ BUFFER_SIZE ];
memset( buf, '\0', BUFFER_SIZE );
while( 1 )
{
int ret = recv( sockfd, buf, BUFFER_SIZE-1, 0 );
if( ret == 0 )
{
close( sockfd );
printf( "foreiner closed the connection\n" );
break;
}
else if( ret < 0 )
{
if( errno == EAGAIN )
{
reset_oneshot( epollfd, sockfd );
printf( "read later\n" );
break;
}
}
else
{
printf( "get content: %s\n", buf );
sleep( 5 );
}
}
printf( "end thread receiving data on fd: %d\n", sockfd );
} int setnonblocking(int fd)
{
int old_option = fcntl(fd,F_GETFL);
int new_option = old_option | O_NONBLOCK;
fcntl(fd,F_SETFL,new_option);
return old_option;
} void addfd(int epollfd,int fd,bool setOneShot)
{
epoll_event event;
event.data.fd = fd;
event.events = EPOLLIN; event.events |= EPOLLET;
if(setOneShot)
event.events |= EPOLLONESHOT;
epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event);
setnonblocking(fd);
} void et(epoll_event* events,int number,int epollfd,int listenfd){
char buf[BUFFER_SIZE];
for(int i = 0; i < number;i++){
int sockfd = events[i].data.fd;
if(sockfd == listenfd){
struct sockaddr_in client_address;
socklen_t client_addrlength = sizeof(client_address);
int connfd = accept(listenfd,(struct sockaddr*)&client_address,&client_addrlength);
addfd(epollfd,connfd,true);
}else if(events[i].events & EPOLLIN){
/*
printf("event trigger once\n");
while(1){
memset(buf,'\0',BUFFER_SIZE);
int ret = recv(sockfd,buf,BUFFER_SIZE-1,0);
if(ret < 0){
if((errno == EAGAIN) || (errno == EWOULDBLOCK)){
printf("read later\n");
break;
}
close(sockfd);
break;
}else if(ret == 0){
close(sockfd);
}else{
printf("get %d bytes of content: %s \n",ret,buf);
}
}*/
pthread_t thread;
fds fds_for_new_worker;
fds_for_new_worker.epollfd = epollfd;
fds_for_new_worker.sockfd = sockfd;
pthread_create( &thread, NULL, worker, ( void* )&fds_for_new_worker );
}else{
printf("something else happened \n");
}
}
} int main()
{
int ret = 0;
struct sockaddr_in address;
bzero(&address,sizeof(address));
address.sin_family = AF_INET;
inet_pton(AF_INET,"127.0.0.1",&address.sin_addr);
address.sin_port = htons(1134); int listenfd = socket(AF_INET,SOCK_STREAM,0);
assert(listenfd >= 0); ret = bind(listenfd,(struct sockaddr*)&address,sizeof(address));
assert(ret != -1); ret = listen(listenfd,5);
assert(ret != -1); epoll_event events[MAX_EVENT_NUMBER];
int epollfd = epoll_create(5);
assert(epollfd != -1);
addfd(epollfd,listenfd,false); while(1){
int ret = epoll_wait(epollfd,events,MAX_EVENT_NUMBER,-1);
if(ret < 0){
printf("epoll failure\n");
break;
}
et(events,ret,epollfd,listenfd);
} close(listenfd);
return 0;
}

  

输入

Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
1
2
3
4
5
sdfufghbskdjhkbaskjhbkjlsadvfblkajwsvdfbljkashdbkfjhasdfhasjkhD

输出

start new thread to receive data on fd: 5
get content: 1

get content: 2
3
4

get content: 5
sdfufg
get content: hbskdjhkb
get content: askjhbkjl
get content: sadvfblka
get content: jwsvdfblj
get content: kashdbkfj
get content: hasdfhasj
get content: khD
hasj
read later
end thread receiving data on fd: 5

LINUX网络编程 IO 复用的更多相关文章

  1. Linux网络编程-IO复用技术

    IO复用是Linux中的IO模型之一,IO复用就是进程预先告诉内核需要监视的IO条件,使得内核一旦发现进程指定的一个或多个IO条件就绪,就通过进程进程处理,从而不会在单个IO上阻塞了.Linux中,提 ...

  2. <网络编程>IO复用

    IO复用是一种机制,一个进程可以监听多个描述符,一旦某个描述符就绪(读就绪和写就绪),能够同志程序进行相应的读写操作. 目前支持I/O复用的系统调用有select,poll,pselect,epoll ...

  3. linux网络编程 IO多路复用 select epoll

    本文以我的小型聊天室为例,对于服务器端的代码,做了三次改进,我将分别介绍阻塞式IO,select,epoll . 一:阻塞式IO 对于聊天室这种程序,我们最容易想到的是在服务器端accept之后,然后 ...

  4. linux网络编程IO模型

    同步与异步:         同步就是一个任务的完成需要依赖另外一个任务时,只有等待被依赖的任务完成后,依赖的任务才能算完成.         异步是不需要等待被依赖的任务完成,只是通知被依赖的任务要 ...

  5. Linux 网络编程的5种IO模型:多路复用(select/poll/epoll)

    Linux 网络编程的5种IO模型:多路复用(select/poll/epoll) 背景 我们在上一讲 Linux 网络编程的5种IO模型:阻塞IO与非阻塞IO中,对于其中的 阻塞/非阻塞IO 进行了 ...

  6. Linux 网络编程(IO模型)

    针对linux 操作系统的5类IO模型,阻塞式.非阻塞式.多路复用.信号驱动和异步IO进行整理,参考<linux网络编程>及相关网络资料. 阻塞模式 在socket编程(如下图)中调用如下 ...

  7. Linux 网络编程的5种IO模型:信号驱动IO模型

    Linux 网络编程的5种IO模型:信号驱动IO模型 背景 上一讲 Linux 网络编程的5种IO模型:多路复用(select/poll/epoll) 我们讲解了多路复用等方面的知识,以及有关例程. ...

  8. Linux 网络编程的5种IO模型:异步IO模型

    Linux 网络编程的5种IO模型:异步IO模型 资料已经整理好,但是还有未竟之业:复习多路复用epoll 阅读例程, 异步IO 函数实现 背景 上一讲< Linux 网络编程的5种IO模型:信 ...

  9. Linux网络编程(六)

    网络编程中,使用多路IO复用的典型场合: 1.当客户处理多个描述字时(交互式输入以及网络接口),必须使用IO复用. 2.一个客户同时处理多个套接口. 3.一个tcp服务程序既要处理监听套接口,又要处理 ...

随机推荐

  1. checkbox的美化(转)

    http://www.w3cfuns.com/blog-5422889-5398674.html <!DOCTYPE html> <html> <head> < ...

  2. java ee7 配置文件

    java ee7 配置文件 1. 项目目录 # ee pom.xml      Maven构建文件 /src/main/java      Java源文件 /src/main/resource     ...

  3. padding 扩大边距 margin-top 与页面顶部的距离 hover鼠标移动到上面出现背景色CSS

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  4. eclipse 注释字体不一致的问题

    eclipse中 1.解决注释的文字大小不一的情况 2.想让注释和代码大小不一样 3.win10系统下,设置Text Font时找不到Courier New字体 1.解决注释的文字大小不一的情况 打开 ...

  5. mongodb基础学习9-分片

    分片和复制集不同,复制集是多个实例存储相同的内容,而分片是将内容存储到不同的分片上,不同分片存储的数据不同 下面看看具体的操作: 先启动两个片的实例: 再启动configsvr,要加上configsv ...

  6. Gearman安装及使用

    基础安装包 yum install vim wget gcc gcc-c++ make dos2unix gperf libevent libevent-devel zlib-devel bzip2- ...

  7. webpack项目在开发环境中使用静态css文件

    webpack项目在开发环境中使用静态css文件 在webpack项目(本人使用的 vue-cli-webpack )中,需要引入 css 或 scss等样式文件时,本人目前知道的,通常有以下几种方法 ...

  8. Social media users of the world unite!

    Social media users of the world unite!全世界社交媒体用户联合起来!If Plato were alive today, he might well regard ...

  9. sql一个题的解法分析讲解

    本篇讲述的是对一个sql面试题的细致语法讲解.关于执行流程(on where),内连接,外连接(左右)上实用.关于这些基本的语法知识请参考我前面的sql基本语法. S(SNO,SNAME)学生学号,姓 ...

  10. Spring @Configuration

    下面是一个典型的spring配置文件(application-config.xml): <beans> <bean id="orderService" class ...