epoll模型边沿触发
body, table{font-family: 微软雅黑; font-size: 10pt}
table{border-collapse: collapse; border: solid gray; border-width: 2px 0 2px 0;}
th{border: 1px solid gray; padding: 4px; background-color: #DDD;}
td{border: 1px solid gray; padding: 4px;}
tr:nth-child(2n){background-color: #f8f8f8;}
|
EPOLL 事件有两种模型:
Edge Triggered (ET) 边缘触发 只有数据到来,才触发,不管缓存区中是否还有数据。int op = EPOLLIN | EPOLLET;
//边缘触发,读接收缓冲区中的数据的时候,读完一部分数据就会减少一部分,减少的时候不会触发,只有当客户端再次发送数据,接收数据缓存区数据有一个上升状态才会触发epoll。
不管你发多少数据,我一次只能读多少就读多少,读不完的也不会再次触发epoll_wait函数,下次再发数据在触发,就会连同上次没读完,仍然在缓存区的数据也接着读到
Level Triggered (LT) 水平触发 只要有数据都会触发。(默认就是这种)
当缓冲区有数据是,epoll_wait会不断得到触发(效率不高)如果一次发送的数据太多,超过接受数据数组的大小,当接受数据的数组读满后,read会再次触发epoll_wait函数,来接着读输入到缓冲区的数据
|
|
新的场景
1、针对客户端的每一次发送的数据,epoll_wait只触发一次
2、我们想一次把缓冲区里边的数据全部读完(为了提高效率) //要实现这两个步骤,就要把我们的描述符改为非阻塞状态,用while(1)一直读,直到返回-1,表示IO缓冲区里面的数据已经全部读走。
//阻塞状态一次读入数据送到我们定义的数组里,如果数组太小,IO缓冲区里的数据大,那么一次只能读满数组大小,后面的数据一直存在IO缓冲区里,等待下一个阻塞再读取。
|
| func.h | |
|
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
|
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <errno.h>
#include <fcntl.h>
|
| epoll_tcp_server.c | epoll_tcp_client.c |
|
#include "func.h"
//通过epoll来实现tcp即时通信
#define NUM 10
void change_noblock(int fd) //设置文件描述符属性非阻塞
{
int status;
status=fcntl(fd,F_GETFL);
status=status|O_NONBLOCK;
int ret=fcntl(fd,F_SETFL,status);
if(-1==ret)
{
perror("fcntl");
return;
}
}
int main(int argc,char* argv[])
{
if(argc!=3)
{
printf("error args\n");
return -1;
}
int sfd=socket(AF_INET,SOCK_STREAM,0);
if(-1==sfd)
{
perror("socket");
return -1;
}
struct sockaddr_in ser;
memset(&ser,0,sizeof(ser));
ser.sin_family=AF_INET;
ser.sin_port=htons(atoi(argv[2])); //一定要用htons
ser.sin_addr.s_addr=inet_addr(argv[1]);
int ret;
//给sfd绑定IP地址和端口号
ret=bind(sfd,(struct sockaddr*)&ser,sizeof(struct sockaddr));
if(-1==ret)
{
perror("bind");
return -1;
}
ret=listen(sfd,NUM);
if(-1==ret)
{
perror("listen");
return -1;
}
int epfd=epoll_create(1);
struct epoll_event event,evs[NUM+2];
event.events=EPOLLIN; //注册sfd
event.data.fd=sfd;
ret=epoll_ctl(epfd,EPOLL_CTL_ADD,sfd,&event);
if(-1==ret)
{
perror("epoll_ctl");
return -1;
}
event.events=EPOLLIN; //注册标准输入
event.data.fd=0;
ret=epoll_ctl(epfd,EPOLL_CTL_ADD,0,&event);
if(-1==ret)
{
perror("epoll_ctl");
return -1;
}
int i;
int new_fd;
char buf[5]; //buf改小,为了看到边沿触发效果
int n;
while(1)
{
memset(evs,0,sizeof(evs));
ret=epoll_wait(epfd,evs,NUM+2,-1);
if(ret >0)
{
for(i=0;i<ret;i++)
{
if(evs[i].events == EPOLLIN && evs[i].data.fd == sfd)
{
new_fd=accept(sfd,NULL,NULL);
printf("new_fd =%d\n",new_fd);
event.events=EPOLLIN | EPOLLET; //边沿触发的是否可读
change_noblock(new_fd); //改变描述符的属性为非阻塞,这个放前放后都可以,只要修改,没关闭之前在哪都是修改完的属性
event.data.fd=new_fd;
epoll_ctl(epfd,EPOLL_CTL_ADD,new_fd,&event);
}
if(evs[i].events == EPOLLIN && evs[i].data.fd == 0)
{
memset(buf,0,sizeof(buf));
n=read(0,buf,sizeof(buf));
if(n>0)
{
send(new_fd,buf,strlen(buf)-1,0);
}else if(n==0)
{
printf("bye\n");
event.events=EPOLLIN;
event.data.fd=new_fd;
epoll_ctl(epfd,EPOLL_CTL_DEL,new_fd,&event);
close(new_fd);
}
}
if(evs[i].events == EPOLLIN &&evs[i].data.fd == new_fd)
{
while(1)
//while(1) 一直读IO缓冲区里面的数据,直到全部读走,返回EAGAIN;由于newfd描述符属性已经改为非阻塞,监听事件改为边缘触发,IO缓冲区里数据没有读完不会再次触发描述符,所以这里while(1)要IO缓冲区数据一次读完; 对端发一次,我们这边只触发一次,不会一直触发;
//这个例子是单线程,多线程的情况下可以交给其他线程处理
{
memset(buf,0,sizeof(buf));
n=recv(new_fd,buf,sizeof(buf),0);
if(n>0)
{
printf("%s",buf);
}else if(n == -1 && errno == EAGAIN)
//加上errno判断是因为recv函数失败也是返回-1;recv只有描述符是非阻塞的才会返回-1,默认阻塞情况下只有函数出错才返回-1;
{
break;
}else if(n==0){
printf("Bye\n");
event.events=EPOLLIN;
event.data.fd=new_fd;
epoll_ctl(epfd,EPOLL_CTL_DEL,new_fd,&event);
close(new_fd);
}
}
printf("\n"); //刷新缓冲区,输出数据
//前面printf都是是把数据送到输出缓冲区,必须回车或者fflush(stdout)才能把接收到的数据送到输出终端;使用fflush(stdout);后还是要加一个输出一个回车
//这个回车符可以不要,但是要客户端发送数据的时候不要strlen(len)-1,要连同换行符一起发过来;
}
}
}
}
return 0;
}
|
#include "func.h"
void change_noblock(int fd) //设置文件描述符属性非阻塞
{
int status;
status=fcntl(fd,F_GETFL);
status=status|O_NONBLOCK;
int ret=fcntl(fd,F_SETFL,status);
if(-1==ret)
{
perror("fcntl");
return;
}
}
int main(int argc,char** argv)
{
if(argc !=3)
{
printf("error args\n");
return -1;
}
int sfd=socket(AF_INET,SOCK_STREAM,0);
if(-1==sfd)
{
perror("socket");
return -1;
}
struct sockaddr_in ser;
memset(&ser,0,sizeof(ser));
ser.sin_family=AF_INET;
ser.sin_port=htons(atoi(argv[2])); //一定要用htons
ser.sin_addr.s_addr=inet_addr(argv[1]);
int ret;
ret=connect(sfd,(struct sockaddr*)&ser,sizeof(struct sockaddr));
if(-1==ret)
{
perror("connect");
return -1;
}
int epfd=epoll_create(1);
struct epoll_event event,evs[2];
event.events = EPOLLIN | EPOLLET;
event.data.fd=sfd;
change_nonblock(sfd);
ret=epoll_ctl(epfd,EPOLL_CTL_ADD,sfd,&event);
if(-1==ret)
{
perror("epoll_ctl");
return -1;
}
event.events=EPOLLIN;
event.data.fd=0;
ret=epoll_ctl(epfd,EPOLL_CTL_ADD,0,&event);
if(-1==ret)
{
perror("epoll_ctl");
return -1;
}
int i;
char buf[5];
int n;
while(1)
{
memset(evs,0,sizeof(evs));
ret=epoll_wait(epfd,evs,2,-1);
if(ret>0)
{
for(i=0;i<ret;i++)
{
if(evs[i].events == EPOLLIN && evs[i].data.fd == 0)
{
memset(buf,0,sizeof(buf));
n=read(0,buf,sizeof(buf));
if(n==0)
{
printf("bye\n");
close(sfd);
return 0;
}
n=send(sfd,buf,strlen(buf)-1,0);
//这里如果不减1,对端最后就不用printf("\n");刷新缓冲区,输出接收到的数据
if(-1==n)
{
perror("send");
return -1;
}
}
if(evs[i].events == EPOLLIN && evs[i].data.fd == sfd)
{
while(1)
{
memset(buf,0,sizeof(buf));
n=recv(sfd,buf,sizeof(buf),0);
if(n > 0)
{
printf("%s\n",buf);
}
else if(-1==n&&errno==EAGAIN)
{
//close(sfd);
break;
}
else if(n==0)
{
printf("bye\n");
close(sfd);
return 0;
}
}
}
}
}
}
return 0;
}
|
epoll模型边沿触发的更多相关文章
- (OK) Linux epoll模型—socket epoll server client chat
http://www.cnblogs.com/venow/archive/2012/11/30/2790031.html http://blog.csdn.net/denkensk/article/d ...
- 基于EPOLL模型的局域网聊天室和Echo服务器
一.EPOLL的优点 在Linux中,select/poll/epoll是I/O多路复用的三种方式,epoll是Linux系统上独有的高效率I/O多路复用方式,区别于select/poll.先说sel ...
- Epoll模型详解
Linux 2.6内核中提高网络I/O性能的新方法-epoll I/O多路复用技术在比较多的TCP网络服务器中有使用,即比较多的用到select函数. 1.为什么select落后 首先,在Lin ...
- 【转】select和epoll模型的差异
http://www.cppblog.com/converse/archive/2008/10/12/63836.html epoll为什么这么快 epoll是多路复用IO(I/O Multiplex ...
- linux epoll模型
原文:http://yjtjh.blog.51cto.com/1060831/294119 Linux I/O多路复用技术在比较多的TCP网络服务器中有使用,即比较多的用到select函数.Linux ...
- Linux网络服务器epoll模型的socket通讯的实现(一)
准备写一个网络游戏的服务器的通讯模块,参考网上看到的一些代码,在linux下面实现一个多线程的epoll模型的socket通讯的代码,以下是第一部分多线程的切换代码: 1 #include <s ...
- nginx中的epoll模型
要了解epoll模型,就要一个一个知识点由浅至深地去探索. 1.IO复用技术 IO流请求操作系统内核,有串行处理和并行处理两种概念. 串行处理是前面一个操作处理地时候,后面的所有操作都需要等待.因此, ...
- select 和epoll模型区别
1.select 和epoll模型区别 1.1.网络IO模型概述 通常来说,网络IO可以抽象成用户态和内核态之间的数据交换.一次网络数据读取操作(read),可以拆分成两个步骤:1)网卡驱动等待数据准 ...
- Epoll模型
Epoll模型 相比于select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率.因为在内核中的select实现中,它是采用轮询来处理的,轮询的fd数目越多,自然耗时越多.并且,在l ...
随机推荐
- vue.js(四)
由于组件内容太多又特别关键,我决定在官网教程的基础上,加上自己的理解,针对每个内容详细记录一下 1.注册组件 ①全局注册 //首先创建组件 Vue.component('blog-post', { ...
- UML Diagrams Using Graphviz Dot
Introduction Background This article is about using the dot tool from the Graphviz package to automa ...
- Mysql中的auto_increment
Mysql中的auto_increment 1.创建 2.使用 [1]如果不写固定列,则必须要插入该列,可以直接写Null,否则会报错 [2]可以直接在auto_increment 列上直接插入显式值 ...
- 使用JAVA实现语音朗读一段文本
需要做的工作: 1.下载 jacob-1.17-M2 或 jacob-1.18 2.解压jacob-1.17-M2 或 jacob-1.18 3.向工程里导入jacob.jar 4.将 jacob- ...
- php中使用Curl、socket、file_get_contents三种方法POST提交数据
抓取远程内容,之前一直都在用file_get_content函数,其实早就知道有curl这么一个好东西的存在,但是看了一眼后感觉使用颇有些复杂,没有file_get_content那么简单,再就是需求 ...
- Miller-Rabin素数测试算法(POJ1811Prime Test)
题目链接:http://poj.org/problem?id=1811 题目解析:2<=n<2^54,如果n是素数直接输出,否则求N的最小质因数. 求大整数最小质因数的算法没看懂,不打算看 ...
- 【转】Deep Learning(深度学习)学习笔记整理系列之(六)
9.3.Restricted Boltzmann Machine (RBM)限制波尔兹曼机 假设有一个二部图,每一层的节点之间没有链接,一层是可视层,即输入数据层(v),一层是隐藏层(h),如果假设所 ...
- Ubuntu下navicat过期解决办法
Ubuntu下使用navicat过期.试用期是15天. 可以删除.navicat64/解决.不好的一点就是.需要重新连接数据库,以前的连接记录会被删除 rm -rf ~/.navicat64/
- curl基本使用
curl简介 linux curl是一个利用URL规则在命令行下工作的文件传输工具.它支持文件的上传和下载. curl可以使用URL的语法模拟浏览器来传输数据,因为它是模拟浏览器,因此它同样支持多种协 ...
- 一个PE文件的逆向分析
一个PE文件的逆向分析 idf-ctf上有个题,是PE文件的逆向,反正对我来说做出来就是有意思的题,做不出来就没劲.言归正传,下面看一下吧 大家想玩可以去这个地方去拿题http://pan.baidu ...