TCP:有连接,可靠传输,面向字节流,全双工可靠传输:不能保证数据百分百发送到对方手上,但发出去之后能知道有没有到达对方的手上(1)确认应答:信息在传输过程中,会出现后发先至的情况,为了解决这个问题,对发送的数据引入了编号并且重新将数据根据编号重新排队,就可解决先发后置的情况所以TCP采用了针对字节进行编号32位序号:针对传输的数据进行编号32位确认序号:给ACK报文使用,关联当前这个ACK是应答哪个数据TCP采用的是面向字节流的方式进行数据传输对数据编号的方式:确认序号填写方式:收到数据的最后一个字节编号1比如说最后一个数据的字节序号是1000,则返回1001,表示1-1000已经接收到ACK就是6个标志位之一:返回ACK时,设置为1(2)超时重传:重传的机制由内核触发,传输的数据先到达内核的接收缓冲区(就像生产者消费者模型),如果再次触发重传,数据会再次到达接收方的缓冲区根据序号进行去重,内核是进行了去重,但站在应用程序的角度仍然读到的是一份数据数据的重新排队也是在缓冲区进行,等队伍排好队后再被应用程序给取走所以TCP解决了可靠性,数据传输顺序,数据重复传输的问题重传可能会丢包,那就等一会再发一次,但不是无限次的重传,如果连续多次重新传输都没有响应回来,就会认为网络问题从而放弃TCP连接,同时每一次超时重传的时间间隔也会越来越长(3)连接管理:(1)建立连接:当代码执行到 Socket new Socket(serverIp,serverPort)时就会建立连接,这个过程无法感知,由内核来完成三次握手:通信双方需要进行三次握手后才能完成连接,握手就相当于给对方发送一个不包含业务数据的数据包,相当于通知而已握手是由客户端发起的,当客户端要建立连接时会先发送一个SYN同步报文给服务器,告诉服务器我要和你建立连接,此时服务器会返回一个ACK应答报文告诉客户端:好的,我收到了你的请求,毕竟连接是两个人的事,不是一个人的当方面同意就行,所以服务器也会给客户端发送一个SYN来告诉客户端我也要和你建立连接,当客户端收到这个建立连接后会接着给服务器返回一个ACK表示收到这样一来一回就相当于两个人总共发送了四次信息,但为什么是三次握手呢?原因在于服务器的ack和syn在发送的时可以一起打包发给客户端,因为这两个报文都有内核在同一时间来完成,为了减少一个ack的开销,就把ack捎带到syn上一起返回给了客户端,从而完成了建立连接三次握手的意义:1.投石问路:先初步验证通信链路是否畅通(地铁早上空车跑一趟)2.验证通信双方的发送能力和接收能力是否正常3.进行一些必要的协商:最关键的是协商32位序号,通信过程中会出现传输断开,再连接再传输再断开反复的过程,为了避免前朝的剑斩本朝的官,所以要协商好新连接接下来数据的32位序号如何定义初始值所以TCP的三次握手是在验证当前的环境是否符合可靠传输,本身不是保证可靠传输,只是为了验证后续能否进行可靠传输TCP的握手必须是三次吗?四次是可以但是没必要,为了降低成本提高效率,两次不可以,这样无法得知双方的通信路线是否畅通(2)断开连接:对于断开连接来说,客户端和服务器谁都有可能发起断开连接四次挥手:和握手一样,只有报文,没有载荷,没有业务当客户端发起断开连接时,会给服务器发送一个FIN结束报文(6个标志位之一),服务器这时返回一个ack告诉客户端我收到了你的断开请求,然后接着发送一个FIN给客户端,这时客户端收到FIN后返回一个ack,此时双方消除对方的信息从而断开连接.这个流程和三次握手类似,那么能不能也合并为三次挥手呢?能合并:握手中的syn和ack都是由内核同一时间进行发送,所以能合并不能合并:但在挥手中,ACK的返回由内核控制,收到FIN之后立刻触发,但FIN的返回是由应用程序控制的,也就是代码中调用了Socket.close方法才会触发FIN但不是说绝对不能合并,看ack和FIN返回的时机,如果时间短,有可能合并,比如你两分钟内下了两单,那么商家就会一起打包发给你,但若果你隔天下了两单,收到的大概率是错开的两个包裹(这取决于你的代码逻辑是咋写的)TCP的常见状态:1.LISTEN:服务器持有的状态,表示已启动,准备就绪2.ESTABLISHED:客户端和服务器都会持有的状态,表示两者建立连接,可以进行通信3.CLOSE_WAIT:哪方收到FIN就会进入这个状态,直到这一方主动发出FIN就会消失4.TIME_WAIT:主动断开的一方会进入的状态,作用是等待一定时间,是为了处理最后一个ACK丢包的特殊情况(4)滑动窗口:优化效率,批量发送,批量等待ACK这就类似于下载时候的进度条的实时下载:窗口表示批量传输多少数据,不需要等待ACK,相当于一秒钟下载多少个MB,这些MB不需要等待ACK,是一次性的前一部分下载完了会接着往后下载,就像一个窗口在滑动但滑动窗口只是在亡羊补牢,窗口的大小决定传输的效率,但前提是可靠传输,所以窗口不能无限大,滑动窗口不会破坏可靠传输滑动窗口下丢包1.丢ACK:不做处理,因为丢的ACK后面的ACK带回来的消息中可以推断出丢的ACK(你在哪里上学?我孩子都上小学了)2.丢数据:不断返回数据的确认信号,提醒发送方丢数据了,重传数据之后,返回的未必是丢包的下一条序号,可能是丢的数据后面的数据都已经接收到,返回的是最新已经接收数据的下一个序号.多次重复的发送ACK进行确认,也是为了区分数据是真的丢了还是在路上迟到了,就像拳击比赛的倒数快速重传VS超时重传:快速重传是在连续接收到3个ACK时立马重传,但当迟迟未收到对方的ACK时,这时会进入超时重传,当等待时间结束,TCP默认报文或者ACK在路上丢失,于是会重新传输这份丢失的数据,,重新传输后,重返这一份ACK,而这份重复的数据如果是ACK丢了会直接被丢弃(5)流量控制:保证可靠性当窗口特别大时,传输快,接收方处理不来就会丢包所以流量控制是为了对滑动窗口大小进行控制流量控制根据接收方的处理能力来反向限制发送方的发送速度(窗口大小)1.接收方的处理能力衡量:接收方缓冲区剩余空间大小2.衡量后如何通知发送方?如何限制?把接收缓冲区剩余空间大小的值通过ack通知给发送方,也就是16位窗口大小,发送方从而按照这个值来决定下一轮窗口大小是多少,但窗口大小并不是16位最大就只能是64kb,在TCP协议的选项中,有窗口扩展因子,通过这个来调整窗口大小上限如果接收方满了,此时发送方就得暂停发送,但暂停就无法触发ack了,那么后面缓冲区有了空间也无法通知给发送方了?当ack返回窗口0时,发送方会暂停发送数据,但是ack不能停,所以发送方会定期发送一个窗口探测包,无载荷,只为触发ack来询问接收方新窗口的大小(6)拥塞控制:与流量控制类似,都是对滑动窗口做出限制流量控制:依据接收方的处理能力拥塞控制:依据通信路径的处理能力把复杂的中间路径看成是一个整体,水多加面,面多加水,先慢后快,丢包了就减速由于这两个机制来控制着窗口大小,所以窗口大小的动态变化的,逐渐动态平衡窗口大小取决于两个拥塞窗口大小和流量控制窗口大小,取两者的最小值来决定最终的窗口大小这两个机制同时存在,同时工作,同时产生作用,下一轮发数据用较小值拥塞窗口动态曲线:慢(指数增长)-达到阈值-线性增长(快)-丢包-重置新阈值(丢包位置窗口大小/2)-降低到新阈值-线性增长这个曲线全程动态变化,根据网络状态实时变化(7)延时应答:提升效率,减少ack次数我本来收到1-1000后返回1001ack,但我先不着急发送,等我接收了1001-2000时再返回一个ack,这个延时应答只延时两个包,接收两个包之后必须发送一个ack延时应答只服务于正常、有序、无丢包的顺畅传输一旦丢包乱序立刻切回「即时应答 重复 ACK」正常触发快速重传完全不冲突。回归有序之后再重新进入延时应答(8)捎带应答:就跟三次握手中间把ack和syn打包一样(9)面向字节流:注意粘包问题包与包之间的边界不明确,粘的是应用层数据包服务器返回的包在缓冲区中,应用层read时,无法完整区分从哪里到哪里是一个完整的应用层数据包解决:在数据开头的地方,添加固定属性表示长度:约定两个字节表示长度(10)异常情况:1.进程崩溃:和四次握手完全相同2.正常关机:触发FIN,然后进入四次握手3.突然关机:接收方挂了:发送方超时重传几次后主动断开,会发送一个复位报文RST进行收尾发送方挂了:接收方只能感知到发送方突然沉默了,会定期发送一个心跳包给接收方,只为触发一次ack4.网线断了:接收方发心跳包;发送方超时重传,就是把上面两种方法结合