tcp 服务端接收数据处理思路梳理,以及select: Invalid argument报错 笔记

2023-05-16

每次在实现tcp服务器端时,总会思考:处理接收到的客户端的消息细节时,总会陷入一点点的误区,

加上分析公司前人各式各样的业务代码,总是会被略微带偏,这里做简单的tcp接收后相关流的处理思路。

在这里插入图片描述

0:总结

在处理tcp的接收时:

1:tcp是可靠的,内核为每个tcp 客户端连接都分配了一个发送缓冲区和接收缓冲区。

2:基于第一点:

====》针对每个连接,可以可靠,按顺序得接收到数据(即放入对应得接收缓冲区中)。

====》每个连接的缓冲区是独立的,不会有串包现象。

====》缓冲区中存放,是流的形式,无法识别多个包的边界,需要业务层适配。

3:基于第二点:

====》我们需要在业务层适配,识别到一个完整的包,一般有两种方案:(Length+Data), (特定的头部或者尾部标识),我的理解其他相关特定协议思路大概相同

====》作为服务器端,我们需要同时处理多个接收缓冲区(epoll/select管理),同时要考虑如何读取到一个完整的包(每次读多长的数据,肯定能读到一个完整的包吗?)

4:基于第三点:怎么保证一次的事件触发,能正确的读取到缓冲区的内容。

===》比如如果是epoll ET模式,应该循环进行读读完所有的数据(可能有粘包现象,需要处理)

===》如果是epoll LT或者select模式,代码比较简单,但是,如果tcp内核底层拆过包,分多次发过来,可能有半包现象,以及recv时这个长度如何定义?

===》针对以上:(我能思考到的最优的就是: 读固定的长度,while读放入应用层缓冲区(我们应该每个连接维持一个应用层缓冲区)中,然后缓冲区做拆包处理)

