最新文�? TCP之深入浅出send&recv ​linux网络编程:网络_IO_演变发展过程和模型介绍 智能指针-使用、避坑和实现 C++_Boost_多线程并发库 C++多线程编程:解锁性能与并发的奥秘
万字长文详解从socket到epoll高效网络编程 历史版本:
上次修改时间:
同时把数据写入内存;\n\n2:引发CPU中断;\n\n3:CPU执行中断程序;\n\n4:接收对应的socket数据;\n\n5:把处于阻塞队列的进程重新加入执行队列;\n\n 6: **把所有位图从内核态拷贝到用户态。** \n\n **7:在用户态轮询查找对应进程的socket。** \n\n以上所有代码:\n\n\n\n```\nint main()\n{ \n int listensock = initserver(5005);\n printf(\"listensock=%d\\n\",listensock);\n \n if (listensock < 0) { printf(\"initserver() failed.\\n\"); return -1; }\n \n // 读事件:1)已连接队列中有已经准备好的socket(有新的客户端连上来了);\n // 2)接收缓存中有数据可以读(对端发送的报文已到达);\n // 3)tcp连接已断开(对端调用close()函数关闭了连接)。\n // 写事件:发送缓冲区没有满,可以写入数据(可以向对端发送报文)。\n \n fd_set readfds; // 需要监视读事件的socket的集合,大小为16字节(1024位)的bitmap。\n FD_ZERO(&readfds); // 初始化readfds,把bitmap的每一位都置为0。\n FD_SET(listensock,&readfds); // 把服务端用于监听的socket加入readfds。\n \n int maxfd=listensock; // readfds中socket的最大值。\n \n while (true) // 事件循环。\n {\n // 用于表示超时时间的结构体。\n struct timeval timeout; \n timeout.tv_sec=10; // 秒\n timeout.tv_usec=0; // 微秒。\n \n fd_set tmpfds=readfds; // 在select()函数中,会修改bitmap,所以,要把readfds复制一份给tmpfds,再把tmpfds传给select()。\n \n // 调用select() 等待事件的发生(监视哪些socket发生了事件)。\n int infds=select(maxfd+1,&tmpfds,NULL,NULL,0); \n \n // 如果infds<0,表示调用select()失败。\n if (infds<0)\n {\n perror(\"select() failed\"); break;\n }\n \n // 如果infds==0,表示select()超时。\n if (infds==0)\n {\n printf(\"select() timeout.\\n\"); continue;\n }\n \n // 如果infds>0,表示有事件发生,infds存放了已发生事件的个数。\n for (int eventfd=0;eventfd<=maxfd;eventfd++)\n {\n if (FD_ISSET(eventfd,&tmpfds)==0) continue; // 如果eventfd在bitmap中的标志为0,表示它没有事件,continue\n \n // 如果发生事件的是listensock,表示已连接队列中有已经准备好的socket(有新的客户端连上来了)。\n if (eventfd==listensock)\n {\n struct sockaddr_in client;\n socklen_t len = sizeof(client);\n int clientsock = accept(listensock,(struct sockaddr*)&client,&len);\n if (clientsock < 0) { perror(\"accept() failed\"); continue; }\n \n printf (\"accept client(socket=%d) ok.\\n\",clientsock);\n \n FD_SET(clientsock,&readfds); // 把bitmap中新连上来的客户端的标志位置为1。\n \n if (maxfd0;ii--) // 从后面往前找。\n {\n if (FD_ISSET(ii,&readfds))\n {\n maxfd = ii; break;\n }\n }\n }\n }\n else\n {\n // 如果客户端有报文发过来。\n printf(\"recv(eventfd=%d):%s\\n\",eventfd,buffer);\n \n // 把接收到的报文内容原封不动的发回去。\n send(eventfd,buffer,strlen(buffer),0);\n }\n }\n }\n }\n \n return 0;\n}\n```\n\n## poll执行过程详解\n\n### poll函数参数\n\n \n\n \n\n### poll执行流程\n\n总的来说,poll与大体流程select一样,只不过相对于select来说,poll可以接受更高的并发度。主要的我们来看以下代码:\n\n最主要的不同就是poll多了一个结构体来存储相应的文件描述符、要处理的事件以及实际发生的事件。\n\n\n\n```\nstruct\n{\n int fd;\n short events;\n short revents;\n}\n```\n\n首先还是做好初始化的工作:\n\n\n\n```\n int listensock = initserver(atoi(argv[1]));\n printf(\"listensock=%d\\n\",listensock);\n \n if (listensock < 0) { printf(\"initserver() failed.\\n\"); return -1; }\n```\n\n接下面代码:\n\n\n\n```\n pollfd fds[2048]; // fds存放需要监视的socket。\n \n // 初始化数组,把全部的socket设置为-1,如果数组中的socket的值为-1,那么,poll将忽略它。\n for (int ii=0;ii<2048;ii++) \n fds[ii].fd=-1; \n \n // 打算让poll监视listensock读事件。\n fds[listensock].fd=listensock;\n fds[listensock].events=POLLIN; // POLLIN表示读事件,POLLOUT表示写事件。\n // fds[listensock].events=POLLIN|POLLOUT;\n \n int maxfd=listensock; // fds数组中需要监视的socket的实际大小。\n```\n\n首先定义一个fds数组,用于存放我们要监视的socket; **这里fds大小可以自己定义,但是并不是越大越好,当fds大小越大性能方面就会下降。** \n\n **再初始化fds数组,然后把监听客户端连接的socket--listensock加入fds。** \n\n **同时,maxfd指定fds 文件描述符的最大值。主要目的和select相同。** \n\n接下面代码:\n\n\n\n```\n while (true) \n {\n// 调用poll() 等待事件的发生(监视哪些socket发生了事件)。\n int infds=poll(fds,maxfd+1,NULL); \n \n// 如果infds<0,表示调用poll()失败。\n if (infds < 0)\n {\n perror(\"poll() failed\"); break;\n }\n// 如果infds==0,表示poll()超时。\n if (infds == 0)\n {\n printf(\"poll() timeout.\\n\"); continue;\n }\n \n// 如果infds>0,表示有事件发生,infds存放了已发生事件的个数。\n for (int eventfd=0;eventfd<=maxfd;eventfd++)\n {\n if (fds[eventfd].fd<0) continue; // 如果fd为负,忽略它。\n \n if ((fds[eventfd].revents&POLLIN)==0) continue; \n \n// 如果发生事件的是listensock,表示已连接队列中有已经准备好的socket(有新的客户端连上来了)。\n if (eventfd==listensock)\n {\n struct sockaddr_in client;\n socklen_t len = sizeof(client);\n int clientsock = accept(listensock,(struct sockaddr*)&client,&len);\n if (clientsock < 0) { perror(\"accept() failed\"); continue; }\n \n printf (\"accept client(socket=%d) ok.\\n\",clientsock);\n \n // 修改fds数组中clientsock位置的元素。\n fds[clientsock].fd=clientsock;\n fds[clientsock].events=POLLIN;\n \n if (maxfd0;ii--) // 从后面往前找。\n {\n if (fds[ii].fd!=-1)\n {\n maxfd = ii; break;\n }\n }\n }\n }\n else\n {\n // 如果客户端有报文发过来。\n printf(\"recv(eventfd=%d):%s\\n\",eventfd,buffer);\n \n send(eventfd,buffer,strlen(buffer),0);\n }\n }\n }\n }\n```\n\n总体代码和select是一样的,流程也差不多,只不过把用位图该换为了结构体数组来存取监视的socket。流程可参考select。\n\n \n\n \n\n## select与poll小结\n\n总的来说,select与poll大体流程是一样的,但是也有细微差别:\n\n是否需要拷贝到内核是;每次调用都需要拷贝;且需要全部拷贝是;每次调用都需要拷贝;且需要全部拷贝是否有最大文件描述符数限制是 1024否内核实现和工作效率o(n),需要遍历fd集合判断是否有事件发生;以及遍历fd集合确定具体的socketo(n),需要遍历fd结构体数组集合判断是否有事件发生;以及遍历fd结构体数组确定具体的socket工作模式水平触发水平触发 **细微差别主要是poll结构体和select的位图所导致的。** \n\n## epoll执行过程详解\n\n### epoll执行流程\n\n开始先初始化:\n\n\n\n```\nint main()\n{\n \n int listensock = initserver(5005);\n printf(\"listensock=%d\\n\",listensock);\n \n if (listensock < 0) { printf(\"initserver() failed.\\n\"); return -1; }\n```\n\n接下面代码:\n\n\n\n```\n// 创建epoll句柄。\n int epollfd=epoll_create(1);\n \n// 为服务端的listensock准备读事件。\n \n epoll_event ev; // 声明事件的数据结构。\n ev.data.fd=listensock; // 指定事件的自定义数据,会随着epoll_wait()返回的事件一并返回。\n \n ev.events=EPOLLIN; // 打算让epoll监视listensock的读事件。\n \n// 把需要监视的socket和事件加入epollfd中。 \n epoll_ctl(epollfd,EPOLL_CTL_ADD,listensock,&ev); \n \n epoll_event evs[10]; // 存放epoll返回的事件。\n```\n\n首先创建一个epollfd句柄, **即创建一个eventpoll模型,这个其实本质也是一个文件描述符** 。 \n\neventpoll结构体里面的成员很多,我们主要关注其内部的: **rdllist --->就绪队列;rb_root rbt-->红黑树的节点(即创建的eventpoll模型的文件描述符对应的数值);wq-->等待队列。** \n\n在进行下一步之前,我们要了解一些结构体,首先就是epitem,在调用epoll_ctl()的时候,我们会用到这个结构体。 \n\n\n\n```\nepitem\n{\n rbn;\n rdllink;\n next;\n ffd;\n nwait;\n pwqlist;\n ep;\n fllink;\n event;\n}\n```\n\n \n\n我们主要关心:rbn;rdllink;ffd;ep;event。\n\nrbn:是我们文件描述符对应红黑树的节点\n\nrdllink:是已经就绪的文件描述符队列\n\nffd:是文件描述符\n\nep:是eventpoll结构体指针 (当我们拿到了某个事件,我们就知道他是属于哪个eventpoll)\n\n在往下进行下一步的代码之前,我们得要了解epoll大致步骤是如何处理socket的。\n\n \n\n当服务端从网卡接收到数据的时候,会把数据写入内核缓冲区,然后,这个时候会调用epoll_ctl()函数,维护等待队列。 **我们假设A进程对应的文件描述符事件已经到达,** 这个时候,epoll区别于select与poll,他会有一个 **回调机制,这就使得可以不需要轮询去查找到底哪个socket有事件,把o(n)降到了o(1)。** \n\n当找到这个目标socket的对应事件后,会把相应的文件描述符节点加入到就绪队列,同时会等待epoll_wait()的调用。这里注意:并不是真正加到就绪队列,而是我们为了描述方便所以才这么表述, **实际上只是在红黑树改变了节点的指向** 。\n\nepoll_wait()调用该事件的时候,会把 **已经发生的事件节点拷贝回用户态,同时会唤起相对应的阻塞进程** 。这里只是拷贝了一次,而且区别于select、poll **只拷贝已经发生了的事件节点** 。 \n\n了解了epoll的工作方式,我们接下来对于代码的理解就更为清楚了。\n\n继续接上面代码: \n\n\n\n```\nwhile (true) \n {\n// 等待监视的socket有事件发生。\n int infds=epoll_wait(epollfd,evs,10,-1);\n \n // 返回失败。\n if (infds < 0)\n {\n perror(\"epoll() failed\"); break;\n }\n \n // 超时。\n if (infds == 0)\n {\n printf(\"epoll() timeout.\\n\"); continue;\n }\n```\n\n\n\n\n\n\n```\nepoll_wait()\n第一个参数:eventpoll的返回值,也就是eventpoll对应的文件描述符\n第二个参数:把内核已经就绪的事件拷贝到某个数组\n第三个参数:evs数组的元素个数\n第四个参数:超时时间\n```\n\n咱们继续接着聊 ,接上面代码:\n\n\n\n```\n // 如果infds>0,表示有事件发生的socket的数量。\n for (int ii=0;ii0,表示有事件发生的socket的数量。\n for (int ii=0;ii\n\n[https://zhuanlan.zhihu.com/p/10248444882](https://zhuanlan.zhihu.com/p/10248444882)
\n -->
0条评�?
全部评论

