面向字节流
? ? ? ? TCP在内核有自己的接收和发送缓冲区。
????????面向字节流最重要的就是接收和发送缓冲区的存在,在应用层向下发送一个数据包时,可以发送一次或者多次,即可以调用一次或者多次send。在应用层接收一个数据包时,可以接收一次或者多次,可以调用一次或者多次recv。发和收次数不需要对应。而TCP发送数据有自己的规则,如下。
? ? ? ? 应用层调用传输层提供的接口发送一个数据块给TCP,数据块被写到了TCP的发送缓冲区。而TCP是以字节为单位看待这个数据块的。
? ? ? ? 当数据块字节数太长,会被拆分称多个TCP数据包发送出去。
? ? ? ? 当数据块字节数太短,先在缓冲区里等待,等待缓冲区长度差不多了或者其它适合的时机发送出去。
粘包问题
面向字节流会有一个问题:
? ? ? ? 因为接收数据会按照TCP报头序号,在接收缓冲区里排好序,但是,在应用层在读数据的时候,可能读到的数据少于一个报文或者多余一个报文。
? ? ? ? 因为它是按字节流的,要获取整个数据,可能需要多个报文分割出来的有效载荷。
? ? ? ? 此时,一次读到的不是一个完整的报文,不好分离。?
- TCP是按字节流的,想读取整个数据,可能需要读取多个报文分割出来的有效载荷。
- 在TCP报文中没有明确报文长度,但是有序号字段。
- 接收来的数据按照序号在接收缓冲区里排好序。
- 应用层看到的是一连串的字节数据,不知道从哪部分开始,哪部分结束是一整个报文。
粘包问题就是:在应用层读数据的时候,可能会出现读的数据大于或者小于一个报文(request)的情况。
如何避免粘包问题:明确两个报文之间的边界。
- 对于定长的报文,保证每次按照固定大小读取
- 对于变长的报文,可以在报头位置,约定一个报文总长度的字段。
- 对于编程的报文,还可以在报文之间明确分隔符,应用层协议,程序员写的,保证分隔符与正文不冲突即可。
对于http协议解决:
? ? ? ? 在接收缓冲区中,一开始读到的肯定是http的报头。当读到空行说明报头读完了,在http报头中有一个字段Content_length记录了正文的字节数,按照这个读就可以读到整个报文。后面也按照这样读,就都可以都读到整个报文了。
? ? ? ? 在分成多个字节recv到缓冲区。
比如:send一次,100字节。recv100次,一次recv一个字节。应用层就是先将接收缓冲区数据,去掉http报头,按照上面规则全部读出来,然后一个字节一个字节recv上去。
对于UDP协议,不会存在粘包问题。
????????在UDP报头中,存在一个字段,记录了报文的总长度,UDP报头是定长的8字节。并且UDP是将数据一个一个交付给应用层的,会有数据之间很很明确的边界。一个报文就是一个数据。
TCP异常情况
? ? ? ? 通信进程终止了:进程终止会释放文件描述符,套接字与文件有关,文件生命周期随进程,进程终止,相当于是关闭套接字,底层会自动触发四次挥手。
? ? ? ? 机器重启:机器重启之前,需要将进程终止,所以和进程终止情况相同。
? ? ? ? 机器掉电/网线断开:此时接收端认为连接还在,首先,一旦接收端向发送端发送数据,发现连接不在了,会发送RST报文给发送端。即使及手段没有写入动作,TCP内部内置了保活定时器,会定期询问对方是否还在,不在的话,会将连接释放。? ?
|