当前位置: 首页 > news >正文

11. TCP并发网络编程

本文主要介绍TCP并发网络的编程,重点介绍io多路复用的epoll实现

一、TCP/IP 网络通信过程

要完成一个完整的 TCP/IP 网络通信过程,需要使用一系列函数来实现。这些函数包括 bind、listen、accept 和 recv/send 等。下面是它们的配合流程:

  1. 创建套接字(socket):使用 socket 函数创建一个套接字,指定协议族和套接字类型。
  2. 绑定地址(bind):将本地地址绑定到套接字上,使得客户端可以通过该地址访问服务器。
  3. 监听连接请求(listen):将套接字设置为监听状态,并指定最大等待连接数(backlog)。
  4. 接受连接请求(accept):当有客户端发起连接请求时,使用 accept 函数创建新的套接字用于与客户端进行通信。
  5. 读写数据(recv/send):使用新创建的套接字进行数据传输,包括从客户端读取数据和向客户端发送数据。
  6. 关闭连接(close):在通信结束后,需要使用 close 函数关闭套接字以释放资源。

对于第4步的请求有两种处理方式:一线程一请求和epoll方法。

二、io多路复用的epoll 实现

epoll是一种在Linux操作系统中实现高性能I/O多路复用的机制。它可以同时监听多个文件描述符,当其中任何一个文件描述符发生读写等事件时,就会触发相应的回调函数进行处理。
基于 epoll 实现的 TCP 服务端程序的流程如下:

  1. 创建一个监听套接字,使用 socket() 函数创建套接字并设置相关参数(如地址重用等)。

  2. 将监听套接字绑定到本地 IP 地址和端口号,使用 bind() 函数将套接字与指定的地址进行绑定。

  3. 开始监听连接请求,使用 listen() 函数将该套接字标记为被动监听状态,并设置可同时处理的最大连接数。

  4. 创建一个 epoll 实例,使用 epoll_create() 函数创建 epoll 实例,并设置需要监视的事件类型。

  5. 将监听套接字添加到 epoll 实例中,使用 epoll_ctl() 函数向 epoll 实例中添加需要监视的文件描述符及对应事件。其中事件类型一般为 EPOLLIN 表示可读事件或者 EPOLLERR 表示错误事件。

  6. 进入主循环处理客户端请求,使用 epoll_wait() 等待内核通知就绪事件,并获取到就绪的文件描述符列表。然后遍历这个文件描述符列表并根据每个文件描述符对应的事件类型进行相应处理。如果是新连接请求,则调用 accept() 接收该连接,并将其加入 epoll 监听队列;否则,直接读取数据或者关闭连接等操作。

  7. 关闭监听套接字以及已经建立连接的客户端套接字,清理资源并退出程序。

总之,在 epoll 实现的 TCP 服务端程序中,通过使用 epoll 实例,可以同时处理多个客户端连接请求,并且在有新数据到达时能够及时地通知程序进行相应的处理。

另外,epoll还提供了ET(边缘触发)和LT(水平触发)两种工作模式。

  1. 水平触发模式
    在水平触发模式下,如果文件描述符上的事件没有被处理完毕,epoll 会持续通知应用程序该文件描述符上仍有事件待处理。在这种情况下,如果应用程序不及时响应并读取数据,则 epoll 会一直通知应用程序该文件描述符上有数据可读取。
  2. 边沿触发模式
    在边沿触发模式下,只要文件描述符上出现新的事件(例如数据可读或连接建立),epoll 就会通知应用程序。但是,在通知之后,如果应用程序没有立即响应并读取所有数据,则 epoll 不会再次通知该文件描述符上有新的数据可读。

总体来说,边沿触发模式相比于水平触发模式更为高效,并且可以避免由于重复监听导致 CPU 占用率过高的问题。但是,在使用边沿触发模式时需要注意及时读取所有数据,并确保每个事件都得到了正确处理。