关于博主

an actually real engineer

通信工程专业毕业,7年开发经验

技术栈:

精通c/c++

精通golang

熟悉常见的脚本,js,lua,python,php

熟悉电路基础,嵌入式,单片机

耕耘领域:

服务端开发

嵌入式开发

git

>

gin接口代码CURD生成工具

sql ddl to struct and markdown,将sql表自动化生成代码内对应的结构体和markdown表格格式,节省宝贵的时间。

输入ddl:
输出代码:

qt .ui文件转css文件

duilib xml 自动生成绑定控件代码

协议调试器

基于lua虚拟机的的协议调试器软件 支持的协议有:

串口

tcp客户端/服务端

udp 组播/udp节点

tcp websocket 客户端/服务端

软件界面

使用例子: 通过脚本来获得接收到的数据并写入文件和展示在界面上

下载地址和源码

duilib版本源码 qt qml版本源码 二进制包

webrtc easy demo

webrtc c++ native 库 demo 实现功能:

基于QT

webrtc摄像头/桌面捕获功能

opengl渲染/多播放窗格管理

janus meeting room

下载地址和源码

源码 二进制包

wifi,蓝牙 - 无线开关

实现功能:

通过wifi/蓝牙实现远程开关电器或者其他电子设备

电路原理图:

实物图:

