linux网络编程(五)epoll进阶

简介: linux网络编程(五)epoll进阶

1. 事件模型


EPOLL事件有两种模型:

Edge Triggered (ET) 边缘触发只有数据到来才触发,不管缓存区中是否还有数据。

Light Triggered (LT) 水平触发只要有数据都会触发。


思考如下步骤:

1.假定我们已经把一个用来从管道中读取数据的文件描述符(rfd)添加到epoll描述符。

2.管道的另一端写入了2KB的数据

3.调用epoll_wait,并且它会返回rfd,说明它已经准备好读取操作

4.读取1KB的数据

5.调用epoll_wait…… 在这个过程中,有两种工作模式:


1.1 ET模式


ET模式即Edge Triggered工作模式。


如果我们在第1步将rfd添加到epoll描述符的时候使用了EPOLLET标志,那么在第5步调用epoll_wait之后将有可能会挂起,因为剩余的数据还存在于文件的输入缓冲区内,而且数据发出端还在等待一个针对已经发出数据的反馈信息。只有在监视的文件句柄上发生了某个事件的时候ET工作模式才会汇报事件。因此在第5步的时候,调用者可能会放弃等待仍在存在于文件输入缓冲区内的剩余数据。epoll工作在ET模式的时候,必须使用非阻塞套接口,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。最好以下面的方式调用ET模式的epoll接口,在后面会介绍避免可能的缺陷。


1)基于非阻塞文件句柄

2)只有当read或者write返回EAGAIN(非阻塞读,暂时无数据)时才需要挂起、等待。但这并不是说每次read时都需要循环读,直到读到产生一个EAGAIN才认为此次事件处理完成,当read返回的读到的数据长度小于请求的数据长度时,就可以确定此时缓冲中已没有数据了,也就可以认为此事读事件已处理完成。


1.2 LT模式


LT模式即Level Triggered工作模式。


与ET模式不同的是,以LT方式调用epoll接口的时候,它就相当于一个速度比较快的poll,无论后面的数据是否被使用。


1.3 比较


LT(level triggered):LT是缺省的工作方式,并且同时支持block和no-block socket。在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表。


ET(edge-triggered):ET是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知。请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once).


2. 实例一


基于管道epoll ET触发模式

#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <errno.h>
#include <unistd.h>
#define MAXLINE 10
int main(int argc, char *argv[])
{
  int efd, i;
  int pfd[2];
  pid_t pid;
  char buf[MAXLINE], ch = 'a';
  pipe(pfd);
  pid = fork();
  if (pid == 0) {
  close(pfd[0]);
  while (1) {
    for (i = 0; i < MAXLINE/2; i++)
    buf[i] = ch;
    buf[i-1] = '\n';
    ch++;
    for (; i < MAXLINE; i++)
    buf[i] = ch;
    buf[i-1] = '\n';
    ch++;
    write(pfd[1], buf, sizeof(buf));
    sleep(2);
  }
  close(pfd[1]);
  } else if (pid > 0) {
  struct epoll_event event;
  struct epoll_event resevent[10];
  int res, len;
  close(pfd[1]);
  efd = epoll_create(10);
  /* event.events = EPOLLIN; */
  event.events = EPOLLIN | EPOLLET;  /* ET 边沿触发 ,默认是水平触发 */
  event.data.fd = pfd[0];
  epoll_ctl(efd, EPOLL_CTL_ADD, pfd[0], &event);
  while (1) {
    res = epoll_wait(efd, resevent, 10, -1);
    printf("res %d\n", res);
    if (resevent[0].data.fd == pfd[0]) {
    len = read(pfd[0], buf, MAXLINE/2);
    write(STDOUT_FILENO, buf, len);
    }
  }
  close(pfd[0]);
  close(efd);
  } else {
  perror("fork");
  exit(-1);
  }
  return 0;
}


3. 实例二


基于网络C/S模型的epoll ET触发模式

