绝大部分情况下, 从 TCP 接收数据都存在一个把 "TCP 流" 转成 "数据块" 的问题, 那么为什么 TCP 当初还要设计成 "流"

2018-07-08 10:19:14 +08:00
 c3824363

TCP 设计成 "流", 只是实现起来容易一些吧, 但用起来真不爽 实际上程序里用到的都是一块一块的内存, TCP 强行弄出一个"流"来. 这样在接收方必然要从 "流" 转换成 "块", 转换的方式有: 固定长度, 加包头指定长度, 用特殊的间隔标记.

固定长度: 简单无开销, 但是太死板不灵活, 适用场合很少. 加包头指定长度: 编程简单, 但会多一次读取开销. 用特殊的间隔标记: 比如 HTTP 这种, 就需要遍历全部内容

如果 TCP 原本就保留“块”信息, 则使用起来就会简单很多了。 比如这样定义

struct iovec {
   void  *iov_base;
   size_t iov_len;
};

/*
  返回发送出去的 struct iovec 的个数, 不要发送半个
  出错的情况的返回值和 write()/send() 一样
 */
int my_writev(int fd, struct iovec *vec, size_t n_vec);

/*
  返回接收到的 struct iovec 的个数, 不要接收半个
  出错的情况的返回值和 read()/recv() 一样
  接收的时候可以把 struct iovec *vec 预先分配,也可以不分配直接接收到 buf 里面同时把分块信息保存到 struct iovec *vec。
 */
int my_readv(int fd, struct iovec *vec, size_t n_vec, void *buf, size_t size_of_buf);

这样用来起来再很多场合就非常方便了。

6307 次点击
所在节点    程序员
53 条回复
GTim
2018-07-09 08:36:04 +08:00
@Mirana 哈哈

多多观察:水和蓄水池,还有流速 应该就会理解了
ca1123
2018-07-09 08:56:22 +08:00
不用流,你发的时候岂不是得知道尾巴在哪?有些应用,发的时候根本不知道什么时候会有尾巴。其实流的意思是,我的块就这样,全是标准的,看你怎么用吧。
enenaaa
2018-07-09 09:13:13 +08:00
流比块更好用。
如果设计成块结构, 就会有人问, 我想像文件流那样读数据还得自己写代码?
我想自定义块结构, 还得在块上加块?
c3824363
2018-07-09 09:24:23 +08:00
@enenaaa 从编程的角度看 块可以当成流,但反之不行
enenaaa
2018-07-09 09:28:22 +08:00
@c3824363 现在应用层协议照样可以自定义块结构。
zhicheng
2018-07-09 09:43:16 +08:00
不是要设计成流,而是只能设计成流,不然设计成块,你打算让 OS 怎么办?传送一个 1G 的块,OS 要把它全 Cache 到内存里?内存不够怎么办?存到磁盘?一下可用场景就少了。如果一开始加上最大容量,比如 4G,现在又会嫌不够,又要处理多块变成流了。如果你真的需要块,请在创建连接的时候发一个 4 字节或 8 字节的长度,然后不停的 recv,至于存到内存里还是磁盘里还是转发给另一个设备,取决于 App 自己。如果一个工程师连这都解决不了,不建议去吐槽 TCP 的设计。
deadEgg
2018-07-09 10:03:57 +08:00
流实际上就是更小块的块。设计成流是为了 os 更方便的读,如果涉及成块,足够大的块,失败回退的可能远远放大。放大的原因不仅仅是 os 缓冲区大小受限(#46 老哥也说到了)。更为重要的原因在于,失败回退导致不断重发容易导致拥堵。

换句话说,就好比在一条大道上,各种大小宽度不同的车辆, 没有规定大小的概念,所有车辆都一直往前开,是不是拥堵而又效率低下。

设计成流个人认为就是块结构更小的划分,虽然多了一些冗余的字段,但是更好地保证了交通的畅通。

个人观点。
mengzhuo
2018-07-09 10:07:01 +08:00
如果传输的数据始终小于 68 字节,不讲究顺序,那可以用 udp。
mingl0280
2018-07-09 10:22:52 +08:00
这就是没搞清楚为什么 TCP 叫可靠的基于字节流的协议的人问出来的问题,我估计这个题主恐怕也是深受“ tcp 粘包”问题的困扰啊。
人家 TCP 就不管你上层协议传了什么东西的,分包和分块都是上层协议的事情,TCP 只负责传递的数据包是可靠有序的,它为什么还要管你上层数据包传了什么东西?如果要管的话你 TCP 协议还要去验证上层包发对没,既容易出错也不好解决数据过大导致的块过大的问题。
还有,有些数据根本没有长度限制的,如果按上层协议进行检查怕不是分分钟一个文件发送吃完整个系统的内存……
BOYPT
2018-07-09 10:50:03 +08:00
“数据块”需求很明显是较为上层的需求。了解一下 OSI 模型,TCP/UDP 处于中低层;“流”是颗粒度更小的“块”,流到块是应该根据应用层需求,在上层模型实现的。
newtype0092
2018-07-09 11:18:28 +08:00
为什么自来水管不设计成以桶为单位送水呢?拧一下水龙头就掉出来一桶水,到时候收水费也方便,直接算你用了多少桶就好了。
什么?你说你只要接一杯水?为了效率这点浪费是不可避免的,习惯就好了。。。
sampeng
2018-07-09 14:05:27 +08:00
你自己发 udp。封装一下。就是你要的块的方式。然后再加保证到达,超时,巴拉巴拉巴拉机制,恭喜你,你实现了 tcp 协议的轮子
flynaj
2018-07-09 21:59:54 +08:00
OSI 模型了解一下,基础不好就是这样,限制自己发展!

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://tanronggui.xyz/t/468985

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX