谈网络中的数据收发处理

事先说明下情况:
写socket 的时候,一般都是会在 数据包 加上【包头】,指示这个数据包的size。

(消息防篡改可能还会加密, 再校验)
可以参考【对称加密算法】,【非对称加密算法】

0.网络拥堵问题

- 无线移动 信号不稳定
- 有线带宽占满,网络阻塞。阻塞超时,会重传。
(超时越多,重传越多)↓
↑(重传越多,超时越多)
严重的场合,会陷入循环。
所有才有下面,修改
- 有的操作系统或者程序,会有优化处理
* windows的 [Nagle](https://en.wikipedia.org/wiki/Nagle%27s_algorithm)
* google BBR 会自动根据网络状况,修改滑动窗口大小。 [google BBR](https://github.com/google/bbr)

所以 网络上TCP协议的校验, 是TCP协议上的。为了做重传。

但实际结果:延时高的时候,当然数据不一定正确 or 不一定能传送到
这个时候,就需要我们自己定义包头, 收发端 一起来规范格式。

防止数据出问题,可以在业务层 做处理, TCP协议毕竟是80~90年代的产物,不是那么靠谱。
( 当然最近版的linux 内核好像对网络层 改动很大。提高了很多性能。

微软估计也是因为技术创新这点,才拥抱Linux的吧。 )

1.网络中的 数据包结构设计

一般是 { [package size] [ md5 ] [buffer ..... ] }
size: 4Byte 32byete packageSize
- package头的size 是固定的长度, (里面一般是[buffer]的size)
- md5 size 随意,看你自己要生成多少位的数据了,一般是32个字符, 32byte (不一定非要MD5 可以用其他校验方式)
- buffer 一般是对这个buffer 进行MD5, package size就是他的长度。

2.是否 有【粘包】这个概念

一般是指数据包 ,每次recv 大小不明确,

- 2.1 正常情况
- 如果先 detach() recv thread,再 detach() send thread,就没有问题。这是没有问题的。
- 2.2 异常情况
- 如果先 detach() send thread,再 detach() recv thread. 如果另一端,收到后立马再发送。
则因为,send() 之后,没有recv(), 导致前面send()的数据包可能丢失。
那么这次 recv()的数据,包含上次(上上次, 上上上次)远程 echo的数据, 所以会导致数据 变成以下状态.(可能完整,可能不完整)
//假设一个完整数据包就是 "data"字符
* ta...}{data... } //缺少da
*{data...}{da... //缺少ta
*{data...}{data...} //运气好,没有丢失数据
- 还有一种是,socket开启后,recv()正常,然后再shutdownRecv() 没有接收数据,在缓存中, 下次开启recv()时,导致网络中(接受方网络硬件中的缓存)也会出现以上问题。
当发送方send()数据后,
数据可能在网络中(光纤线路, 机房路由中),
可能在你的路由器,
可能在 你的主机 网卡缓存中(你还没有取),
可能 recv()处理出错
可能 你正确recv()数据后, 你的业务流程 却没有处理(软件崩溃,断电,资源耗尽 等等)
必须完成业务逻辑, 进行持久化. 才能完整一个业务流程.
中间任何环节都可能存在问题.

结论:

一般是没有网络粘包这个说法的。 一般情况下 TCP的SYN 序号,和重传 已经足够处理大部分网络阻塞,延时需求
只不过 有的场合 会出现点意外,如果业务设计 做到足够好,就可以不用管粘包
就像陈硕说的,服务器并不是要保证7*24小时正常运行,而是无论哪台机器,出问题之后,还能保证业务100%不出错
无论哪种,处理数据不知道每个包的大小,处理起来就格外麻烦。所以需要处理socket 业务的时候好好考虑下。
1.包格式
2.异常的时候,该结束 还是独自解析粘包的数据?(业务设计的时候,如何保持强一致性) ==》回复接收成功
3.包的校验
相关: setsockopt 设置 SO_LINGER 选项, 保证socket close之前,把数据发送出去。

3.网络IO模型

简单网络IO模型有(不讨论API,网上资源已经足够多了。 linux 可以在这里查看资料 https://www.kernel.org/doc/man-pages/

- 阻塞
* 一recv,一send
* 一recv,多send
- 非阻塞
* send thread, recv thread(哪有业务,就处理哪,效率最高)
- 半阻塞
* send 为非阻塞,recv 为阻塞
* recv 为非阻塞,send 为阻塞

其实我一直觉得 [Registered I/O] 就是微软对Linux [epoll]的致敬。

  • windows:select/iocp/[Registered I/O]
  • linux:select/poll/ epoll
  • freebsd:kqueue

转载请注明来源-

posted @   scott_h  阅读(425)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示