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复用实现方式,前面在网络编程系列四还是五来 ...
随机推荐
- lua-excel助手
excel是我们工作及生活当中不可或缺的东西,好吧,我是一个游戏程序员,数值哥哥肯定会给我些表格的.回归正题,为什么需要做这个封装? 为什么需要这个项目,因为我们需要使用程序进行自动化操作 VBA我们 ...
- redis三节点sentinel部署
角色 ip port master 127.0.0.1 6379 slave-1 127.0.0.1 6380 slave-2 127. ...
- Ubuntu16.04更换NVIDIA驱动导致无法进入图形界面的解决方案
一.进入recovery模式 由于无法进入图形界面,所以需要在开机时进入恢复模式.我的机器上时在开机时通过引导选项中的recovery mode选项进入,进入之后可以看到许多选项卡,选择root,回车 ...
- 【ACM】那些年,我们挖(WA)过的最短路
不定时更新博客,该博客仅仅是一篇关于最短路的题集,题目顺序随机. 算法思想什么的,我就随便说(复)说(制)咯: Dijkstra算法:以起始点为中心向外层层扩展,直到扩展到终点为止.有贪心的意思. 大 ...
- 洛谷 P4321 【随机漫游】
题目大意 给出\(n(n\leq 18)\)个点的无向连通图,\(m(m\leq 10^5)\)次询问.每次询问给出一个点集和一个起点\(s\),询问从\(s\)出发,经过这个点集中的每一个点至少一次 ...
- Monad新解-FRP对比——ReactiveCocoa、RxSwift、Bacon以及背后的Functional
ReactiveX Rx的Observable的本质就是一个Event Monad,即上下文(就是图文教程中包裹的盒子)为Event的一个Monad,这里的Event定义,可以对应语言的struct或 ...
- CALayer & bitmap Content
Working with High-Resolution Images Layers do not have any inherent knowledge of the resolution of t ...
- thinkPHP输出sql语句(3.2和5.0通用)
//5.0$qwe = db::table('think_user')->where('id',1)->fetchsql()->column('name'); dump($qwe); ...
- Redis的Windows端开发连接Linux端以及相应的代码实现
在Windows端开发连接需要进行配置文件的配置: 在redis.conf配置文件中 将protected-mode yes,修改为protected-mode no:不保护redis # By de ...
- jQuery中append()、prepend()与after()、before()的区别
转载 未曾见海 https://www.cnblogs.com/afei-qwerty/p/6682963.html 在jQuery中,添加元素有append(),prepend() 和 after ...