epoll 服务端 ET模式
windows下IOCP, linux下 epoll。
epoll模型其实也是一个同步模型,ET是epoll里面的一种模式,叫 边缘触发。 个人理解,类似于 windows下的事件选择模型。代码如下:
#include <unistd.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <fcntl.h>
#include <netinet/in.h> #include <errno.h> #define MAX_BACKLOG 256
#define MAX_EVENTS 1024 // 如EPOLL的作者Davide Libenzi所说,如果你对一fd同时注册EPOLLIN | EPOLLOUT事件,
// 即使发送缓冲区并非由满变空,也会触发EPOLLOUT事件
// 使用epoll的最好是用ATM模式,当真正需要用到EPOLLOUT时才注册。我理解的ATM模式就是读、写、读、写这样的循环。 // ET模式下的读写
// 只要可读, 就一直读, 直到返回 0, 或者 errno = EAGAIN
// 只要可写, 就一直写, 直到数据发送完, 或者 errno = EAGAIN // 保证对非阻塞的套接字写够请求的字节数才返回
ssize_t socket_write(int sockfd, const char* buffer, size_t buflen)
{
ssize_t tmp = ;
size_t total = buflen;
const char* p = buffer;
while ()
{
tmp = write(sockfd, p, total);
if(tmp < )
{
// 当send收到信号时,可以继续写,但这里返回-1.
if(errno == EINTR)
{
return -;
}
// 当socket是非阻塞时,如返回此错误,表示写缓冲队列已满,在这里做延时后再重试.
if(errno == EAGAIN)
{
usleep();
continue;
} return -;
} if((size_t)tmp == total)
{
return buflen;
} total -= tmp;
p += tmp;
} return tmp; // 返回已写的字节数
} // 设定socket句柄为非阻塞
static int SetNonBlock(int nFd)
{
int nOldOpt = fcntl(nFd, F_GETFL, );
int nNewOpt = nOldOpt | O_NONBLOCK; return fcntl(nFd, F_SETFL, nNewOpt);
} // 为套接字追加事件
//static void AddEvent(int nEpfd, int nFd)
//{
// struct epoll_event event;
// event.data.fd = nFd;
// //event.events = EPOLLIN | EPOLLOUT | EPOLLHUP | EPOLLET;
// event.events = EPOLLIN | EPOLLOUT | EPOLLET;
// epoll_ctl(nEpfd, EPOLL_CTL_ADD, nFd, &event);
//} static void add_event(int epollfd,int fd,int state)
{
struct epoll_event ev;
ev.events = state | EPOLLET;
ev.data.fd = fd;
epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&ev);
} static void delete_event(int epollfd,int fd,int state)
{
struct epoll_event ev;
ev.events = state | EPOLLET;
ev.data.fd = fd;
epoll_ctl(epollfd,EPOLL_CTL_DEL,fd,&ev);
} static void modify_event(int epollfd,int fd,int state)
{
struct epoll_event ev;
ev.events = state | EPOLLET;
ev.data.fd = fd;
epoll_ctl(epollfd,EPOLL_CTL_MOD,fd,&ev);
} // 主函数
int main(int argc, char const *argv[])
{
if (argc != )
{
printf("usage: CMD Port\n");
exit(-);
} int nPort = atoi(argv[]);
if (nPort < )
{
printf("Port Invalid\n");
exit(-);
} int nSvrFd = socket(AF_INET, SOCK_STREAM, );
// 设置非阻塞
SetNonBlock(nSvrFd); // 绑定地址
struct sockaddr_in addr;
//bzero(&addr,sizeof(addr));
memset(&addr,0x00, sizeof(addr));
addr.sin_port = htons(nPort);
inet_pton(AF_INET,"127.0.0.1",&addr.sin_addr);
addr.sin_family = (AF_INET); if ( != bind(nSvrFd, (struct sockaddr*)&addr, sizeof(addr)))
{
perror("Bind Failure:");
exit(-);
} //监听端口
if ( != listen(nSvrFd, MAX_BACKLOG))
{
perror("Listen Failure:");
exit(-);
}
// 创建epoll句柄
int nEpfd = epoll_create(); struct epoll_event events[MAX_EVENTS];
// AddEvent(nEpfd, nSvrFd); add_event(nEpfd,nSvrFd,EPOLLIN); while ()
{
//等待事件到来
int nReadyNums = epoll_wait(nEpfd, events, MAX_EVENTS, -);
int nClientFd = -; struct sockaddr_in cliaddr;
socklen_t cliaddrlen = sizeof(cliaddr); for (int i = ; i < nReadyNums; ++i)
{
if (events[i].data.fd == nSvrFd)
{
// accept放到线程中,可以使用while循环
// 多个连接同时到达,服务器的 TCP 就绪队列瞬间积累多个就绪
// 连接,由于是边缘触发模式,epoll 只会通知一次,accept 只处理一个连接,导致 TCP 就绪队列中剩下的连接都得不到处理。
// 解决办法是用 while 循环抱住 accept 调用,处理完 TCP 就绪队列中的所有连接后再退出循环。
while ((nClientFd = accept(nSvrFd,(struct sockaddr*)&cliaddr,&cliaddrlen)) > )
// if ((nClientFd = accept(nSvrFd,(struct sockaddr*)&cliaddr,&cliaddrlen)) > 0)
{
printf("建立连接\n");
//设置为非阻塞
SetNonBlock(nClientFd);
//添加事件监听
// AddEvent(nEpfd, nClientFd);
add_event(nEpfd,nClientFd,EPOLLIN);
} }
else
{
// 处理FD事件
if (events[i].events & EPOLLIN)
{
int n = ;
int nread = ;
char buf[BUFSIZ];
memset(buf, 0x00, sizeof(buf));
while ((nread = read(events[i].data.fd, buf + n, BUFSIZ-)) > )
{
n += nread;
} if (nread == - && errno != EAGAIN)
{
perror("read error");
}
printf("read----%s\n", buf); // 直接去写,不需要EPOLLOUT事件
const char* p = "epoll_server.cpp answer....\n";
socket_write(events[i].data.fd, p, strlen(p) + ); // 后面代码无效,ET模式下,EPOLLOUT事件不响应或者出错,没搞通,实际代码中也不会用到,忽略。
continue; // modify_event(nEpfd,nClientFd,EPOLLOUT); // continue;
// 强制触发一次
struct epoll_event ev;
int fd = events[i].data.fd;
ev.events = events[i].events | EPOLLOUT;
if(- == epoll_ctl(nEpfd, EPOLL_CTL_MOD, fd, &ev))
{
perror("epoll_ctl: mod");
}
}
// 可写
if (events[i].events & EPOLLOUT)
{
const char* p = "epoll_server.cpp answer....";
socket_write(events[i].data.fd, p, strlen(p) + ); //modify_event(nEpfd,nClientFd,EPOLLIN);
// 强制触发一次
// struct epoll_event ev;
// int fd = events[i].data.fd;
// ev.events = events[i].events | EPOLLIN;
// if(-1 == epoll_ctl(nEpfd, EPOLL_CTL_MOD, fd, &ev))
// {
// perror("epoll_ctl: mod");
// }
}
} }
} }
使用telnet 命令模拟客户端进行测试,结果如下:
服务端:
客户端:
比较好的linux 网络编程文章 http://www.cnblogs.com/Anker/p/3265058.html
epoll 服务端 ET模式的更多相关文章
- 数据同步canal服务端配置mysql多主
canal服务端HA模式,本人并未使用过,为保证文章的完整性,从以下地址摘抄该部分内容,待以后验证及使用 https://github.com/alibaba/canal/wiki/AdminGuid ...
- 数据同步canal服务端HA配置
canal服务端HA模式,本人并未使用过,为保证文章的完整性,从以下地址摘抄该部分内容,待以后验证及使用 https://github.com/alibaba/canal/wiki/AdminGuid ...
- Redis源代码分析(三十五)--- redis.c服务端的实现分析(2)
在Redis服务端的代码量真的是比較大,假设一个一个API的学习怎么实现,无疑是一种效率非常低的做法,所以我今天对服务端的实现代码的学习,重在他的运行流程上.而对于他的模块设计在上一篇中我已经分析过了 ...
- ZooKeeper单机服务端的启动源码阅读
程序的入口QuorumPeerMain public static void main(String[] args) { // QuorumPeerMain main = new QuorumPeer ...
- http服务端架构演进
摘要 在详解http报文相关文章中我们介绍了http协议是如何工作的,那么构建一个真实的网站还需要引入组件呢?一些常见的名词到底是什么含义呢? 什么叫正向代理,什么叫反向代理 服务代理与负载均衡的差别 ...
- react基础学习和react服务端渲染框架next.js踩坑
说明 React作为Facebook 内部开发 Instagram 的项目中,是一个用来构建用户界面的优秀 JS 库,于 2013 年 5 月开源.作为前端的三大框架之一,React的应用可以说是非常 ...
- [Next] 服务端渲染知识补充
渲染 渲染:就是将数据和模版组装成 html 客户端渲染 客户端渲染模式下,服务端把渲染的静态文件给到客户端,客户端拿到服务端发送过来的文件自己跑一遍 js,根据 JS 运行结果,生成相应 DOM,然 ...
- Service系统服务(六):rsync基本用法、rsync+SSH同步、配置rsync服务端、访问rsync共享资源、使用inotifywait工具、配置Web镜像同步、配置并验证Split分离解析
一.rsync基本用法 目标: 本例要求掌握远程同步的基本操作,使用rsync命令完成下列任务: 1> 将目录 /boot 同步到目录 /todir 下 2> 将目录 /boot 下的 ...
- c++ 网络编程(八) LINUX-epoll/windows-IOCP下 socket opoll函数用法 优于select方法的epoll 以及windows下IOCP 解决多进程服务端创建进程资源浪费问题
原文作者:aircraft 原文链接:https://www.cnblogs.com/DOMLX/p/9622548.html 锲子:关于并发服务器中的I/O复用实现方式,前面在网络编程系列四还是五来 ...
随机推荐
- Oracle修改表名的几种方式
因为原来所在表不想被删除,但又需要新建立一个相同表名的表,故先把原来的表的表名更改为另一个临时表名. 查看当前用户下所有的表 select tname from tab where tabtype= ...
- 沉淀,再出发——在Hadoop集群的基础上搭建Spark
在Hadoop集群的基础上搭建Spark 一.环境准备 在搭建Spark环境之前必须搭建Hadoop平台,尽管以前的一些博客上说在单机的环境下使用本地FS不用搭建Hadoop集群,可是在新版spark ...
- OC基础数据类型-NSDictionary
1.字典NSDictionary 字典与数组的区别:数组讲究顺序,而字典可以快速帮助我们找到数据,数据是我们的目的,键是我们的手段 NSDictionary *dict = [[NSDictionar ...
- PhoneGap Geolocation结合百度地图api获取地理位置api
一.使用百度地图API 1.地址:http://developer.baidu.com/map/ 2.在js DEMO中获取反地址解析的DEMO 3.修改这个DEMO的密钥,去创建应用就能创建密钥,然 ...
- Ubuntu14.04安装PowerDNS踩坑实录
公司要使用PowerDNS,作为内网域名解析的工具.让我和另一组的同事学一下如何配置及调优.所以先找了两台服务器试着安装一下.这一装就是一个礼拜,经历了大大小小的坑,记下来以后可能需要参考.安装过程如 ...
- Could not publish to the server. null argument:
启动tomcat或clean tomcat报错:Could not publish to the server. null argument: Could not publish to the ser ...
- tp3.2上一篇下一篇功能
1. 后台 //上一页 $map1['a_id'] = array('gt',$a_id); $map1['cate_id'] = array('eq',$cate_id); $front=$arc- ...
- PHP读取文件内容的五种方式(转载)
php读取文件内容的五种方式 分享下php读取文件内容的五种方法:好吧,写完后发现文件全部没有关闭.实际应用当中,请注意关闭 fclose($fp); php读取文件内容: -----第一种方法--- ...
- ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室 实战系列(内容已过期,阅读请慎重)
项目简介 利用ASP.NET SignalR技术与Layim前端im框架实现的一个简单的web聊天室,包括单聊,群聊,加好友,加群,好友搜索,管理,群组管理,好友权限设置等功能.涉及技术: Elast ...
- Css3 实现关键帧动画
<div class="person"> </div> <script> var str1 = "@keyframes move{&q ...