你可能需要理解这句话:TCP是流传输协议,UDP是包传输协议。
1:什么是流传输协议?打个比方,发送方一次或分多次send了“1234,567,890,abc....”这些数据,接收方每次recv时得到的数据可能是分成了以下这么多片段:123,45,67890,ab,c....,也可能是这样的片段12,345,678,90abc,...。所以在网络正常的情况下,TCP虽然能保证数据按序到达对方,但不保证每次到达的数据量跟发送的时候是一致的,因此应用层必须要处理这样的情况,而处理这种情况就只能在应用层的数据里加额外信息,TLV就是其中一种实现方式。
理解流传输协议的本质还可以想象一下家里用的自来水管的情况,自来水厂源源不断的供水,我们用的时候都是开一下水龙头就放一点水,开一下放一点,水是先进先出,但一次进多少跟一次出多少是完全不对等的。
2:什么是包传输协议?
打个比方,发送方一次或分多次send了“1234,567,890,abc....”这些数据,接收方要么收不到包(譬如567这个包,要丢就是整体都丢了,不可能收到了5,而丢了67),要是收到的话,也会是这样一个个完整的包(顺序不保证):567,1234,890,abc....,所以每个包都是一个整体,是最小传输单元,不可再被分解。
3:为何你本机传输没问题,而两台计算机间传输就出问题了?
因为本机传输时,TCP包一次最大可以传送65535个字节的数据,所以当你图片小于64K的时候,一次就传完了(完全没有分解的必要),但你可以试试传输多个大于64K的图片,看看会出现什么情况。当两台机子的数据是通过以太网甚至广域网传输的时候,包从发送端出去的时候最大也就1500个字节,经过广域网可能还会被分解成更小的,因此产生了问题1中的情况,你再通过原先简单的recv代码肯定出错。
4:为什么
其实,这种不同是由TCP和UDP的特性决定的。
TCP是面向连接的,也就是说,在连接持续的过程中,socket中收到的数据都是由同一台主机发出的(劫持什么的不考虑),因此,知道保证数据是有序的到达就行了,至于每次读取多少数据自己看着办。
而UDP是无连接的协议,也就是说,只要知道接收端的IP和端口,且网络是可达的,任何主机都可以向接收端发送数据。这时候,如果一次能读取超过一个报文的数据,则会乱套。
比如,主机A向发送了报文P1,主机B发送了报文P2,如果能够读取超过一个报文的数据,那么就会将P1和P2的数据合并在了一起,这样的数据是没有意义的。
5:带来的问题
由于TCP"流"的特性以及网络状况,在进行数据传输时会出现以下几种情况.
假设我们连续调用两次send分别发送两段数据data1和data2,在接收端有以下几种接收情况(当然不止这几种情况,这里只列出了有代表性的情况).A.先接收到data1,然后接收到data2.
B.先接收到data1的部分数据,然后接收到data1余下的部分以及data2的全部.C.先接收到了data1的全部数据和data2的部分数据,然后接收到了data2的余下的数据.D.一次性接收到了data1和data2的全部数据.对于A这种情况正是我们需要的,不再做讨论.对于B,C,D的情况就是大家经常说的"粘包",就需要我们把接收到的数据进行拆包,拆成一个个独立的数据包.为了拆包就必须在发送端进行封包.
另:对于UDP来说就不存在拆包的问题,因为UDP是个"数据包"协议,也就是两段数据间是有界限的,在接收端要么接收不到数据要么就是接收一个完整的一段数据,不会少接收也不会多接收.
为什么会出现B.C.D的情况:
"粘包"可发生在发送端也可发生在接收端.1.由Nagle算法造成的发送端的粘包:Nagle算法是一种改善网络传输效率的算法.简单的说,当我们提交一段数据给TCP发送时,TCP并不立刻发送此段数据,而是等待一小段时间,看看在等待期间是否还有要发送的数据,若有则会一次把这两段数据发送出去.这是对Nagle算法一个简单的解释,详细的请看相关书籍.象C和D的情况就有可能是Nagle算法造成的.2.接收端接收不及时造成的接收端粘包:TCP会把接收到的数据存在自己的缓冲区中,然后通知应用层取数据.当应用层由于某些原因不能及时的把TCP的数据取出来,就会造成TCP缓冲区中存放了几段数据.