server
/* server.c */
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/epoll.h>
#include <unistd.h>
#define MAXLINE 10
#define SERV_PORT 8080
int main(void)
{
  struct sockaddr_in servaddr, cliaddr;
  socklen_t cliaddr_len;
  int listenfd, connfd;
  char buf[MAXLINE];
  char str[INET_ADDRSTRLEN];
  int i, efd;
  listenfd = socket(AF_INET, SOCK_STREAM, 0);
  bzero(&servaddr, sizeof(servaddr));
  servaddr.sin_family = AF_INET;
  servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
  servaddr.sin_port = htons(SERV_PORT);
  bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
  listen(listenfd, 20);
  struct epoll_event event;
  struct epoll_event resevent[10];
  int res, len;
  efd = epoll_create(10);
  event.events = EPOLLIN | EPOLLET;  /* ET 边沿触发 ,默认是水平触发 */
  printf("Accepting connections ...\n");
  cliaddr_len = sizeof(cliaddr);
  connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);
  printf("received from %s at PORT %d\n",
    inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
    ntohs(cliaddr.sin_port));
  event.data.fd = connfd;
  epoll_ctl(efd, EPOLL_CTL_ADD, connfd, &event);
  while (1) {
  res = epoll_wait(efd, resevent, 10, -1);
  printf("res %d\n", res);
  if (resevent[0].data.fd == connfd) {
    len = read(connfd, buf, MAXLINE/2);
    write(STDOUT_FILENO, buf, len);
  }
  }
  return 0;
}
client
/* client.c */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#define MAXLINE 10
#define SERV_PORT 8080
int main(int argc, char *argv[])
{
  struct sockaddr_in servaddr;
  char buf[MAXLINE];
  int sockfd, i;
  char ch = 'a';
  sockfd = socket(AF_INET, SOCK_STREAM, 0);
  bzero(&servaddr, sizeof(servaddr));
  servaddr.sin_family = AF_INET;
  inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
  servaddr.sin_port = htons(SERV_PORT);
  connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
  while (1) {
  for (i = 0; i < MAXLINE/2; i++)
    buf[i] = ch;
  buf[i-1] = '\n';
  ch++;
  for (; i < MAXLINE; i++)
    buf[i] = ch;
  buf[i-1] = '\n';
  ch++;
  write(sockfd, buf, sizeof(buf));
  sleep(10);
  }
  Close(sockfd);
  return 0;
}


4.实例三


基于网络C/S非阻塞模型的epoll ET触发模式

server
/* server.c */
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <fcntl.h>
#define MAXLINE 10
#define SERV_PORT 8080
int main(void)
{
  struct sockaddr_in servaddr, cliaddr;
  socklen_t cliaddr_len;
  int listenfd, connfd;
  char buf[MAXLINE];
  char str[INET_ADDRSTRLEN];
  int i, efd, flag;
  listenfd = socket(AF_INET, SOCK_STREAM, 0);
  bzero(&servaddr, sizeof(servaddr));
  servaddr.sin_family = AF_INET;
  servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
  servaddr.sin_port = htons(SERV_PORT);
  bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
  listen(listenfd, 20);
  struct epoll_event event;
  struct epoll_event resevent[10];
  int res, len;
  efd = epoll_create(10);
  /* event.events = EPOLLIN; */
  event.events = EPOLLIN | EPOLLET;  /* ET 边沿触发 ,默认是水平触发 */
  printf("Accepting connections ...\n");
  cliaddr_len = sizeof(cliaddr);
  connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);
  printf("received from %s at PORT %d\n",
    inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
    ntohs(cliaddr.sin_port));
  flag = fcntl(connfd, F_GETFL);
  flag |= O_NONBLOCK;
  fcntl(connfd, F_SETFL, flag);
  event.data.fd = connfd;
  epoll_ctl(efd, EPOLL_CTL_ADD, connfd, &event);
  while (1) {
  printf("epoll_wait begin\n");
  res = epoll_wait(efd, resevent, 10, -1);
  printf("epoll_wait end res %d\n", res);
  if (resevent[0].data.fd == connfd) {
    while ((len = read(connfd, buf, MAXLINE/2)) > 0)
    write(STDOUT_FILENO, buf, len);
  }
  }
  return 0;
}


