Epoll在LT和ET模式下的读写方式

From: http://www.ccvita.com/515.html

 

在一个非阻塞的socket上调用read/write函数, 返回EAGAIN或者EWOULDBLOCK(注: EAGAIN就是EWOULDBLOCK)
从字面上看, 意思是:EAGAIN: 再试一次,EWOULDBLOCK: 如果这是一个阻塞socket, 操作将被block,perror输出: Resource temporarily unavailable

总结:
这个错误表示资源暂时不够,能read时,读缓冲区没有数据,或者write时,写缓冲区满了。遇到这种情况,如果是阻塞socket,read/write就要阻塞掉。而如果是非阻塞socket,read/write立即返回-1, 同时errno设置为EAGAIN。
所以,对于阻塞socket,read/write返回-1代表网络出错了。但对于非阻塞socket,read/write返回-1不一定网络真的出错了。可能是Resource temporarily unavailable。这时你应该再试,直到Resource available。

综上,对于non-blocking的socket,正确的读写操作为:
读:忽略掉errno = EAGAIN的错误,下次继续读
写:忽略掉errno = EAGAIN的错误,下次继续写

对于select和epoll的LT模式,这种读写方式是没有问题的。但对于epoll的ET模式,这种方式还有漏洞。

epoll的两种模式LT和ET
二者的差异在于level-trigger模式下只要某个socket处于readable/writable状态,无论什么时候进行epoll_wait都会返回该socket;而edge-trigger模式下只有某个socket从unreadable变为readable或从unwritable变为writable时,epoll_wait才会返回该socket。

所以,在epoll的ET模式下,正确的读写方式为:
读:只要可读,就一直读,直到返回0,或者 errno = EAGAIN
写:只要可写,就一直写,直到数据发送完,或者 errno = EAGAIN

正确的读

n = 0;
while ((nread = read(fd, buf + n, BUFSIZ-1)) > 0) {
  n += nread;
}
if (nread == -1 && errno != EAGAIN) {
  perror("read error");
}

正确的写

int nwrite, data_size = strlen(buf);
n = data_size;
while (n > 0) {
  nwrite = write(fd, buf + data_size - n, n);
  if (nwrite < n) {
  if (nwrite == -1 && errno != EAGAIN) {
  perror("write error");
  }
  break;
  }
  n -= nwrite;
}

正确的accept,accept 要考虑 2 个问题
(1) 阻塞模式 accept 存在的问题
考虑这种情况:TCP连接被客户端夭折,即在服务器调用accept之前,客户端主动发送RST终止连接,导致刚刚建立的连接从就绪队列中移出,如果套接口被设置成阻塞模式,服务器就会一直阻塞在accept调用上,直到其他某个客户建立一个新的连接为止。但是在此期间,服务器单纯地阻塞在accept调用上,就绪队列中的其他描述符都得不到处理。

解决办法是把监听套接口设置为非阻塞,当客户在服务器调用accept之前中止某个连接时,accept调用可以立即返回-1,这时源自Berkeley的实现会在内核中处理该事件,并不会将该事件通知给epool,而其他实现把errno设置为ECONNABORTED或者EPROTO错误,我们应该忽略这两个错误。

(2)ET模式下accept存在的问题
考虑这种情况:多个连接同时到达,服务器的TCP就绪队列瞬间积累多个就绪连接,由于是边缘触发模式,epoll只会通知一次,accept只处理一个连接,导致TCP就绪队列中剩下的连接都得不到处理。

解决办法是用while循环抱住accept调用,处理完TCP就绪队列中的所有连接后再退出循环。如何知道是否处理完就绪队列中的所有连接呢?accept返回-1并且errno设置为EAGAIN就表示所有连接都处理完。

综合以上两种情况,服务器应该使用非阻塞地accept,accept在ET模式下的正确使用方式为:

while ((conn_sock = accept(listenfd,(struct sockaddr *) &remote, (size_t *)&addrlen)) > 0) {
  handle_client(conn_sock);
}
if (conn_sock == -1) {
  if (errno != EAGAIN && errno != ECONNABORTED && errno != EPROTO && errno != EINTR)
  perror("accept");
}

