9.面向字节流
“粘包问题” => 包指的是“TCP的载荷中的应用数据包”
tcp传输的数据到了接收方之后,接收方要根据socket api来read出来,read出来的结果就是应用层数据包(由于整个read过程非常灵活,可能会使代码中无法区分出来当前的数据是从哪到哪是一个完整的数据包)
此处假定,这三个tcp数据报都是携带完整的应用层数据包(其实一个tcp数据报也可以携带多个应用层数据包/半个应用层数据包)
接收缓冲区
应用程序调用read读取数据,由于此处是字节流的,非常灵活。
可能多个应用层数据包混淆不清了,称为“粘包”。
读出数据之后,就需要把这里的数据转成应用层数据包(应用层要做的事情),然后这个数据才能被正确的使用。
粘包问题不是TCP独有的问题,只要是面向字节流的,都有相同的问题。
解决方法的关键 => 明确包之间的边界
1.通过特殊符号,作为分隔符。见到分隔符,就视为一个包结束了。
使用任意字符作为分隔符均可以,只要确保当前分隔符不会出现在正式的数据中。
2.指出包的长度,比如在包开始的位置加上一个特殊的空间来标识整个数据的长度。
粘包问题是TCP引起的,但是TCP并不会解决,需要程序员写代码,写应用层逻辑的时候自己进行处理。
各种应用层协议的格式,xml、json、protobuffer均能处理该问题。
10.异常问题
1)其中有一方出现了进程崩溃
进程无论是正常结束还是异常崩溃,都会触发到回收文件资源,关闭文件,这样的效果(系统自动完成的),就会触发四次挥手。
TCP连接的生命周期,可以比进程更长一些,虽然进程已经退出了,但是TCP连接仍在,仍然可以进行四次挥手。
虽然说是异常崩溃,实际上和正常的四次挥手结束没什么区别,进程不在了,是通过系统中仍然持有的连接信息完成后续的挥手过程的。
2)其中有一方出现了关机(按照正常流程)
当有个主机触发关机操作,就会先强制终止所有的进程(类似于上述的强杀进程)终止进程自然就会触发四次挥手。
点了关机之后,此时,四次挥手不一定可以进行完,系统马上就关闭了。
如果进行的很快,能够顺利挥完,此时本端和对端都能正确的删掉保存的连接信息 => 四次挥手的核心任务
如果进行的不快,至少也能把第一个fin发给对端,至少能告诉对方,我这边要结束了。对端收到fin后,也要进行释放连接的流程了,返回ack,并且也发fin。这里发的fin不会再有ack,fin没有收到ack就要进行重传(超时重传的流程)当重传几次后,还是没有ack,就单方面的释放连接信息。
3)其中一方出现断电(直接拔电源,更突然的关机)
如果直接断电,机器瞬间关机,此时来不及发fin => 这种操作比较伤机器。尤其是上硬盘(机械硬盘)硬盘写到一半就没了,磁头来不及归位,可能使硬盘被写坏,或是写入一些错误数据。
a)断电的是接收方
发送方会突然发现没有ack了,就要重传,重传几次后还是不行,TCP就会尝试“复位”连接(相当于清除原来的TCP中的各种临时数据,重新开始)
需要用到一个TCP中的“复位报文段”
RST => 通过这个报文段直接复位,既往不咎
PSH => 催促对方快点发消息
URG => TCP外带数据有关,TCP有写特殊的数据包,携带了一些特殊功能的数据。
b)断电的是发送方
接收方本来就是在阻塞等待发送方的消息,这种情况下,接收方需要区分出,发送方是挂了,还是好着只是暂时没发。
TCP中也是如此,接收方一段时间后,没有收到发送方的消息,就会触发“心跳包”(也不携带应用层数据的特殊数据包 1.周期性的 2.没有心跳,视为对方挂了)来询问对方的情况。
如果对端没有心跳了,本端就会尝试复位并单方面释放连接了。
4)网线断开
本质情况就是将3)中的a和b结合了