并且,与select相比,虽然两者都是Linux下的I/O多路复用机制,但是它们有一些重要的区别:

  • 监听文件描述符数量限制不同:在Linux中,select函数所支持的最大文件描述符数量默认为1024个,而epoll没有这个限制,可以监听成千上万个文件描述符。

  • 文件描述符集合拷贝方式不同:在使用select时,每次调用需要将待监视的所有文件描述符从用户空间拷贝到内核空间,在返回结果之后还需要再将结果从内核空间拷贝回用户空间。这样会带来较大的性能开销。而在使用epoll时,只需将待监视的文件描述符加入一个内核事件表中即可完成注册,当有就绪事件发生时直接通知应用程序进行处理。

  • 对于非阻塞套接字处理方式不同:在使用select时,对于非阻塞套接字我们需要手动设置为非阻塞模式并且轮询读写操作是否就绪。而epoll则通过设置EPOLLET标志位实现了边缘触发模式,并且对于非阻塞套接字只需要等待其返回EAGAIN错误码即可知道其已经处于非阻塞状态。

总体来说,与select相比,epoll具有更高效、更灵活、更易扩展等优点,在处理大量并发连接时具有更好的性能和可扩展性。


#include <stdio.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>// #include <winsock2.h>
// #include <mswsock.h>
// #include <windows.h>
// #include <sys/types.h>  
// #include <unistd.h>
// #include <fcntl.h>#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <errno.h>
#include <fcntl.h> 
#include <unistd.h> 
#include <sys/epoll.h>#define BUFFER_LENGTH       1024
#define EPOLL_SIZE          1024void *client_routine(void *arg){int clientfd=*(int *)arg;while (1){char buffer[BUFFER_LENGTH]={0};int len=recv(clientfd,buffer,BUFFER_LENGTH,0);if (len < 0){//非阻塞状态下读到空数据close(clientfd);break;}else if(len == 0) {//断开连接close(clientfd);break;}else{printf("Recv: %s, %d btye(s)\n",buffer,len);}}
}int main(int argc,char *argv[]){if (argc < 2) {printf("Param Error \n");return -1;}int port=atoi(argv[1]);//atoi将一个字符串转换为对应的整数值int sockfd=socket(AF_INET,SOCK_STREAM,0);struct sockaddr_in addr;memset(&addr,0,sizeof(struct sockaddr_in));addr.sin_family=AF_INET;addr.sin_port=htons(port);addr.sin_addr.s_addr=INADDR_ANY;if (bind(sockfd,(struct sockaddr*)&addr,sizeof(struct sockaddr_in))<0){perror("bind");return -2;}if(listen(sockfd,5)<0){perror("listen");return -3;}#if 0// 一请求一线程while (1){  struct sockaddr_in client_addr;memset(&client_addr,0,sizeof(struct sockaddr_in));socklen_t client_len =sizeof(client_addr);/*调用 accept() 函数后,它会一直阻塞等待直到有新的客户端连接请求到达为止。当有新的连接请求到达时,它会返回一个新产生的套接字文件描述符,并且将该连接对应的客户端地址信息存储在 addr 指向的结构体中*/int clientfd=accept(sockfd,(struct sockaddr *)&client_addr,&client_len);pthread_t thread_id;pthread_create(&thread_id,NULL,client_routine,&clientfd);}#else     /*使用epoll的基本流程如下:1,创建一个epoll实例,可以通过调用 epoll_create() 函数来创建。2,向 epoll 实例中添加需要监控的文件描述符及其事件类型,可以通过调用 epoll_ctl() 函数进行操作。3,调用 epoll_wait() 函数等待监控对象上发生事件,并处理活跃的文件描述符及其事件类型。4,处理完活跃文件描述符的相关操作后,返回到第三步继续等待新的事件发生。*/int epfd=epoll_create(1);   struct epoll_event events[EPOLL_SIZE] = {0};    //创建一个结构体数组 events 用于存储 epoll_wait() 返回的事件列表。struct epoll_event ev;  ev.events=EPOLLIN;  //创建一个新的 epoll_event 结构体 ev 并设置其关注的事件类型为 EPOLLIN (表示等待读事件)ev.data.fd = sockfd;epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&ev);   //使用 epoll_ctl() 函数将 sockfd 文件描述符加入到 epfd 实例中,并关联上面创建的 ev 结构体。while (1){int nready=epoll_wait(epfd,events,EPOLL_SIZE,5); //if (nready == -1) continue; //表示5秒内,没有事件,继续监听int i=0;for (i=0;i<nready;i++){/*判断当前事件所对应的文件描述符是否为监听套接字 sockfd。如果是,则说明有新的客户端连接请求到来了,需要通过 accept() 函数获取新产生的客户端连接并添加到 epoll 实例中;否则,说明是已经建立好连接的客户端发送了数据,需要通过 recv() 函数接收数据并进行相应处理。*/if(events[i].data.fd == sockfd){/*当有新的连接请求到来时(即 sockfd 上有 EPOLLIN 事件),使用 accept() 函数接受连接,并将其加入 epoll 实例中关注该套接字上是否有输入事件。*/struct sockaddr_in client_addr;memset(&client_addr,0,sizeof(struct sockaddr_in));socklen_t client_len =sizeof(client_addr);int clientfd=accept(sockfd,(struct sockaddr *)&client_addr,&client_len);ev.events=EPOLLIN | EPOLLET;  //EPOLLET 则表示将 I/O 事件设置为边缘触发模式。ev.data.fd=clientfd;epoll_ctl(epfd,EPOLL_CTL_ADD,clientfd,&ev);}else{//当某个客户端套接字上出现可读事件时(即该文件描述符在 events 中对应的元素有 EPOLLIN 标志),则调用 recv() 函数从该套接字中读取数据int clientfd=events[i].data.fd;char buffer[BUFFER_LENGTH]={0};int len=recv(clientfd,buffer,BUFFER_LENGTH,0);if (len < 0){//出现了异常情况或者非阻塞状态下没有更多数据可读//关闭该套接字并将其从 epoll 实例中删除close(clientfd);ev.events=EPOLLIN;  ev.data.fd=clientfd;epoll_ctl(epfd,EPOLL_CTL_DEL,clientfd,&ev); //从 epoll 实例中删除 clientfd 对应的文件描述符,并且停止监听该套接字上的事件。}else if(len == 0) {//对方已经断开连接//关闭该套接字并将其从 epoll 实例中删除close(clientfd);ev.events=EPOLLIN;  ev.data.fd=clientfd;epoll_ctl(epfd,EPOLL_CTL_DEL,clientfd,&ev);}else{printf("Recv: %s, %d btye(s)\n",buffer,len);}}}}#endifreturn 0;
}

