我们通过实现分析知道LT模式下epoll_wait被唤醒可以通过两种方式,而ET模式只能通过一种方式。所以ET模式下能被唤醒的情况,LT模式下一定也能被唤醒。我们先来讨论特殊情况(ET模式),再来讨论一般情况(LT模式)。

1 ET

根据上一节对两种加入rdlist途径的分析,可以得出ET模式下被唤醒(返回就绪)的条件为:

  • 对于读取操作:

(1) 当buffer由不可读状态变为可读的时候,即由空变为不空的时候。

(2) 当有新数据到达时,即buffer中的待读内容变多的时候。

另外补充一点:

(3) 当buffer中有数据可读(即buffer不空)且用户对相应fd进行epoll_mod IN事件时(具体见下节内容)。

对于情况(1)(2)分别对应图1(a),图1(b)。

  • 对于写操作:

(1) 当buffer由不可写变为可写的时候,即由满状态变为不满状态的时候。

(2) 当有旧数据被发送走时,即buffer中待写的内容变少得时候。

另外补充一点:

(3) 当buffer中有可写空间(即buffer不满)且用户对相应fd进行epoll_mod OUT事件时(具体见下节内容)。

对于情况(1)(2)分别对应图2(a),图2(b)。

   

(a)                                                                                                       (b)

图1 ET读触发的两种情况

图2 LT写触发的两种情况

2 LT

LT模式下进程被唤醒(描述符就绪)的条件就简单多了,它包含ET模式的所有条件,也就是上述列出的六中读写被唤醒的条件都是用于LT模式。此外,还有更普通的情况LT可以被唤醒,而ET则不理会,这也是我们需要注意的情况。

  • 对于读操作

当buffer中有数据,且数据被读出一部分后buffer还不空的时候,即buffer中的内容减少的时候,LT模式返回读就绪。如下图所示。

  • 对于写操作

当buffer不满,又写了一部分数据后扔然不满的的时候,即由于写操作的速度大于发送速度造成buffer中的内容增多的时候,LT模式会返回就绪。如下图所示。

注:poll和select都是LT模式。

3 源码分析