5:基于第四点,如果有的代码没有用应用层缓冲区暂存呢?(仅仅考虑的是Length+data的模式

看到有的代码,是特定的业务,就是每次io事件触发,先接收特定的长度,在读取实际数据,这能确定一个完整的包,去做业务处理。

我就会思考这中处理会不会有缺陷,最容易思考的就是

​ 1:一次事件触发处理一个包,会不会有数据没有处理完,内核的接收缓冲区中仍然有数据,比如epoll的ET模式场景,但是貌似select和epoll的LT影响不大

​ 2: 如果业务层没有做相关的处理,有可能的场景是tcp底层拆包,这样先读取特定的自己取长度,再读取实际长度数据时,可能是有问题的?下个包一直没到。

===》但是,最终思考,如果业务层做过处理,保证tcp底层不会拆包,我们如果不用应用层缓冲区应该也是可行的。

===》即,业务层已经保证每次接收是一个特定格式(length+data)的完整的包,每次先接收特定字节头部,解析后接收实际长度数据 后做完整包的处理。

===》考虑事件触发特性,保证处理完善,我们epoll ET处理时,应该循环一次性读完所有的包,而epoll LT以及select的模式下,貌似影响不大,都能正确取完。

1:tcp进行相关recv的处理时

在实际的业务中,我们通常配合select或者epoll对服务器端相关连接进行管理,在接收客户端的消息时,有一些关注点:

1.1:tcp是可靠的流式传输,并且实际上服务器端为每个客户端都维持了一个接收缓冲区和一个发送缓冲区。

第一:tcp底层的流的接收可以保证。(有自己的缓冲区,并且可靠,顺序)

对于每个客户端的连接,可以保证可靠按顺序接收到,放进自己对应的缓冲区中

===》我一直陷入一个误区,如果依赖tcp底层的拆包逻辑,可能在收到多个包的中间会收到其他的包,这其实是一个思想误区,不可能串包

===》服务端对tcp每个连接都设有一个发送缓冲区和接收缓冲区,针对一个连接加上tcp的可靠传输,tcp底层的接收是可以得到保障的。

第二:需要定制协议,对缓冲区流正确处理。

===》tcp虽然可靠,但是却是流的形式进行接收,无法知道多个包的边界。

===》为了能正确识别到每个完整的包,去正确处理(识别到一个完整的包(tcp recv时是从缓冲区中拿数据,第一:缓冲区可能多个包(粘包)第二:可能recv读了半个包,或者tcp底层拆包,第二个包还没来))

===》所以我们需要在业务层对流做特定的限制,保证能识别到一个包 : 比如length+data,比如 加特定标识的协议头部或者尾部

第三:在特定协议的基础上,如何保证正确读取多个缓冲区。

===》如果是epoll的ET边缘触发模式,就得用while循环进行多次读取

===》如果是epoll的LT水平触发模式,或者select,是能下次触发的,不过也可以循环读取完做处理提升效率。

第四:如何保证包的完整性,一个完整的包去做对应业务处理。

===》根据应用层协议,可以先进行数据读取,放入缓存中,然后根据协议解析缓存中的相关数据进行完整包的处理。 (相对应的,应用层缓冲区也应该是一个连接对应一个缓冲区)

??采用缓冲区的方式是没有问题的,但是针对特定的协议,比如length+data的方式,先读取特定字节,解析实际长度,再接收特定的实际数据可以吗?

===》个人理解是在一定的业务层保证上是可以的,

===》tcp如果发送一个过大的包,会进行拆包的,这种场景事件触发后,根据特定的自己字节读实际的长度,下个包迟迟不到就有问题。

===》但是如果tcp我们业务层保证了不回tcp拆包,我们控制了发包大小,我觉得其实也是可行的。

2:关注细节:网络字节序,结构体的对齐方式

如果代码中实现tcp发送与接收相关协议设计时,需要关注一些细节:

1:如果协议用的结构体的方式,要注意结构体字节对齐大小,会影响接收端的解析。

2:一般大于2byte的字节,最后按照特定的函数进行相关的主机字节序和网络字节序的转换

#include <arpa/inet.h> 
uint32_t htonl(uint32_t hostlong);        //32位主机字节序转为网络字节序   Host to Network Long   4字节
uint16_t htons(uint16_t hostlong);        //十六位主机字节序转为网络字节序  Host to Network Short  2字节

uint32_t ntohl(uint32_t hostlong)         //32位网络字节序转为主机字节序    Network to Host Long
uint16_t ntohs(uint16_t hostlong)         //16位网络字节序转为主机字节序    Network to Host Short

3:代码测试时 select: Invalid argument

栈内存定义结构变量最好清零

在简单demo进行测试时,环境运行启动不起来,报错select: Invalid argument

百度结合测试后,发现时select最后参数struct timeval tv;设置的问题

===》1:参考百度,有类似的问题是因为设置了 tv.tv_usec 值过大。

===》2:然而我代码中并没有对这个值设置过大,但是没有对初始化时清零。

	struct timeval tv;
	memset(&tv, 0, sizeof(struct timeval));  //注意  这里清理初始化后的内存
	fd_set rset;
	int maxfd = m_listenfd + 1;
	while(m_running)
	{
		tv.tv_sec = 30;
		//tv.tv_usec = 0;  //是因为用的栈内存  如果这里的内存比较大的话就会导致select  Invalid argument

        rset = m_allset;
        ret = select(maxfd, &rset, (fd_set *)0,(fd_set *)0, (struct timeval *)&tv);
        ...
    }

===》另外,我听同事说tcp缓冲区溢出问题,个人理解是 tcp接收缓冲区不会有所谓的溢出问题,发送缓冲区因为发送频率应该会出现类似问题。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

tcp 服务端接收数据处理思路梳理,以及select: Invalid argument报错 笔记 的相关文章

  • wireshark 和 tcpdump -r:奇怪的 tcp 窗口大小

    我正在使用 tcpdump 捕获 http 流量 并且对 TCP 慢启动以及窗口大小如何增加感兴趣 sudo tcpdump i eth1 w wget tcpdump tcp and port 80 当我使用 Wireshark 查看转储
  • Linux Socket write() 的错误文件描述符 错误的文件描述符 C

    我对 write 2 函数有一个有趣的问题 PrepareResponseForSetCoordinates 函数会导致写入时出现错误的文件描述符错误 这是错误行 perror 写入套接字时出错 总产量 写入套接字时出错 文件描述符错误 我
  • SQL Server 的 SELECT JOIN 语句导致的死锁

    当执行带有两个表的 JOIN 的 SELECT 语句时 SQL Server 似乎 分别锁定语句的两个表 例如通过像这样的查询 这 SELECT FROM table1 LEFT JOIN table2 ON table1 id table
  • Mysql 选择行的总和

    我有一张包含以下信息的表 id Item ID stock package id amount price 0 775 1 16 0 22 1 758 2 10 0 28 2 775 3 10 0 30 3 774 4 10 0 25 4
  • MySQL SELECT 中的条件 SELECT

    Table id price is active 1 20 99 0 2 10 99 1 3 30 99 0 4 15 99 1 5 35 99 1 我试图选择 is active 等于 1 的所有行的 COUNT 所以我使用了这个简单的查
  • 很难理解带有 async_read 和 async_write 的 Boost ASIO TCP 的一些概念

    我很难理解使用 async read 和 async write 时构建 tcp 客户端的正确方法 这examples http www boost org doc libs 1 38 0 doc html boost asio examp
  • 是什么导致 MSSQL 中出现“非阻塞套接字上的操作将阻塞”错误?

    错误 异常查询为 CREATE NONCLUSTERED INDEX I1 ON AllAccounts BAK Master received Day ASC 出现异常 发生一个或多个错误 错误 异常内部异常无法从传输连接读取数据 非阻塞
  • 如何检查日期范围是否存在

    正如你从标题中可能注意到的 我的问题很难表达 但有一个问题 我有包含一些数据的 MySQL 表 在注册表中 我必须指定预订开始和结束时间 如何查询指定时间是否已登记预订 例如 我不应该能够添加从 13 20 开始到 15 00 结束的新预订
  • 通过 TCP 客户端套接字接收数据时出现问题

    我正在尝试用 C 语言编写一个 TCP 客户端程序 客户端将在其中启动 连接到服务器 然后它会发送一些信息 然后监听它收到的信息并做出相应的反应 我遇到麻烦的部分是持续聆听 这是我所拥有的 while 1 numbytes recv soc
  • 跳出选择循环?

    我正在尝试使用select在循环中接收消息或超时信号 如果收到超时信号 则循环应中止 package main import fmt time func main done time After 1 time Millisecond num
  • 如何使用 kotlin 通过 TCP 连接发送和接收字符串

    我在 Windows 上有一个 TCP 服务器 我想在服务器和我的 Android 设备之间发送和接收文本字符串 我花了很多时间搜索使用 Kotlin 的示例 但没有找到任何有用的代码 所以我现在只能创建套接字并连接 fun connect
  • 使用 GROUP BY 时 MySQL SUM 不起作用

    假设我们有这张表 Symbol Size A 12 B 5 A 3 A 6 B 8 我们想要这样的视图 Symbol Size A 21 B 13 所以我们用这个 Select Symbol sum Size from table grou
  • 将一项选择中的两项计数相除

    我有一个这样的表 date timestamp Error integer someOtherColumns 我有一个查询来选择特定日期的所有行 SELECT from table WHERE date date 2010 01 17 现在
  • 如何根据多个字段选择不同的行

    我有一个表 其中包含 MSSQL 数据库中一系列事件的数据 ID Name Date Location Owner 1 Seminar Name 1 2013 08 08 A Location Name
  • Oracle 相当于 information_schema.tables

    当我尝试在 Oracle 中执行以下查询时 出现 表或视图不存在 错误 SQL查询 SELECT table type table name FROM information schema tables WHERE table rows g
  • 轨道:fields_for选择

    在我的视图中 我使用 fields for 来显示关系表的表单数据 然而 此表格的一部分将有可供选择的选择列表 我看到 form for 和 fields for 帮助程序有 label text field text area 帮助程序
  • 将 HTML 选择元素转换为带有子菜单的树

    我想让一个选择元素有一个树形的子菜单 我希望它是这样的 source colinear com http www colinear com rmenu gif 有没有一个 jQuery 插件可以将 select 元素变成这种东西 这里有树插
  • 如何在java应用程序中检测FIN - tcp标志?

    我在两台计算机之间有持久的 TCP 连接 第二台计算机不受我的控制 第二台计算机可以随时发送FIN标志 并且首先必须关闭当前连接 将FIN标志发送回第二台计算机 我如何知道第二台计算机正在发送 FIN 标志 以及何时必须调用 Java 应用
  • 构建多线程 TCP/IP 服务器

    我想构建一个可供最多 100 个并发客户端使用的 TCP IP 服务器 但仍不确定如何开始 至少我需要服务器 监听客户端 并将它们全部存储在数组或列表中 对于每个客户端 它需要根据其客户端状态接收和发送数据 当有人连接或断开连接时 服务器应
  • 如果通过 SQL 查询结果没有找到记录,则应为 0

    我正在使用火鸟 我需要以下结果 但我没有得到我需要的结果 我尝试了以下查询 SELECT CASE EXTRACT MONTH FROM pd Date WHEN 1 THEN January WHEN 2 THEN February WH

随机推荐