说明

一直听说epoll的饥饿场景,但是从未在实际环境中面对过,那么能不能模拟出来呢?实际的情况是怎样呢?

模拟步骤

  • 基于epoll写一个简单的tcp echo server,将每次read返回的字节数打印出来
  • 模拟一个客户端大量写入
  • 测试其他客户端能否正常返回

Server代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h> #define MAX_EVENTS 1024
#define LISTEN_BACKLOG 10 int epoll_fd;
void do_read(int fd); int main() {
int server_fd, nfds, i;
struct epoll_event event, events[MAX_EVENTS];
struct sockaddr_in server_addr, client_addr;
socklen_t client_addr_len = sizeof(client_addr);
int client_fd; // 创建 socket
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd == -1) {
perror("socket");
return 1;
} // 设置 socket 选项
int opt = 1;
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) {
perror("setsockopt");
close(server_fd);
return 1;
} // 绑定 socket
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(8080);
if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
perror("bind");
close(server_fd);
return 1;
} // 监听 socket
if (listen(server_fd, LISTEN_BACKLOG) == -1) {
perror("listen");
close(server_fd);
return 1;
} // 创建 epoll 实例
epoll_fd = epoll_create1(0);
if (epoll_fd == -1) {
perror("epoll_create1");
close(server_fd);
return 1;
} // 注册服务器 socket
event.events = EPOLLIN;
event.data.fd = server_fd;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &event) == -1) {
perror("epoll_ctl");
close(server_fd);
close(epoll_fd);
return 1;
} printf("Server listening on port 8080...\n"); while (1) {
// 等待事件就绪
nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
if (nfds == -1) {
perror("epoll_wait");
close(server_fd);
close(epoll_fd);
return 1;
} // 处理就绪事件
for (i = 0; i < nfds; i++) {
if (events[i].data.fd == server_fd) {
// 接受新连接
client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_addr_len);
if (client_fd == -1) {
perror("accept");
continue;
} if (fcntl(client_fd , F_SETFL, O_NONBLOCK) == -1) {
perror("fcntl");
close(client_fd);
continue;
}
// 注册客户端 socket
event.events = EPOLLIN;
event.data.fd = client_fd;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &event) == -1) {
perror("epoll_ctl");
close(client_fd);
continue;
} printf("New connection from %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
} else {
do_read(events[i].data.fd);
}
}
} close(server_fd);
close(epoll_fd);
return 0;
} void do_read(int fd) {
// 处理客户端数据
char buf[1024];
while(1) {
ssize_t bytes_read = read(fd, buf, sizeof(buf));
if (bytes_read == -1) {
perror("read");
close(fd);
if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL) == -1) {
perror("epoll_ctl");
}
break;
} else if (bytes_read == 0) {
printf("Client disconnected\n");
close(fd);
if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL) == -1) {
perror("epoll_ctl");
}
break;
} else {
printf("Received data: %d\n", bytes_read);
if (write(fd, buf, bytes_read) != bytes_read) {
perror("write");
close(fd);
if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL) == -1) {
perror("epoll_ctl");
}
break;
}
if (bytes_read < 1024) {
break;
}
}
}
}

模拟客户端

客户端1:大量写入客户端:

cat /dev/random 2>/dev/null | nc 127.0.0.1 8080 >/dev/null

客户端2:其他写入客户端,少量写入检查返回值

nc 127.0.0.1 8080

模拟结果

  • server端收到大量的数据,每次read返回1024个字节,句柄非常忙碌

  • 客户端2往server发送的数据一直没有返回【处于饥饿状态】

  • 一旦客户端1断开,客户端2就收到回复了

结果分析

从代码中可以知道,read一直都有数据读取,一直在处理数据,导致其他句柄无法处理数据。也就是说,其实是我们的代码造成了所谓的饥饿,那么也可以从我们的代码层面上去解决这个问题,思路官方man page中已经提到了,将fd维护一个list,均匀的读写数据即可。