相关文章:

11. TCP并发网络编程

本文主要介绍TCP并发网络的编程&#xff0c;重点介绍io多路复用的epoll实现 一、TCP/IP 网络通信过程 要完成一个完整的 TCP/IP 网络通信过程&#xff0c;需要使用一系列函数来实现。这些函数包括 bind、listen、accept 和 recv/send 等。下面是它们的配合流程&#xff1a; 创…...

[GUET-CTF2019]number_game[数独]

目录 题目 学到的知识点&#xff1a; 题目 在buu上看到了一道数独题&#xff0c;没见过&#xff0c;记录一下 下载附件&#xff0c;查壳&#xff0c;无壳&#xff0c;在IDA中打开&#xff0c;直接找到主函数 unsigned __int64 __fastcall main(int a1, char **a2, char **a3…...

探索可视化大屏:引领信息时代的视觉革命

可视化大屏是一种利用先进的数据可视化技术和交互技术&#xff0c;将大量的数据和信息以直观、易于理解的方式展示在大屏幕上的解决方案。可视化大屏通常由高分辨率的显示屏、强大的计算和处理设备以及专业的可视化软件组成&#xff0c;它通过图表、图形、动画等可视化元素&…...

Groovy学习笔记-2.Groovy相关基础信息

更多代码相关的内容可以参考&#xff1a;https://github.com/zclhit/groovy_learning/tree/main 代码结构 注释 #!注释&#xff0c;只允许出现在groovy脚本的第一行&#xff0c;通过这种注释可以方便Unix shell进行定位启动并运行 //单行注释 /* xxxxx */多行注释 /** xxxxx…...

android 12.0Settings去掉二级三级菜单搜索功能

1.概述 在12.0由于客户定制开发需求,需要去掉Settings里面的搜索功能,主页面的搜索功能,在前面的章节已经讲了 这里需要去掉二级三级菜单的搜索功能,需要从搜索功能流程分析去掉搜索功能 2.Settings去掉二级三级菜单搜索功能核心代码 packages/apps/Settings/src/com/and…...

