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模式的更多相关文章

  1. 数据同步canal服务端配置mysql多主

    canal服务端HA模式,本人并未使用过,为保证文章的完整性,从以下地址摘抄该部分内容,待以后验证及使用 https://github.com/alibaba/canal/wiki/AdminGuid ...

  2. 数据同步canal服务端HA配置

    canal服务端HA模式,本人并未使用过,为保证文章的完整性,从以下地址摘抄该部分内容,待以后验证及使用 https://github.com/alibaba/canal/wiki/AdminGuid ...

  3. Redis源代码分析(三十五)--- redis.c服务端的实现分析(2)

    在Redis服务端的代码量真的是比較大,假设一个一个API的学习怎么实现,无疑是一种效率非常低的做法,所以我今天对服务端的实现代码的学习,重在他的运行流程上.而对于他的模块设计在上一篇中我已经分析过了 ...

  4. ZooKeeper单机服务端的启动源码阅读

    程序的入口QuorumPeerMain public static void main(String[] args) { // QuorumPeerMain main = new QuorumPeer ...

  5. http服务端架构演进

    摘要 在详解http报文相关文章中我们介绍了http协议是如何工作的,那么构建一个真实的网站还需要引入组件呢?一些常见的名词到底是什么含义呢? 什么叫正向代理,什么叫反向代理 服务代理与负载均衡的差别 ...

  6. react基础学习和react服务端渲染框架next.js踩坑

    说明 React作为Facebook 内部开发 Instagram 的项目中,是一个用来构建用户界面的优秀 JS 库,于 2013 年 5 月开源.作为前端的三大框架之一,React的应用可以说是非常 ...

  7. [Next] 服务端渲染知识补充

    渲染 渲染:就是将数据和模版组装成 html 客户端渲染 客户端渲染模式下,服务端把渲染的静态文件给到客户端,客户端拿到服务端发送过来的文件自己跑一遍 js,根据 JS 运行结果,生成相应 DOM,然 ...

  8. Service系统服务(六):rsync基本用法、rsync+SSH同步、配置rsync服务端、访问rsync共享资源、使用inotifywait工具、配置Web镜像同步、配置并验证Split分离解析

    一.rsync基本用法 目标: 本例要求掌握远程同步的基本操作,使用rsync命令完成下列任务: 1> 将目录 /boot 同步到目录 /todir 下   2> 将目录 /boot 下的 ...

  9. c++ 网络编程(八) LINUX-epoll/windows-IOCP下 socket opoll函数用法 优于select方法的epoll 以及windows下IOCP 解决多进程服务端创建进程资源浪费问题

    原文作者:aircraft 原文链接:https://www.cnblogs.com/DOMLX/p/9622548.html 锲子:关于并发服务器中的I/O复用实现方式,前面在网络编程系列四还是五来 ...

随机推荐

  1. Python初学者第七天 字符串及简单操作

    7day 数据类型:字符串 1.定义 字符串是一个有序的字符的集合,用于储存和表示基本的文本信息.单.双.三引号之间的内容称之为字符串: a = ‘hello world!’ b = "你好 ...

  2. 工作好搭档(三):慧想 S100 液晶显示器支架

            引言:工欲善其事,必先利其器.码农十年,与电脑打了二十多年的交道,也配置了一些过得去的装备.资金有限,更希望所有的投入都在刀刃上.写工作好搭档系列,是晒考虑的原因.思路.经验和教训.欢 ...

  3. hiredis

    hiredis是redis开源库对外发布的客户端API包. 当redis-server配置启动后,可以通过hiredis操作redis资源. 主要分为: strings.hash.lists.sets ...

  4. [译] 怎样(以及为什么要)保持你的 Git 提交记录的整洁

    最近在掘金翻译了一篇文章,主要讲的是 Git 提交记录的维护,确实很有用,感兴趣的同学可以去看一下.链接如下: [译] 怎样(以及为什么要)保持你的 Git 提交记录的整洁 截图:

  5. 3.26-3.31【cf补题+其他】

      计蒜客)翻硬币 //暴力匹配 #include<cstdio> #include<cstring> #define CLR(a, b) memset((a), (b), s ...

  6. BIND简易教程(1):安装及基本配置

    首先,为什么说是简易教程呢?因为BIND的功能实在太多,全写出来的话要连载好久,我觉得我没有那么多精力去写:而我了解的仅仅是有限的一点点,不敢造次.百度上的文章也是一抓一大把呐!所以,教点基本使用方法 ...

  7. ZOJ Monthly, January 2019 Little Sub and his Geometry Problem 【推导 + 双指针】

    传送门:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5861 Little Sub and his Geometry Prob ...

  8. MyBatis框架(6)动态sql

    本次全部学习内容:MyBatisLearning   什么是动态sql:     mybatis的核心,对sql进行灵活的操作,通过对表达式的判断,对sql灵活的拼接 在之前小案例的基础上我们先进行简 ...

  9. MyBatis(3)开发dao方法

    本次全部学习内容:MyBatisLearning       SqlSession         SqlSession是一个面向用户(程序员)的接口.         SqlSession中提供了很 ...

  10. Spring(十八)之页面重定向

    首先说明,该示例的maven依赖可以复用Spring(十七)之表单处理还有 还有就是对应的web.xml和servlet.xml文件都能复用,不必再次修改. 说到重定向不得不提到一个转发.这里概述一下 ...