服务端:

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <poll.h>
#include <iostream>
#include <string.h>
#include <vector>
#include <errno.h>
#include <iostream>
#include <stdio.h>using namespace std;#define MAX_EVENT_NUMBER 1024
#define BUFFER_SIZE      200
#define PORT 8080int setnonblocking(int fd)
{int old_option = fcntl(fd, F_GETFL);int new_option = old_option | O_NONBLOCK;fcntl(fd, F_SETFL, new_option);return old_option;
}void addfd(int epollfd, int fd, bool enable_et)
{epoll_event event;event.data.fd = fd;event.events = EPOLLIN;if (enable_et){event.events |= EPOLLET;}epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);setnonblocking(fd);
}void lt_handle(epoll_event* events, int number, int epollfd, int listenfd)
{char buf[BUFFER_SIZE];for (int i = 0; i < number; i++){int sockfd = events[i].data.fd;if (sockfd == listenfd){struct sockaddr_in clientaddr;socklen_t clientaddrlen = sizeof(clientaddr);int connfd = accept(listenfd, (struct sockaddr*)&clientaddr, &clientaddrlen);addfd(epollfd, connfd, false);        } else if (events[i].events & EPOLLIN){printf("event trigger once\n");memset(buf, 0, BUFFER_SIZE);int ret = recv(sockfd, buf, BUFFER_SIZE - 1, 0);if (ret <= 0){close(sockfd);continue;}printf("get %d bytes of content: %s\n", ret, buf);}else{printf("something else happened\n");}}
}void et_handle(epoll_event* events, int number, int epollfd, int listenfd)
{char buf[BUFFER_SIZE];char send_buf[BUFFER_SIZE];for (int i = 0; i < number; i++){int sockfd = events[i].data.fd;if (sockfd == listenfd){struct sockaddr_in clientaddr;socklen_t clientaddrlen = sizeof(clientaddr);int connfd = accept(listenfd, (struct sockaddr*)&clientaddr, &clientaddrlen);addfd(epollfd, connfd, false);continue;            } else if (events[i].events & EPOLLIN){printf("---------------------------------\n");printf("event trigger once\n");while (1){memset(buf, 0, BUFFER_SIZE);int ret = recv(sockfd, buf, BUFFER_SIZE - 1, 0);if (ret <= 0){if (errno == EAGAIN || errno == EWOULDBLOCK){printf("read later\n");break;}close(sockfd);break;}else if (ret == 0){close(sockfd);}else{memset(send_buf, 0, BUFFER_SIZE);sprintf(send_buf, "think, the server have accepted the text, the text is :%s \n", buf);printf("get %d bytes of content: %s\n", ret, buf);}}// 回复客户端消息int nwrite, n,data_size = strlen(send_buf);n = data_size;while (n > 0) {nwrite = write(sockfd, send_buf + data_size - n, n);//ET下一直将要写数据写完if (nwrite < n) {if (nwrite == -1 && errno != EAGAIN) {perror("write error");}break;}n -= nwrite;}close(sockfd);}else {}}
}int main()
{//创建一个监听socketint listenfd = socket(AF_INET,SOCK_STREAM,0);if(listenfd == -1){cout << "create listen socket error" << endl;return -1;}//将监听socker设置为非阻塞的int oldSocketFlag = fcntl(listenfd, F_GETFL, 0);int newSocketFlag = oldSocketFlag | O_NONBLOCK;if(fcntl(listenfd,F_SETFL,newSocketFlag)==-1) {close(listenfd);cout << "set listenfd to nonblock error" << endl;return -1;}//初始化服务器地址  struct sockaddr_in bindaddr;bindaddr.sin_family = AF_INET;bindaddr.sin_addr.s_addr = htonl(INADDR_ANY);bindaddr.sin_port = htons(PORT);if(bind(listenfd, (struct sockaddr*)&bindaddr,sizeof(bindaddr))==-1){cout << "bind listen socker error." << endl;close(listenfd);return -1;}//启动监听if(listen(listenfd,SOMAXCONN)==-1){cout << "listen error." << endl;close(listenfd);return -1;}epoll_event events[MAX_EVENT_NUMBER];//创建epollfdint epollfd = epoll_create(1);if(epollfd == -1){cout << "create epollfd error." << endl;close(listenfd);return -1;}addfd(epollfd, listenfd, true);while(true){       int ret = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -1);if(ret < 0){//被信号中断if(errno == EINTR) continue;//出错,退出printf("epoll failed\n");break;}//lt_handle(events, ret, epollfd, listenfd);et_handle(events, ret, epollfd, listenfd);      }close(listenfd);return 0;
}

客户端:

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/shm.h>#define MYPORT  8080
#define BUFFER_SIZE 1024int main()
{///定义sockfdint sock_cli = socket(AF_INET,SOCK_STREAM, 0);///定义sockaddr_instruct sockaddr_in servaddr;memset(&servaddr, 0, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(MYPORT);  ///服务器端口servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");  ///服务器ip///连接服务器,成功返回0,错误返回-1if (connect(sock_cli, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0){perror("connect");exit(1);}char sendbuf[BUFFER_SIZE];char recvbuf[BUFFER_SIZE];while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL){send(sock_cli, sendbuf, strlen(sendbuf),0); ///发送if(strcmp(sendbuf,"exit\n")==0)break;recv(sock_cli, recvbuf, sizeof(recvbuf),0); ///接收fputs(recvbuf, stdout);memset(sendbuf, 0, sizeof(sendbuf));memset(recvbuf, 0, sizeof(recvbuf));}close(sock_cli);return 0;
}

Linux环境SOCKET编程2:epoll分析相关推荐

  1. Linux环境SOCKET编程3:压力测试

    测试方法:使用epoll实现一个通用的服务器压力测试程序. 测试代码: #include <stdlib.h> #include <stdio.h> #include < ...

  2. linux下socket编程和epoll的使用

      这两天在学Linux下的网络编程,于是便看了些关于socket和epoll的资料.   首先介绍socket,socket编程我之前也接触过,不过是在windows下接触的.和windows不同的 ...

  3. Linux环境SOCKET编程5:定时器接口timerfd

    1.概述 timerfd是Linux为用户程序提供的一个定时器接口.这个接口基于文件描述符,通过文件描述符的可读事件进行超时通知,所以能够被用于select/poll的应用场景.timerfd是lin ...

  4. Linux环境SOCKET编程1:套接字

    一.socket运行过程 服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接.在这时如果有个客户端初始化一个Socket,然 ...

  5. Linux下Socket编程

    Linux下Socket编程    网络的Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符.Socket也具有一个类似于打开文件的函数调用Socket(),该函数返回一个整型的S ...

  6. Linux的SOCKET编程 简单演示

    转载:http://blog.csdn.net/hguisu/article/details/7445768/ Linux的SOCKET编程详解 1. 网络中进程之间如何通信 进 程通信的概念最初来源 ...

  7. asp.core api 通过socket和服务器通信发送udp_详解Linux的SOCKET编程

    文章来自于 https://www.zhangshengrong.com/p/9Oabd95XdK/ PHP进阶学习交流QQ群:983229225 本篇文章对Linux的SOCKET编程进行了详细解释 ...

  8. LINUX下Socket编程 函数格式详解

    你需要了解的一些系统调用: socket() bind() connect() listen() accept() send() recv() sendto() recvfrom() close() ...

  9. Linux的SOCKET编程详解

    Linux的SOCKET编程详解 一. 网络中进程之间如何通信 进程通信的概念最初来源于单机系统.由于每个进程都在自己的地址范围内运行,为保证两个相互通信的进 程之间既互不干扰又协调一致工作,操作系统 ...

最新文章

  1. SQL查询 的一些原则
  2. 手持GPS坐标系统的转换与应用
  3. [转]通达信主图指标--13日黄金K线
  4. win10家庭版无法安装mysql_大师处置win10系统家庭版安装MySQL server 5.7.19失败的详细办法...
  5. android httpget 参数,如何在Android中向HTTP GET请求添加参数?
  6. shell 获取指定ip的丢包率
  7. MT2503环境搭建步骤及注意事项
  8. C++实现Vgg19分类器(四)主函数
  9. 产品经理之如何做好演讲
  10. 形式化验证(Formal verification)如何确保完美的智能合同?
  11. GIS招聘 | 青海省省直事业单位(含地信、测绘等专业)
  12. 组播域GNS3与eNSP桥接以及GNS3的使用教程
  13. 黑马粉丝感叹:好可呀,好想要!!【最新福利你还没领?】
  14. 基于JavaScript+css写一个简单的h5动态下雨效果
  15. KEIL MDK中的RO、RW和ZI
  16. java 图像合成加相框_合成走进相框人物照片效果的PS教程
  17. 计算机与计算思维读后感论文,读书笔记——计算思维培养的一点领悟与构想
  18. 无人值守安装 linux 系统
  19. 全景图有什么优势?为什么这么多人在做?
  20. Harbor仓库清理

热门文章

  1. LVS DR模型详解
  2. linux软件中心无法安eclipse,Ubuntu软件中心安装Eclipse无法启动的问题
  3. 不使用border-radius,实现一个可复用的高度和宽度都自适应的圆角矩形
  4. 什么样的博文才能上首页呢?『博客使用技巧』
  5. Linux—MySQL安装配置详解
  6. amd的处理器能兼容idea么_AMD新一代CPU不向下兼容,究竟意味着什么?
  7. Myeclipse 8.5 注册码
  8. spring整合cxf,轻松编写webService客户端、服务端
  9. uboot源码分析(1)uboot 命令解析流程简析
  10. dojo中chart参数