一道腾讯后台开发的面试题
使用Linuxepoll模型,水平触发模式;当socket可写时,会不停的触发socket可写的事件,如何处理?

第一种最普遍的方式:
需要向socket写数据的时候才把socket加入epoll,等待可写事件。
接受到可写事件后,调用write或者send发送数据。
当所有数据都写完后,把socket移出epoll。

这种方式的缺点是,即使发送很少的数据,也要把socket加入epoll,写完后在移出epoll,有一定操作代价。

一种改进的方式:
开始不把socket加入epoll,需要向socket写数据的时候,直接调用write或者send发送数据。如果返回EAGAIN,把socket加入epoll,在epoll的驱动下写数据,全部数据发送完毕后,再移出epoll。

这种方式的优点是:数据不多的时候可以避免epoll的事件处理,提高效率。

最后贴一个使用epoll,ET模式的简单HTTP服务器代码:

#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/epoll.h>
#include <sys/sendfile.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <fcntl.h>
#include <errno.h>
#define MAX_EVENTS 10
#define PORT 8080
//设置socket连接为非阻塞模式
void setnonblocking(int sockfd) {
  int opts;
  opts = fcntl(sockfd, F_GETFL);
  if(opts < 0) {
  perror("fcntl(F_GETFL)\n");
  exit(1);
  }
  opts = (opts | O_NONBLOCK);
  if(fcntl(sockfd, F_SETFL, opts) < 0) {
  perror("fcntl(F_SETFL)\n");
  exit(1);
  }
}
int main(){
  struct epoll_event ev, events[MAX_EVENTS];
  int addrlen, listenfd, conn_sock, nfds, epfd, fd, i, nread, n;
  struct sockaddr_in local, remote;
  char buf[BUFSIZ];
  //创建listen socket
  if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
  perror("sockfd\n");
  exit(1);
  }
  setnonblocking(listenfd);
  bzero(&local, sizeof(local));
  local.sin_family = AF_INET;
  local.sin_addr.s_addr = htonl(INADDR_ANY);;
  local.sin_port = htons(PORT);
  if( bind(listenfd, (struct sockaddr *) &local, sizeof(local)) < 0) {
  perror("bind\n");
  exit(1);
  }
  listen(listenfd, 20);
  epfd = epoll_create(MAX_EVENTS);
  if (epfd == -1) {
  perror("epoll_create");
  exit(EXIT_FAILURE);
  }
  ev.events = EPOLLIN;
  ev.data.fd = listenfd;
  if (epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev) == -1) {
  perror("epoll_ctl: listen_sock");
  exit(EXIT_FAILURE);
  }
  for (;;) {
  nfds = epoll_wait(epfd, events, MAX_EVENTS, -1);
  if (nfds == -1) {
  perror("epoll_pwait");
  exit(EXIT_FAILURE);
  }
  for (i = 0; i < nfds; ++i) {
  fd = events[i].data.fd;
  if (fd == listenfd) {
  while ((conn_sock = accept(listenfd,(struct sockaddr *) &remote,
  (size_t *)&addrlen)) > 0) {
  setnonblocking(conn_sock);
  ev.events = EPOLLIN | EPOLLET;
  ev.data.fd = conn_sock;
  if (epoll_ctl(epfd, EPOLL_CTL_ADD, conn_sock,
  &ev) == -1) {
  perror("epoll_ctl: add");
  exit(EXIT_FAILURE);
  }
  }
  if (conn_sock == -1) {
  if (errno != EAGAIN && errno != ECONNABORTED
  && errno != EPROTO && errno != EINTR)
  perror("accept");
  }
  continue;
  }
  if (events[i].events & EPOLLIN) {
  n = 0;
  while ((nread = read(fd, buf + n, BUFSIZ-1)) > 0) {
  n += nread;
  }
  if (nread == -1 && errno != EAGAIN) {
  perror("read error");
  }
  ev.data.fd = fd;
  ev.events = events[i].events | EPOLLOUT;
  if (epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &ev) == -1) {
  perror("epoll_ctl: mod");
  }
  }
  if (events[i].events & EPOLLOUT) {
  sprintf(buf, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n\r\nHello World", 11);
  int nwrite, data_size = strlen(buf);
  n = data_size;
  while (n > 0) {
  nwrite = write(fd, buf + data_size - n, n);
  if (nwrite < n) {
  if (nwrite == -1 && errno != EAGAIN) {
  perror("write error");
  }
  break;
  }
  n -= nwrite;
  }
  close(fd);
  }
  }
  }
  return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/403530.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

