TCP流式传输的时候有必要在底层增加一个打包的协议,大家是怎么处理流式传输的?

pgmsoul 2011-07-21 11:11:40
几乎所有的TCP传输数据都会面临一个连接数据的问题,因为recv函数可能最少接收一个自己,最多8K个字节,发送端调用Send和接收端的recv数据没有任何的打包概念。比如我用send发了n个同样长度的数据,但是接受端,你不得不自己接收下来然后拆分。

关键是大多数情况下拆分规则是很复杂的,依赖与前面或者后面的数据,所以一般就是先用buffer缓存起来,然后分析,不够就继续接收,够了,还要涉及怎么清理缓存的问题。

这是个相当重复的工作,几乎所有的TCP都会遇到,只不过拆分规则是不同的。

如果TCP协议增加一个打包的概念,send一个包,recv端只有把整个包接收下来,才会返回,会极大的简化程序的设计。

其实自己用C++来封装这么个东西并不难,问题是大家都不这么用的话,要和别人借口的时候,还的自己去连接缓存,分析数据,拆成包。
...全文
269 14 打赏 收藏 转发到动态 举报
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
pgmsoul 2011-07-28
  • 打赏
  • 举报
回复
说的很好。

其实我是觉得,流式这种概念,从一开始方向就错了,它是想提供一个不受限制的任意字节的传输方式,但是实际上这种方式应用并不多。现实中,数据肯定是一个包一个包的收发,底层也是这么做的,那么,把包处理成流式数据,实际上,协议内部是做了连接处理的,反过来,到了用户层,我们又需要解析这个流式成自己的包,有点揉碎了又重新组装的感觉,真是没有必要。

细分析起来,好像用户打个包,解个包,自己定义一个规则没什么难的。但是作为一个协议,它这么设计,用户综合起来要做多少额外的工作呢?

流式不是完全没有用,比如视频,就是连接起来就行了,但是你recv函数返回不是一次一次的吗?假如TCP设计成收发打包,比如1024字节一包,可能网络会有多于流式的数据丢失,比如传了1023字节,网络断了,流式可以得到这1023字节,而包式就必须丢弃了,不够一个包。但是这种看起来其实不是问题,这种影响是可以忽略不计的。用户根据具体场合来决定包的大小,是很方便的。
UDX协议 2011-07-26
  • 打赏
  • 举报
回复
你可以把TCP流数据,想象成一个绳子,反正我是这样理解的。每次发包时,在你的包前面加上长度,每次收发包时都处理这个长度。

在发送方,把长度及有效数据放在一起,发出去,直接完全发出去。具体判断如何已经发完了,不在这里讨论。

接收方,先接收长度或先直管收下来,解析的时候,先解析长度,如果是前者,就一直收完指定长度,如果是后者,判断你接收的缓冲是否大于前面最开始收到的长度,如果是就解析,处理应用。

长度一般用四字节。或二字节看应用。

这样处理一般不会错。
UDX协议 2011-07-26
  • 打赏
  • 举报
回复
UDP,TCP下面是IP层,所有的数据通过IP包传递。

TCP是对IP包进行的可靠算法,就象我对UDP二次包装写的UDX协议一样。

TCP对IP包进行重传以保证可靠,UDX就是对UDP包的重传进行可靠。

所谓的流式,原因是,TCP协议战在提交有效数给上层时,是以字节形式上报给用户,缓冲区是由用户层决定,看上去是任意大小输出,所以称为流式。

UDP包是IP层之上简单包装而成,你可以直接把他看成IP也无大错,但是概念上你要注意就行了。他是一个包一个包,由底层直接上报给上层,俗称,包式。数据报等。

TCP由于其历史及相关应用,得到绝大多数网关,路由器支持,很少有限制TCP的包。而UDP由于P2P应用泛滥,现在基本上路由器或网关对UDP有丢包规则。当然也有限制TCP的,一般是断流。

区别基本上是这些了。没有什么神秘的。
ndy_w 2011-07-21
  • 打赏
  • 举报
回复
需要报文边界的时候,可以做一个通用的tcp拆分协议。这个还是有些普遍意义。tcp收到后依然缓存,每当凑够一个报文就允许read。但是会增加一些开销。
不过一般应用时这样也还不够。比如http还是要去找回车换行,才知道头有多长。
pgmsoul 2011-07-21
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 visualeleven 的回复:]

