为什么TCP要做成流式协议,而非包呢?

0 今日话题

为什么TCP要做成流式协议,而非包呢?

哦莫西多 。 先假设TCP做成“包式协议 “的ROI,再探讨可⾏性。先要澄清,作为传输层协议,实际上有对上和对下两套抽象。

1 对下

即⽹络层,⼀般是IP协议,再下就是⽹络层如以太⽹协议 。

TCP对下层的抽象从第⼀天开始就是包。TCP失败重传、拥塞控制、流量控制等机制都建⽴在“TCP packet”前提下。

对以太⽹MTU这类问题,可假设TCP对下总是能处理好,⽽不会因为某个包太⼤就傻乎乎丢数据。因此不过多讨论。

2 向上

所以,题目中的"包"只能是面向上层或说是"面向用户的包”。可简单理解为类似把HTTP1.0里的request/response (或叫message) 的设计直接放到TCP直接实现。这样发送方就很方便写入一个应用的"包",读取方就能读取整个这样一个应用的包。

对一般的需求看上去很合适。并且看起来这个设计也不会太破坏TCP对下层的抽象。只要在发送和接收buffer里增加标记每个用户包的开始、结束位置,到了地方就给上层recv返回即可。

3 有麻烦了

但这样有问题:必须给这个包设计⼀个最⼤⼤⼩,否则TCP不知道要分配多⼤内存。若⽤户包>接收buffer,很难设计⼀个合理接⼝⾏为,因为这时recv:

  • 是返回⼀部分数据
  • 还是返回错误?
  • 还是必须逼发送⽅⼿⼯拆包?
  • ⼜或⼀个上层应⽤在启动时要告诉TCP⾃⼰需要有多⼤的包。但上层应⽤可能并不能提前知道到底⼀个包多⼤

如上层要上传⼀个⽂件,⽂件多⼤⽆法提前知晓。所以,为了可⽤,上层必然根据⾃⼰的业务需求⼀定要实现⼀个⾃⼰拆包的协议。⽽⼀旦上层实现了这协议,那TCP层拆包就完全丧失意义。

3.1 UDP咋做的?

UDP就是定义最⼤的包⼤⼩。因此使⽤UDP的应⽤层⼀般会设计为“绝对不会超 过这个⼤⼩”;或者如果要超了就改成TCP。⼀个典型的例⼦就是DNS的协议设计。这种形式应 付⼀下简单的场景还凑合,但对于⼀般业务开发是绝对不可接受的。

⽽某些应⽤场景,⾃身就是数据流。若TCP设计为⾯向⽤户包,这些场景就须引⼊“流转包 ”。技术上也可⾏。或者TCP同时提供⾯向包、⾯向流两种语义,TCP需要定义“包连接”和“流连接”两种连接,各传各?这⼤⼤增加协议设计和使⽤复杂度。

更郁闷的,若业务场景需要进⾏有序数据传输,开头是个包,包完了是流,⼜咋处理?对应⽤层协议还好,可针对场景定制设计;问题是TCP定位是通⽤传输协议,若搞这种定制设计就没法⽤。

4 包拆分

更麻烦其实是不同应⽤场景,包拆分机制不⼀。常⻅⽅式提供⼀个字段明确标记包的字节数。

有的根据请求中的特殊字符区分是否要拆包。⽽特殊字符⼜涉及编码和escape之类问题。⽽这些字节流⼜可能被加密,得到结果还跟密钥相关。这些数据到TCP这⾥就就没法搞了。

合理做法还是应⽤层先拆,拆好了加密,加完密再传输。

5 应⽤层拆包

因此发现,⽆论TCP层咋折腾,都不太可能绕开“应⽤层拆包 ”。所以,也许更好处理⽅式:完全⼲掉“流式协议”这层的抽象,但保留底层包的保序/重传/流控/拥塞控制等机制⸺QUIC就是这样。但对⼏⼗年前的⽹络协议设计,TCP流式协议是⾜够好选择。在众多协议下能活下来并得到⼴泛应⽤⾜以证明。

6 总结

BTW,的确存在⽀持message的传输层协议SCTP,但过于复杂得不到⼴泛⽀持(Windows压根不⽀持),濒临灭绝。

⽽那些适合⽤“⽤户包”来实现的功能(如微服务的RPC ),可直接⽤HTTP做传输层。即把⼀个本设计为应⽤层的协议,保留其Header,编码,拆包等基本能⼒;同时将如Cache协商,⽤不到的编码格式等扔掉不实现。在不对基础协议做更改的前提下就能run。

另外⼀个选项⽤ZeroMQ,天然封装Req/Resp能⼒,可在此基础构造应⽤层协议。钱多⼤⼚,⾃⼰手写协议满足业务也完全可。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值