【业务功能篇03】Springboot+POI 带图片的导出Excel

继前面介绍的 Springboot+mybatis-plus+POI实现表单数据导出Excel 这篇实现功能介绍,后期业务又有新的导出需求,就是在导出表单数据的同时,在表单下面位置,放入对应一个业务的图片数据,这些图片数据,就是结合表格的数据,在前端的表格下面的位置展示的,比如针对时间-数量…...

“智慧赋能 强链塑链”——打造电力特色智慧供应链体系

构建业务数智化、资源集约化、运营一体化、发展绿色化的智慧供应链体系&#xff0c;是电力企业实现智慧供应链建设的使命和目标。同时&#xff0c;在国内外双循环、一带一路、建立统一大市场的政策背景推动下&#xff0c;企业经营和居民生活对电力的需求仍然强劲并持续增长&…...

服了呀,被现在的00后卷麻了....

现在的小年轻真的卷得过分了。前段时间我们公司来了个00年的&#xff0c;工作没两年&#xff0c;跳槽到我们公司起薪18K&#xff0c;都快接近我了。后来才知道人家是个卷王&#xff0c;从早干到晚就差搬张床到工位睡觉了。 最近和他聊了一次天&#xff0c;原来这位小老弟家里条…...

带你开发一个远程控制项目---->STM32+标准库+阿里云平台+传感器模块+远程显示-------之 阿里云平台项目建造。

第一篇章&#xff1a; (13条消息) 带你开发一个远程控制项目----&#xff1e;STM32标准库阿里云平台传感器模块远程显示。_海口飞鹏岛科技有限公司的博客-CSDN博客 本次文章是指引开发者进行开发阿里云平台建造设备项目&#xff0c;可观看UP主教程&#xff0c;完成如下&#x…...

MoveIt2中使用trac_ik

文章目录 1.下载trac_ik的源码2.安装 NLopt library3.编译源码4.使用4.1.已经配置好的项目4.2.新使用moveIt_setup_assistant进行配置时 在ros1moveit1中&#xff0c;使用trac_ik是很简单的一件事情&#xff1a;【TRAC-IK Kinematics Solver】 但是在Ros2中&#xff0c;无论Mov…...

搭建服务器的主流中间件有哪些?如何在外网访问内网的服务?

计算机业内人士对于搭建服务器的中间件并不陌生&#xff0c;apache、tomcat、IIS、nginx 都是比较常用的搭建服务器的中间件&#xff0c;它们之间还是有一些区别差异的。今天就说说这些中间件之间有哪些区别&#xff0c;以及如何利用快解析实现内网主机应用让外网访问。 首先说…...

MapperFacade使用

一、MapperFacade是Orika框架中的一个核心类&#xff0c;它用于管理对象映射。使用MapperFacade可以方便地将一个对象转换为另一个对象。以下是使用MapperFacade的基本步骤&#xff1a; 创建一个MapperFactory对象。 使用MapperFactory对象注册对象之间的映射关系。 调用Mapp…...

@开源爱好者,字节跳动这项技术,正式宣布开源了

告诉大家一个好消息&#xff0c;字节跳动的云原生数据仓库 ByConity 正式宣布开源了。 ByConity 是一个云原生数据仓库&#xff0c;由字节跳动数据平台团队在国际知名开源数据库管理系统 ClickHouse 社区版本基础上开发。 早期&#xff0c;字节跳动的数据存储使用的是 ClickHou…...

React学习笔记八-受控与非受控组件

此文章是本人在学习React的时候&#xff0c;写下的学习笔记&#xff0c;在此纪录和分享。此为第八篇&#xff0c;主要介绍非受控组件与受控组件。 目录 1.非受控组件 1.1表单提交案例 1.2案例的总结 2.受控组件 2.1受控组件案例 2.1受控案例总结 1.非受控组件 1.1表单提…...

gcc编译