react 创建

1&#xff1a;可以html页直接引入 <script src"../build/react.development.js"></script><script src"../build/react-dom.development.js"></script><script src"../build/babel.min.js"></script> 2&a…

《精彩绝伦的CSS》读书笔记(二)

3.2 大多数允许使用多个关键字的CSS属性都允许以任何顺序书写关键字,但font属性是很少见的例外之一.font: <font-size> <font-family>;这连个只必须按照既定的顺序进行书写,如果顺序颠倒或者漏掉了其中一个,浏览器会完全忽略这条声明. 其他关键字全部都得放在这两个…

Session机制详解

虽然session机制在web应用程序中被采 用已经很长时间了&#xff0c;但是仍然有很多人不清楚session机制的本质&#xff0c;以至不能正确的应用这一技术。本文将详细讨论session的工作机制并且对在 Java web application中应用session机制时常见的问题作出解答。 一、术语ses…

element-ui的表单校验;el-form表单校验;el-form表单自定义校验;手机号校验;车牌号校验;车牌号正则校验;手动校验表单某一项;手动清空表单的某一项校验结果

示例&#xff1a;代码在末尾 可以直接复制使用 一、基本属性认知&#xff1a; 1. required: true 会有 * &#xff0c; 但仅是触发最后点击提交按钮时&#xff0c;校验某一项位必填&#xff1b;与输入事件或者选择或者失焦时候 怎么校验无关 &#xff08;这时候的校验取决于是…

npm eject 暴露webpack报错,less或sass添加报错

在使用react时候&#xff0c;添加sass或者less&#xff0c;需要暴露config里的webpack出来&#xff0c;直接npm eject会暴以下错&#xff1b; Usage: npm <command> where <command> is one of: access, adduser, audit, bin, bugs, c, cache, ci, cit, …

Eclipse For JavaSE安装、配置、测试

Eclipse For JavaSE安装、配置、测试(win7_64bit) 目录 1.概述 2.本文用到的工具 3.安装与配置 4.JavaSE开发测试 5.ADT安装与Android开发测试 6.注意事项 7.相关博文 >>看不清的图片可在新标签打开查看大图 1.概述 eclipse应该是Java开发界家喻户晓的IDE了&#xff0c;通…

关于GNS3占用很大CPU的问题,很大可能对你有用

在用GNS3的时候不知道为什么&#xff0c;CPU居高不下&#xff0c;占据100%&#xff0c;拼命计算IDLE值&#xff0c;甚至使用上了BES软件&#xff0c;都没用&#xff0c;后来在51CTO看到有个朋友回帖说是更改一下RAM应该可以。我发现GNS3给每台设备默认是分配128M&#xff0c;于…

Typescript学习;Typescript总结;Typescript 的数据类型有哪些?

推荐一个学习Typescript的网址&#xff0c;写的特别清晰易懂&#xff0c;比官网好理解&#xff1a;Typescript文档整理 一个总结&#xff1a;Typescript 的数据类型有哪些

epoll的两种模式

From: http://haoningabc.iteye.com/blog/1432958 linux异步IO浅析 http://hi.baidu.com/_kouu/blog/item/e225f67b337841f42f73b341.html epoll有两种模式,Edge Triggered(简称ET) 和 Level Triggered(简称LT).在采用这两种模式时要注意的是,如果采用ET模式,那么仅当状态发生…

react 添加less预处理语言

首先是安装react react创建 创建完了&#xff0c;添加react 首先cnpm i less less-loader --save-dev 下载完成后执行&#xff1a;npm run eject&#xff1b;暴露config等一些配置文件 config目录有个webpack配置文件&#xff0c;修改 const cssRegex /\.css$/; 改成 con…

微软公布 Windows Phone 8 多项新特性

多款热门游戏登陆WP8 全新锁屏界面可浏览Facebook图集 数据压缩服务DATA SENSE 鲍尔默宣布WP8终端上市细则 10月30日消息&#xff0c;微软今日凌晨于美国旧金山公布Windows Phone 8诸多新特性&#xff0c;并宣布了诺基亚、HTC以及三星多款WP8终端美国上市日期及运营商定制方式。…

博客地址迁移

博客地址迁移 新的文章不在博客园写了&#xff0c;有兴趣关注本人博客的请移步 https://github.com/jsCoder-yy 另外&#xff0c;旧的博客内容也会逐步迁移到github上。posted on 2016-06-30 14:15 jsCoder_洋洋 阅读(...) 评论(...) 编辑 收藏 转载于:https://www.cnblogs.com…

为什么[]==0;JavaScript里什么情况下a==!a为true呢?

原文链接 JavaScript里什么情况下a!a为true呢&#xff1f; 答案是当a []的时候&#xff0c;这是因为JavaScript的类型转换。 我们先来考虑这个问题&#xff0c;console.log([] false)会打印什么呢&#xff1f;答案是true。为什么呢&#xff1f;首先&#xff0c;因为当&quo…

正向代理与反向代理;

正向代理&#xff1a; A页面访问B页面&#xff0c;如果它们跨域&#xff1b;A还是想拿B的资源&#xff1b; 可以通过代理实现&#xff0c;A把信息发给服务器&#xff0c;这里的服务器就是代理&#xff1b;服务器访问B&#xff0c;再把信息返回给A&#xff1b; 这时B页面不知…

字节对齐测试实例

先看理论&#xff1a; 1. 更改C编译器的缺省字节对齐方式 在缺省情况下&#xff0c;C编译器为每一个变量或是数据单元按其自然对界条件分配空间。一般地&#xff0c;可以通过下面的方法来改变缺省的对界条件&#xff1a; 使用伪指令#pragma pack (n)&#xff0c;C编译器将按…

spring-初始化完成后运行指定内容

方案1:继承ApplicationListener public class InstantiationTracingBeanPostProcessor implements ApplicationListener<ContextRefreshedEvent> { Override public void onApplicationEvent(ContextRefreshedEvent event) { //需要执行的逻辑代码&#xff0c;当spring容…

读取Android系统的多媒体库

android系统有一个数据库表会把系统中的所有多媒体文件信息读入&#xff0c;开机的时候会自动读取&#xff0c;也可以模拟发广播让系统扫描。 1.拿到一个ContentResolver ContentResolver resolver context.getContentResolver(); 2.查询数据库表&#xff0c;返回一个cursor 1…

关闭eslint检验;vue-cli3搭建的vue项目关闭eslint;脚手架3关闭eslint;

本文是解决关闭eslint的问题&#xff1b;如果想要开启eslint和配置&#xff0c;可以查看这篇开启eslint检验&#xff1b; 我们使用vue-cli3脚手架搭建vue项目时候&#xff0c;会默认选中eslint风格代码&#xff1b;如果想要关闭eslint检验&#xff0c;有如下两种方案&#xff…

C++模板类示例

记录下一道简单题目的实现&#xff1a;将一个二维矩阵A转换为B&#xff0c;B[i][j]的值用A中i行的最大值和j列的最小值的平均值为替换。 解题思路很简单&#xff0c;求一下每行的最大值和每列的最小值&#xff0c;分别存起来。接下来就是求平均数了。 为了练习下模板&#xff…

wechat code miniprogram 没有找到可以构建的 NPM 包

首先在小程序根目录打开命令窗口&#xff0c;输入 npm init 创建好package.json文件后输入 npm i bootstrap --production 就ok啦 这里解释下 npm install --production 只安装dependencies而不安装devDependencies。 production 很重要