client
/* client.c */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#define MAXLINE 10
#define SERV_PORT 8080
int main(int argc, char *argv[])
{
  struct sockaddr_in servaddr;
  char buf[MAXLINE];
  int sockfd, i;
  char ch = 'a';
  sockfd = socket(AF_INET, SOCK_STREAM, 0);
  bzero(&servaddr, sizeof(servaddr));
  servaddr.sin_family = AF_INET;
  inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
  servaddr.sin_port = htons(SERV_PORT);
  connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
  while (1) {
  for (i = 0; i < MAXLINE/2; i++)
    buf[i] = ch;
  buf[i-1] = '\n';
  ch++;
  for (; i < MAXLINE; i++)
    buf[i] = ch;
  buf[i-1] = '\n';
  ch++;
  write(sockfd, buf, sizeof(buf));
  sleep(10);
  }
  Close(sockfd);
  return 0;
}
相关文章
|
6月前
|
安全 Linux 网络安全
Nipper 3.9.0 for Windows & Linux - 网络设备漏洞评估
Nipper 3.9.0 for Windows & Linux - 网络设备漏洞评估
189 0
Nipper 3.9.0 for Windows & Linux - 网络设备漏洞评估
|
7月前
|
运维 Linux 开发者
Linux系统中使用Python的ping3库进行网络连通性测试
以上步骤展示了如何利用 Python 的 `ping3` 库来检测网络连通性,并且提供了基本错误处理方法以确保程序能够优雅地处理各种意外情形。通过简洁明快、易读易懂、实操性强等特点使得该方法非常适合开发者或系统管理员快速集成至自动化工具链之内进行日常运维任务之需求满足。
478 18
|
7月前
|
网络协议 关系型数据库 Linux
【App Service Linux】在Linux App Service中安装 tcpdump 并抓取网络包
在App Service for Linux环境中,无法像Windows一样直接使用网络排查工具抓包。本文介绍了如何通过TCPDUMP在Linux环境下抓取网络包,包括SSH进入容器、安装tcpdump、执行抓包命令及下载分析文件的完整操作步骤。
367 5
|
8月前
|
Web App开发 网络协议 Linux
【Linux】网络基础
TCP/IP五层模型是网络通信的基础框架,将复杂的数据传输过程分为物理层、数据链路层、网络层、传输层和应用层,每层各司其职,协同完成远程通信。该模型确保了不同设备和网络之间的互联互通,是现代互联网运行的核心机制。
800 5
|
10月前
|
安全 网络协议 Linux
Linux网络应用层协议展示:HTTP与HTTPS
此外,必须注意,从HTTP迁移到HTTPS是一项重要且必要的任务,因为这不仅关乎用户信息的安全,也有利于你的网站评级和粉丝的信心。在网络世界中,信息的安全就是一切,选择HTTPS,让您的网站更加安全,使您的用户满意,也使您感到满意。
289 18
|
8月前
|
网络协议 Linux 开发者
深入Linux中UDP网络通信机制编程探索
以上步骤概述了Linux中UDP网络通信的编程机制。在实现时,因关注细节和上下文环境可能有所调整,但大致流程是一致的。这些知识片段旨在帮助开发者快速上手Linux下的UDP编程,并提供可靠的信息作为编程的基础。在编程实践中,应结合实际业务需求,设计合适的数据传输协议,确保数据的正确性和实时性。
199 0
|
10月前
|
Linux 数据安全/隐私保护
使用Linux命令行接入无线网络Wi-Fi的示例。
现在,你已经使用命令行成功地连接到 Wi-Fi 网络了。这两个示例涵盖了用 `nmcli` 和 `wpa_supplicant` 连接无线网络的常见场景,让你能够不依赖图形化界面来完成这个任务。在日常使用中熟练掌握这些基本操作能增强你对 Linux 系统的理解,帮助你更有效地处理各种问题。
812 12
|
10月前
|
安全 Ubuntu Linux
Nipper 3.8.0 for Windows & Linux - 网络设备漏洞评估
Nipper 3.8.0 for Windows & Linux - 网络设备漏洞评估
415 0
Nipper 3.8.0 for Windows & Linux - 网络设备漏洞评估
|
12月前
|
Ubuntu Linux
Linux系统管理:服务器时间与网络时间同步技巧。
以上就是在Linux服务器上设置时间同步的方式。然而,要正确运用这些知识,需要理解其背后的工作原理:服务器根据网络中的其他机器的时间进行校对,逐步地精确自己的系统时间,就像一只犹豫不决的啮齿动物,通过观察其他啮齿动物的行为,逐渐确定自己的行为逻辑,既简单,又有趣。最后希望这个过程既能给你带来乐趣,也能提高你作为系统管理员的专业素养。
1941 20
|
12月前
|
JSON 运维 Ubuntu
Linux下如何使用Curl进行网络请求
希望这篇文章能帮助您在Linux下更好地使用Curl进行网络请求。如有疑问,请随时提问!
663 10