在应用层自己定义协议,包头信息+数据
[/Quote]

自己用肯定是这么用啊,可以自己定义一个recvpack函数,每一包接收下来都是完整的,但是这个东西因为太普遍,有必要写到底层去。

举个最简单的例子,Java的Socket可以利用ReaderBuffer来读取一行,这样,只要发送端一行一行的发送数据,接收端就是一个整行的数据,非常的方便。但是对于非文本数据,这个方法就行不通了。
ndy_w 2011-07-21
  • 打赏
  • 举报
回复
一般的小应用就自己开buffer了,拆分也不是那么难。大型应用协议复杂,报文多,那么这样搞就复杂了。所以才有了MQ之类的东西。
pgmsoul 2011-07-21
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 fishion 的回复:]

引用楼主 pgmsoul 的回复:
recv函数可能最少接收一个自己,最多8K个字节

可以用setsockopt设置SO_RCVBUF来设置64K的接收缓冲空间
[/Quote]

这是个平台特性,意义不大。对性能和使用都没有本质的影响。
Eleven 2011-07-21
  • 打赏
  • 举报
回复
在应用层自己定义协议,包头信息+数据
pgmsoul 2011-07-21
  • 打赏
  • 举报
回复
如果Http协议使用这种方式来些,那么我们发送一个请求头数据,服务器端返回一个头包,在返回真正的数据,当然数据可以分成多个包。接收端可以清晰的知道,哪些是头数据,哪些是请求的数据,几乎是可以不用缓存,不用分析的。
fishion 2011-07-21
  • 打赏
  • 举报
回复
[Quote=引用楼主 pgmsoul 的回复:]
recv函数可能最少接收一个自己,最多8K个字节
[/Quote]
可以用setsockopt设置SO_RCVBUF来设置64K的接收缓冲空间
pgmsoul 2011-07-21
  • 打赏
  • 举报
回复
可靠包含两种含义,一是有序,一是重传不丢包。这两点UDP都不具备。因为大量的硬件设备都是真的现在的TCP/IP协议设计的,从效率上来讲,UDP没有TCP高,这底层硬件和协议设计时的思路。
ndy_w 2011-07-21
  • 打赏
  • 举报
回复
TCP的诉求是有序,不重复,可靠。
包传输模式就是UDP了。实际上现在也有不少基于UDP重新实现流控的,要做到有序,不重复,可靠,其实实现也不简单。主要是不用三次握手了,在连接数很多时有优势。
pgmsoul 2011-07-21
  • 打赏
  • 举报
回复
实际上在底层,TCP协议使用包的概念来传输的,从数学和物理的角度,单纯的数据在网络上是无法传输的,因为无法校验错误,排序,路由,一个数据包在底层传输时必须是完整的,因为它在网络上传输需要很多必要的信息,单纯的一个字节肯市不行的。

这有点像量子化概念,宏观上看上去是连续的,实际上底层不能是连续的,必须是量子化的。

所以根本还是TCP协议制定的问题,如果不是UDP的连接不可靠(可靠性和TCP不可同日而语,因为硬件就是这么设计的,网络繁忙时,UDP包被丢弃很常见,而TCP数据,自己会重传),我倒是倾向于使用UDP来传输数据。

给TCP传输增加一个包传输模式,说真的,如果当初TCP是这么设计,我估计远比流式用的要多。

TCP唯一的好处就是可靠,不是吗?
pgmsoul 2011-07-21
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 ndy_w 的回复:]

需要报文边界的时候,可以做一个通用的tcp拆分协议。这个还是有些普遍意义。tcp收到后依然缓存,每当凑够一个报文就允许read。但是会增加一些开销。
不过一般应用时这样也还不够。比如http还是要去找回车换行,才知道头有多长。
[/Quote]

如果TCP流式传输一开始就有包的概念,可能HTTP协议就不必使用双回车换行来区分头数据了。因为很简单,头一般很小,几百个字节,一个包发过去,对方一个包接收下来,反正半个头是没有用的,效率只会更高,使用只会更方便,至于头的解析那是用户层的事情,与这个无关。

18,356

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 网络编程
c++c语言开发语言 技术论坛(原bbs)
社区管理员
  • 网络编程
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