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复用实现方式,前面在网络编程系列四还是五来 ...
随机推荐
- linux命令整理
Linux系统命令 1. ls 查看某个 目录下 所有文件的大小总和 ls -lR| awk 'BEGIN{size=0;} /^[-l]/{size+=$5;print $0;} END{print ...
- HDU 3420 -- Bus Fair ACM
Bus Fair Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total S ...
- 重新认识KCP
什么是KCP KCP是一种网络传输协议(ARQ,自动重传请求),可以视它为TCP的代替品,但是它运行于用户空间,它不管底层的发送与接收,只是个纯算法实现可靠传输,它的特点是牺牲带宽来降低延迟.因为TC ...
- 第六周 day6 python学习笔记
1.Python面向对象编程OOP(Object Oriented Programming) 封装:可以隐藏实现细节,使代码模块化 继承:可以扩展已存在的代码模块,可以使代码实现重用 多态:一种接口, ...
- 第二周 day2 python学习笔记
1. python中的三元运算: result=value1 if 条件 else value2 如果条件成立,result=value1 如果条件不成立,result=value2 2. pytho ...
- 在Clion里链接gtest
本来以为像之前链接boost一样,加个链接路径就好了,没想到报找不到gtest的符号,搞了半天,没弄明白啥原因. 网上也没搜到好方法,只能把gtest的源码加到项目里,然后在链接了. CMake配置如 ...
- 浅聊IOC
1.概述 IOC:有很多人把控制反转和依赖注入混为一谈,虽然在某种意义上来看他们是一体的,但好像又有些不同. 1. IOC(控制反转)是一个控制容器,DI(依赖注入)就是这个容器的运行机制. 2. I ...
- Codeforces Round #437 (Div. 2)[A、B、C、E]
Codeforces Round #437 (Div. 2) codeforces 867 A. Between the Offices(水) 题意:已知白天所在地(晚上可能坐飞机飞往异地),问是否从 ...
- 「bzoj 3944: Sum」
题目 杜教筛板子了 #include<iostream> #include<cstring> #include<cstdio> #include<cmath& ...
- 让Git不再难学
写在前面 在团队做过软件开发的,版本控制必是不可或缺的一项.目前,版本控制主要分为集中式版本控制系统和分布式版本控制系统 ,即大家熟知的SVN和Git.Git是当下最流行的分布式版本控制系统,故,今天 ...