模拟epoll的饥饿场景的更多相关文章

  1. FastAPI(38)- 模拟一个跨域场景

    同源策略 https://www.cnblogs.com/poloyy/p/15345184.html CORS https://www.cnblogs.com/poloyy/p/15345871.h ...

  2. Jmeter中模拟多用户执行多场景操作

    1.其实一个用户组就是一个场景(Thread Group).可以在一个测试计划中进行多个场景的执行,在测试计划下加一个全局的User Defined Variables,在这个里面可以设置执行总数to ...

  3. 使用数据库乐观锁解决高并发秒杀问题,以及如何模拟高并发的场景,CyclicBarrier和CountDownLatch类的用法

    数据库:mysql 数据库的乐观锁:一般通过数据表加version来实现,相对于悲观锁的话,更能省数据库性能,废话不多说,直接看代码 第一步: 建立数据库表: CREATE TABLE `skill_ ...

  4. Dummynet模拟高时延网络场景(Windows7)

    如果安装时出现:my_socket failed 2, cannot talk to kernel module 请查看是否以管理员方式运行,如果是,再判断当前操作系统是否为Win7 64位,如果是, ...

  5. Key/Value之王Memcached初探:三、Memcached解决Session的分布式存储场景的应用

    一.高可用的Session服务器场景简介 1.1 应用服务器的无状态特性 应用层服务器(这里一般指Web服务器)处理网站应用的业务逻辑,应用的一个最显著的特点是:应用的无状态性. PS:提到无状态特性 ...

  6. 【转】 Key/Value之王Memcached初探:三、Memcached解决Session的分布式存储场景的应用

    一.高可用的Session服务器场景简介 1.1 应用服务器的无状态特性 应用层服务器(这里一般指Web服务器)处理网站应用的业务逻辑,应用的一个最显著的特点是:应用的无状态性. PS:提到无状态特性 ...

  7. [Bullet3]创建世界(场景)及常见函数

    创建世界(场景)及常见函数 官方文档:http://bulletphysics.org 开源代码:https://github.com/bulletphysics/bullet3/releases A ...

  8. SoapUI模拟REST MockService

    一.新建REST工程 二.添加URI 物流查询接口测试地址:http://www.kuaidi100.com/query?type=快递公司代号&postid=快递单号 三.输入入参,测试一下 ...

  9. 消息中间件activemq的使用场景介绍(结合springboot的示例)

    一.消息队列概述 消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题.实现高性能,高可用,可伸缩和最终一致性架构.是大型分布式系统不可缺少的中间件. 目前在生产环境,使 ...

  10. I/O模型系列之五:IO多路复用 select、poll、epoll

    IO多路复用之select.poll.epoll IO多路复用:通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作. 应用:适用于针 ...

随机推荐

  1. MaxCompute管家详解--管家助力,轻松玩转MaxCompute

    精彩视频回顾请点击:MaxCompute管家详解以下是直播内容精华整理,主要包括以下四个方面:1.背景速览:2.功能介绍:3.案例讲解:4.新功能预告. 一.背景速览 MaxCompute(原ODPS ...

  2. MaxCompute 存储设计

    ​ 简介: 存储策略该怎么设计 写这篇存储规划的文章主要是想告诉大家该如何给存储做一个规划,在关系数据库的时代存储昂贵且珍惜,掰手指头花钱是存储规划的常态.但是到了大数据时代大家又立即就都变成印美元的 ...

  3. [FAQ] chrome.runtime.onMessage 问题, Unchecked runtime.lastError: The message port closed before a response was received

    // quasar background-hook.js chrome.runtime.onMessage.addListener(function (request, sender, sendRes ...

  4. 二进制安装多master节点的k8s集群(2)

    1.环境准备 k8s集群角色 IP 主机名 安装的组件 控制节点 192.168.1.10 master apiserver.controller-manager.scheduler.etcd.doc ...

  5. 全网最详细SpringCloud-实用篇

    SpringCloud-实用篇 学习安排 技术分类 1.微服务 ①架构对比 架构 单体架构 分布式架构 描述 将业务的所有功能集中在一个项目中开发,打成一个包部署. 根据业务功能对系统做拆分,每个业务 ...

  6. dotnet build error CS5001: Program does not contain a static 'Main' method suitable for an entry point

    前言 Docker环境编译.Net6项目,出现诡异的CS5001 Program does not contain a static 'Main' method suitable for an ent ...

  7. gprMax项目代码分解:gprMax.py

    目录 1. 引言 2. 代码分解 (1) 导入模块 (2)main()函数 (3)run_main()函数 (4)run_std_sim()函数 (5)run_benchmark_sim()函数 (6 ...

  8. Python——基本输入和输出

    Python提供了基本的输入和输出功能,这些功能通常是通过内置的input()函数(用于输入)和print()函数(用于输出)来实现的.以下是这两个函数的详细描述和示例: 1. print() 函数( ...

  9. SpringBoot-mybatis-plus 分页

    前言: 想必数据分页对于每一个程序员并不陌生,针对分页查询功能代码实现上:肯定是代码简洁明了且能达到分页的效果会更好! 现在我将基于SpringBoot - mybatisPlus分页查询的方法总结如 ...

  10. uniapp中使用极光推送

    1.注册极光账号 2.注册几个主流手机厂商的开发者账号(注册手机厂商,可以保证app进程不在的时候走厂商通道推送消息) 3.配置uniapp极光插件 https://ext.dcloud.net.cn ...