一、GCC简介 GCC&#xff08;GNU Compiler Collection&#xff09;是 GNU 工具链的主要组成部分&#xff0c;是一套以 GPL 和 LGPL 许可证发布的程序语言编译器自由软件&#xff0c;由 Richard Stallman 于 1985 年开始开发。 GCC 原名为 GNU C语言编译器&#xff0c;因为它原本…...

华为云服务器租用费用及CPU性能(1核2G/2核4G/4核8G)

华为云HECS云服务器即云耀云服务器&#xff0c;类似于阿里云和腾讯云的轻量应用服务器&#xff0c;HECS云服务器1核2G配置39.02元一年、2核4G配置99元一年、4核8G配置69.94元3个月&#xff0c;华为云百科分享华为云HECS云服务器租用费用及CPU性能详解&#xff1a; 目录 华为云…...

Redis---事务管道

目录 一、Redis的事务是什么&#xff1f; 1.1 Redis和关系型数据库事务的区别 二、怎么玩Redis事务&#xff1f; 2.1 正常执行&#xff1a; 2.2 放弃事务 2.3 全体连坐 2.4 冤头债主 2.5 watch监控 三、管道 3.1 为什么会引入管道这个概念呢&#xff1f;我们首先来看一…...

Python的内置数据类型(通过故事来学习)

有一天&#xff0c;小李在学习Python语言时&#xff0c;听到了一个关于内置数据类型的故事。 Python内置了很多数据类型&#xff0c;比如整数、浮点数、字符串、布尔值等等。这些数据类型可以帮助我们更方便地处理数据&#xff0c;提高代码的效率。 小李很好奇&#xff0c;就…...

继瑞吉外卖后的又一个项目——SpringBoot+Vue的前后端博客系统

文章目录 博客系统项目介绍前言项目演示前台演示后台演示 组织结构后端组织结构前端组织结构 技术选型前端技术后端技术架构图系统架构图业务架构图 模块介绍前端模块后端模块 环境搭建开发工具开发环境项目运行 未完待续结语 博客系统项目介绍 前言 本项目已开源在Gitee 后端…...

2023暑期实习历程总结

一.前言 Hello 大家好久不见&#xff0c;已经三个月左右没有更新了&#xff0c;那我这三个月在干什么呢&#xff1f;自2023年3月中旬开始到现在五月底这期间接近三个月的时间里&#xff0c;我一直在进行2023暑期实习的投递和面试。这期间投递了包括各大中厂&#xff08;阿里&am…...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql

智慧工地管理云平台系统&#xff0c;智慧工地全套源码&#xff0c;java版智慧工地源码&#xff0c;支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求&#xff0c;提供“平台网络终端”的整体解决方案&#xff0c;提供劳务管理、视频管理、智能监测、绿色施工、安全管…...

pam_env.so模块配置解析

在PAM&#xff08;Pluggable Authentication Modules&#xff09;配置中&#xff0c; /etc/pam.d/su 文件相关配置含义如下&#xff1a; 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块&#xff0c;负责验证用户身份&am…...

Linux简单的操作

ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...

MVC 数据库

MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...

macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用

文章目录 问题现象问题原因解决办法 问题现象 macOS启动台&#xff08;Launchpad&#xff09;多出来了&#xff1a;Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显&#xff0c;都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:

根据万维钢精英日课6的内容&#xff0c;使用AI&#xff08;2025&#xff09;可以参考以下方法&#xff1a; 四个洞见 模型已经比人聪明&#xff1a;以ChatGPT o3为代表的AI非常强大&#xff0c;能运用高级理论解释道理、引用最新学术论文&#xff0c;生成对顶尖科学家都有用的…...

Java多线程实现之Thread类深度解析

Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

C++.OpenGL (14/64)多光源(Multiple Lights)

多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...

深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用

文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么&#xff1f;1.1.2 感知机的工作原理 1.2 感知机的简单应用&#xff1a;基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...

【Linux】自动化构建-Make/Makefile

前言 上文我们讲到了Linux中的编译器gcc/g 【Linux】编译器gcc/g及其库的详细介绍-CSDN博客 本来我们将一个对于编译来说很重要的工具&#xff1a;make/makfile 1.背景 在一个工程中源文件不计其数&#xff0c;其按类型、功能、模块分别放在若干个目录中&#xff0c;mak…...