TCP与应用层协议

来源:互联网 发布:淘宝 iphone att群 编辑:程序博客网 时间:2024/05/17 23:08

TCP层与UDP层建立在IP之上,以IP层为基础进行数据传输,也就是说TCP与UDP还是通过一个个的IP包来进行传输的,虽然IP层只关心一个数据包(基本单位)的传输过程,但IP并不能保证数据包能安全的被送达,一旦丢失IP也无能为力,TCP在IP的基础上加上了对传输过程进行流控制,增加了一系列保证传输可靠的机制。

1、给每个IP包分配序号sep,同时,保证接收端在接收到序号为seq的IP包时回复ACK消息(ACK为其期望收到的下个IP包的序号,即成功收到的序号为seq + 1的包),如果发送端在合理的往返延时内未收到回复的ACK消息,发送端将重传,接收端收到序号一致的包将丢弃,如果发送端接收到接收端回复的ACK消息,将继续发送下一IP包,(即超时重传,捎带确认机制)这样就保证了传输的有序与可靠

2、TCP提供校验和函数检验数据是否错误,发送接收时都会计算校验和,同时使用MD5认证对数据加密,以此保证数据正确与合法

3、滑动窗口技术会进行流量控制,不会出现发送的数据长度大于输入缓冲的情况,并实现TCP面向流的可靠性

参考博客:     http://blog.csdn.net/ljlstart/article/details/51340829

 

TCP套接字内部有I/O缓冲区(每个套接字都有自己独立的输入/输出缓冲),只要缓冲区不满,一次全部读完,或者分几次读完是没有关系的,缓冲区一般大于单个IP包的字节数,因此会将缓冲区内的数据包分成一个个小包,IP层封装每一个小包并以IP包的形式向接收方发送(以接收IP层来说),接收方IP层解包并将一个个数据包放入接套接字的缓冲区,并组合成一个完整的数据包,一个个的IP包不停的、连续的从发送方到接收方,就好像流一样;

 

以一个个IP包的连续传送的方式,再加上网络环境复杂,IP包的旅途恐怕不会那么一帆风顺,因此接收端在调用read函数读取缓冲区时可能并不能完全读取发送端意图发送的所有数据(read函数在读取套接字缓冲区时默认阻塞,直到接收到数据为止,缓冲中有多少读多少,将有的读完则返回,被读取后的数据将会被清除,正是这些原因,下面使用循环的方式调用read) (好比你去餐馆吃饭,你点了5个菜,一般情况下并不是5盘菜直接就在桌子上等着你吃,而是菜再做好了之后会被一盘一盘的端上来,没有菜的时候肯定就要等着,而且你也不会吃了一个菜就走吧?肯定要吃够5个菜才行,不然你干嘛点5个菜呢???);

 

也是由于有缓冲区的原因,调用write”发送”(write实际只是写缓冲区,只要将数据写入缓冲区便会返回,数据的发送由TCP保证)一次数据也不意味着数据肯定被立即发送出去了,有可能后续发送的数据和之前发送的数据都在缓冲区后随后被一起发送出去了,这就是发送端的粘包;

同样也因为缓冲区的原因,如果没有及时read读取缓冲区中的数据,后续到达的数据会继续存放到缓冲区中,也就是两次发送端发送的数据同时存在了接收端的接收缓冲区中,这是接收端的粘包;

还有可能调用write向缓冲区填充意图发送的数据时,数据填充到一半缓冲区满了直接发送了,但实际情况是,这个数据并不是一个完整的数据,剩下的数据只有等下一次发送的时候再补上,这是不完整的粘包。

 

这时候应用层协议的作用就凸显出来了,在多次收发数据时(要是双方的一次连接就发一次,不管分成几个IP包,组合后依旧是一个完整的数据包,当发送完毕后断开连接时(无论是shutdown或者close,只要触发EOF),肯定意味着数据传输完成,以EOF为依据循环读取即可,即while(0 != read(客户端套接字,buf, BUF_SIZE)) ),双方必须统一口径,发送方必须事先明确告诉接收方一个数据是多少字节,按照约定的长度读取数据即可;

或采用”数据长度 + 实际数据的”格式发送数据(每一帧都以此方式发送数据),这个”数据长度”的格式是固定宽度的,事先也必须商量好,在读取数据时,直接读取固定宽度的”数据长度”获取数据实际长度,再按照实际长度读取实际数据即可;

通过这两种方法,在知道数据长度后,一定要读取够数据长度之后再停止读取,也就是必须以数据长度为判定依据循环读取缓冲区的数据,次数不限,数据个数不多不少,完全读取,如已经知道了数据长度为N字节,即应该读取N个字节:

Int recvlen = 0, recvcount = 0;While((N – recvlen) > 0)  {         recvcount= read(客户端套接字, &buf[recvlen], N - recvlen);//读取到buf[n]元素的位置
//每一次:将缓冲区内有的数据读取上来,以buf[recvlen]元素为基本位置向后存放         recvlen+= recvcount;}



或者。。。。。通过建立多次连接来发送多次数据。。。。。。。

 

网络环境很复杂,不能避免粘包的情况发生,在接收时按照数据实际宽度拆开即可

 

 

个人理解,欢迎大家拍砖

搜了些博客,扫了一遍,写得挺好,于是把他们贴上来:

http://www.tuicool.com/articles/vaE3iq

http://blog.csdn.net/zhangxinrun/article/details/6721427

http://www.cnblogs.com/QG-whz/p/5537447.html

http://blog.csdn.net/my_sky2012/article/details/49486539

0 0
原创粉丝点击