TCP协议是网络多层协议中运输层的最重要的协议之一,运输层是两台主机的进程之间的通信。除了TCP还有一个是UDP协议(用户数据包协议)
TCP全称是Transmission Control Protocol,意思是传输控制协议
一、TCP简介
1.TCP协议两个对等运输实体之间进行传送的数据单位是:TCP报文段
2.TCP提供的是面向连接的服务,在传送数据之前必须建立连接,数据传送完成之后需要关闭连接,TCP只可点对点,不可广播或多播,TCP连接是可靠的运输服务。
3.TCP的工作方式类似于打电话,打电话之前需要先拨号(号码就是连接的IP+端口号)连接,通话结束之后关闭连接
4.TCP提供可靠交付,即TCP传输的数据无差错、不丢失、不重复且有序
5.TCP支持全双工通信,即通信双方可随时发送数据,发送方发送完数据会先放到发送缓存中,发送方发送完毕就可以干别的事去了,TCP会在合适的时机将数据发送给接受方,接收方接收到数据会先把数据放到接收缓存中,应用程序会在合适的时机在缓存中获取数据。
6.TCP是面向字节流的,流入到进程或从进程中流出的是字节序列。而发送时和接收时除了传输的业务数据,可能还会额外加一些字节数据,用于发送方和接收方处理。比如在微信聊天中需要提醒接收方会添加@XXX,而接收方只需要接收有用信息,@信息是和业务无关的。
7.TCP连接的双方不是两个主机、不是两个IP地址、不是两个应用程序、而是两个套接字,每个套接字socket=(IP地址:端口号),每一个TCP连接必须有唯一的两个套接字,即TCP连接={套接字1,套接字2}={(IP1,port1),(IP2,port2)}
二、TCP三次握手和四次挥手
TCP是面向连接的协议,所以协议的基础就是需要有连接,而发起连接的一方可以称为客户端,等待连接的一方可以称为服务端。而TCP建立连接的过程称为握手,每次握手客户端和服务器之前需要交换三个报文段,因此也叫做“三次握手”。
在RFC973(TCP标准文档)中使用的名称是three way handshake,这里的handshake没有用复数,所以三次握手不太准确,应该叫三次报文握手,因为人与人之间握手的意思就是已经连接上了,手上摇晃了三次之后才最终握上,所以准确点说应该是握手成功了一次。但是三次握手的说法比较普遍,所以本文也叫做三次握手。在理解TCP连接之前,先来了解一些常量的含义
SYN:发起一个连接,同步位
FIN:关闭一个连接
ACK:确认连接有效
RST:重置连接
seq:序号,每次发送一次数据,seq=上一个seq+1
ack:应答序号
上图是TCP连接三次握手示意图
第一次握手:A向B发送连接请求报文,同步完syn=1,初始化序号seq=X,TCP协议规定syn=1的报文不允许携带数据,所以SYN=1即表示发起连接请求,当A发送第一次握手之后,A就进入了SYN-SENT状态,表示同步已发送
第二次握手:B接收到A发送的请求报文,如果同意连接,则向A发送确认。发送同步报文段SYN=1,seq=y,确认报文段ACK=1,确认序号ack=x+1。此时服务端进入SYN-RCVD状态,表示同步已接收
第三次握手:A接收到B的确认报文,再向B发起确认,设置ACK=1,确认序号seq=x+1,ack=y+1,此时TCP连接完成,双方都进入ESTAB-LISHED状态,表示连接以建立。
那么为什么需要三次,而不是两次就行,还需要最后再发送一次确认呢?或者说如果没有最后一次确认会发送什么?看看下面这种情况:
A发出连接请求,但是在网络中长时间滞留了而没有发送到B,此时由于A久久没有接收到B发送过来的确认请求,所以A就将连接释放了,在A释放了连接之后B又接收到了A发送过来迟到的连接请求,由于B不知道此时A已经释放了连接了,所以B认为是有效的连接,
此时B向A发送确认,同意建立连接(如果只要两次握手,那么此时B就已经进入了ESTAB-EISHED状态),此时A已经释放了连接,所以接收到B的确认请求也不会理睬,但是B还是认为连接有效而一直等待着A发送数据,就会导致B持有无效的连接而浪费资源。
而如果采用三次握手,那么B在发送完确认之后,只会进入到SYN-RCVD状态,并不会建立连接的等待数据的发送,除非A再次发送了确认请求。
TCP连接是双向的,所以TCP连接的释放是双方都可以发起的,以A为例发起释放连接,需要经过四次挥手,过程如下图示:
在连接释放之前,A和B都是出于ESTAB-LISHED状态,且可以数据传输,此时A开始进行关闭
第一次挥手:A向B发送关闭连接请求,FIN=1,seq=u(u的值等于数据传输的最后一个请求的序号+1),此时A进入FIN-WAIT-1状态,等待B确认。TCP规定FIN报文不可携带数据
第二次挥手:B接收到A的关闭连接请求,并发出关闭确认,ACK=1,seq=v(v的值等于B传送的最后一个字节的序号+1),ack=u+1,然后B进入CLOSE-WAIT(等待关闭)状态,且需要通知应用进程A需要关闭连接,不再发送数据过来了。
此时TCP连接进入半关闭状态,也就是A-B的连接已经关闭,B-A的连接还没有关闭,但A接收到B的关闭确认之后进入FIN-WAIT-2状态,等待B发送关闭连接报文
第三次挥手:若B没有数据再发送给A,则向A发送关闭连接报文,FIN=1,ACK=1,seq=w(w的值等于B传送的最后一个字节的序号+1),ack=u+1,并且B进入LAST-ACK(最后确认)状态,等待A的确认
第四次挥手:A收到B的关闭连接报文之后,再向B发送一次确认报文,ACK=1,seq=u+1,ack=w+1,并且进入到TIME-WAIT(时间等待)状态,需要等待2MSL时间之后才会进入到CLOSED状态,而B接收到关闭确认报文之后就直接进入CLOSED(关闭)状态。
这里有两个问题,一是为什么需要有四次握手,而不是两次或三次?二是A在发送关闭确认请求之后,为什么还需要等待2MSL长时间才进入CLOSED状态?(MSL一般是2分钟,2MSL就是4分钟)
回答一:和三次握手一样,挥手只用两次肯定是不够用的,那三次为什么也不够呢,因为第一次关闭连接是A向B发起的,表示A不会发送数据给B了,但是此时可能B还有数据需要传送给A,如果B在数据还没有传输完成就通知A,则数据就会丢失;如果B在数据传输完成之后再通知A,那么此时可能已经超时了,导致A以为发起的关闭连接失效了就会重新发送多次的关闭连接。而四次挥手比三次握手多的一次报文是B向A发送的关闭连接请求,表示B也已经不会发送数据给A了,这样就确保了双方都没有数据发送了,且都对对方的关闭连接做了关闭确认。
回答二:设置TIME-WAIT状态等待2MSL主要有两个原因:
原因1:防止第四次挥手丢失,如果A在发送第四次挥手报文之后马上进入CLOSED状态,而B有可能没有接收到这个确认报文,就会一直处于LAST-ACK状态,在超时之后B就会重发关闭连接报文,而此时A已经进入CLOSED状态了,就会导致B无法进入CLOSED状态。
而设置了TIME-WAIT状态之后,A会等待2MSL时间,如果B重发了FIN+ACK报文,A还是可以接收到并且发出关闭确认报文,直到B进入CLOSED状态。
原因2:确保A最后发送的报文在网络中消失,MSL表示报文的最长寿命,达到时间报文就会失效,所以A等到2MSL时间之后,报文无论是否被B接收到肯定是会失效,这样就确保了此报文不会影响到下一个新的连接。而如果没有TIME-WAIT状态,而是A直接进入CLOSED状态,然后A又再次和B通过三次握手建立了连接,此时A最后发送的关闭确认报文可能还在网络中传输,就会出现旧的报文段出现的新的连接中。
三、TCP可靠传输
tcp一大特性就是可靠传输,而网络传输过程中,很容易就会出现丢失、重复、错误及延迟的问题,那么TCP能保证数据的传输过程不会丢失、不会重复、不会乱序,是如何达到的呢?主要有以下四点:
3.1、确认和重传机制(ack机制)
TCP连接的双方发送报文之后,必须等待接收方的ACK,否则就认为这个报文丢失了而重传报文,比如发送方发送了报文seq为x,则接收方会返回一个ack=x+1。另外发送方发出报文之后会启动一个计时器(RTO),计时时间到了还没有收到ack就会重新发送。另外接收方还会对接收到的报文进行校验,如果校验不通过会再ack上返回异常的报文seq,这样发送方就会重传这个seq。(ack可以优化成多个报文合并ack一次,只需要ack最后一条报文即可,可以减少ack次数)
3.2、数据排序机制(seq机制)
每条报文都包含一个seq序列号,这个序列号会从初始的seq值上进行递增,如果发送方发送seq=x,则接收方的ack=x+1,然后发送方下一次的报文seq就等于x+1。而如果seq=x+n的报文丢失了,发送方就很清楚是哪一个报文丢失了,从而可以重传。而接收方可以根据seq来对报文进行排序,并且可以丢弃掉重复的报文。
3.3、流量控制(滑动窗口机制)
发送方维持一个滑动窗口来控制发送方报文发送,防止发送方报文发送太快而导致接收方无法及时处理,所以将报文分成四类:
1.发送完成且已经确认的
2.发送完等待确认的
3.等待发送且允许发送的
4.窗口被占满暂时不允许发送的
窗口的左边在1-2类之间,右边在3-4类之间,通过滑动窗口来进行流量控制有三种方法,分别如下:
1.停等协议:窗口大小为1,即每次只可发送一个报文,等确认之后才可发送下一个报文
2.后退N步协议:窗口大小大于1,每次可以发送多个报文,当接收方连续发送三次连续的同一个序号的ack,则表示这个序号的报文丢失了,则发送方从这个序号开始到后面的所有报文都重发一次。比如丢失的是7,那么7后面的8,9,10...都会再重发一次
3.选择重传协议:选择重传是后退N步都优化版,即只重发丢失的报文
3.4、拥塞控制(慢启动、拥塞避免、快速重传、快速恢复)
当网络出现拥塞情况,这时发送方和接收方就会频繁的发生丢包报文以及重发的报文,这样就会使得本来就堵塞的网络变得更加的堵,如果不加控制就可能导致网络崩溃了。所以tcp添加来拥塞控制,通过控制拥塞窗口(cwnd)的大小和拥塞阈值(ssthreshold)的大小来进行拥塞控制
a、慢启动
此时一般是(记住是一般情况)cwnd<ssthreshold,此时cwnd呈指数形式增长,1、2、4、8、16、32...这种增长趋势
b、拥塞避免
此时一般cwnd>ssthreshold,此时cwnd呈线性增长,32、33、34、35...这种增长趋势
c、拥塞解决
此时一般是遇到了网络拥塞的状况,解决方法是拥塞阈值乘性减即ssthreshold=cwnd/2,cwnd=1,或者ssthreshold=cwnd/2,cwnd=ssthreshold,这两种情况在后面说明
d、快速恢复
一般是启用拥塞结局策略之后,根据不同的情况,进入慢启动或者拥塞避免阶段。