深度学习验证工具

vtk+pcl 点云编辑工具

实现功能:

1. 点云文件加载显示(.pcd obj stl)

2. 点云重建

3. 点云三角化

4. 点云缩放

下载地址:

源码 二进制包

虚拟示波器

硬件实物图:

实现原理

基本性能

采集频率: 取决于外部adc模块和ebaz4205矿板的以太网接口速率,最高可以达到100M/8 约为12.5MPS

上位机实现功能: 采集,显示波形,存储wave文件。

参数可运行时配置

上位机:

显示缓冲区大小可调

刷新率可调节

触发显示刷新可调节

进程守护工具

基本功能:

1. 守护进程,被守护程序崩溃后自动重启。

2. 进程输出获取,显示在编辑框中。

二进制包

openblt 烧录工具

基本功能:

1. 加载openblt 文件,下载到具有openblt bootloader 运行的单片机中。

二进制包

opencv 功能验证工具(开源项目二次开发)

基本功能:

1. 插件化图像处理流程,支持自定义图像处理流程。 2. 完善的日志和权限管理

二进制包

又一个modbus调试工具

最近混迹物联网企业,发现目前缺少一个简易可用的modbus调试工具,本软件旨在为开发者提供一个简单modbus测试工具。
主打一个代码简单易修改。
特点:

1. 基于QT5

2. 基于libmodbus

3. 三方库完全跨平台,linux/windows。

二进制包

屏幕录制工具

1. 基于QT5

2. 基于ffmpeg

3. 支持自定义录屏

源代码

开源plutosdr 板卡

1. 完全开源

2. 提高固件定制服务

3. 硬件售价450 手焊产量有线

测试数据

内部DDS回环测试

接收测试

外部发送500MHZ FM波形

硬件原理图

matlab测试

2TRX版本

大部分plutosdr应用场景都是讲plutosdr板卡作为射频收发器来使用。
实际上plutosdr板卡本身运行linux 操作系统。是具有一定脱机运算的能力。 对于一些微型频谱检测,简单射频信号收发等应用完全可以将应用层直接实现在板卡上
相较于通过网卡或者USB口传输具有更稳定,带宽更高等优点。
本开源板卡由于了SD卡启动,较原版pluto支持了自定义启动应用的功能。
提供了应用层开发SDK(编译器,buildroot文件系统)。
通过usb连接电脑,经过RNDIS驱动可以近似为通过网卡连接
(支持固件的开发定制)。

二次开发例子

``` all:
arm-linux-gnueabihf-gcc -mfloat-abi=hard --sysroot=/root/v0.32_2trx/buildroot/output/staging -std=gnu99 -g -o pluto_stream ad9361-iiostream.c -lpthread -liio -lm -Wall -Wextra -lrt
clean:
rm pluto_stream

bsdiff算法补丁生成器

1. 官方bsdiff算法例子自带bzip压缩方式

2. 本例子没有压缩,直接生成补丁文件

3. 图形化界面基于DUILIB

二进制文件

版面分析即分析出图片内的具体文件元素,如文档标题,文档内容,文档页码等,本工具基于cnstd模型

Base64 Image

. 闽ICP备19002644号