一.TCP和UDP的区别:
TCP和UDP都是传输层协议,以IP:port来区分,不同协议的port可以相同。
TCP头部占20字节,源端口和目的端口各占2个字节, 端口范围是0~65535。
UDP头部占8个字节,其中端口和长度字段各2个字节,所以UDP的最大包长是64k。
主要有以下区别:
协议 | TCP | UDP |
是否连接 | TCP是面向连接的。 | UDP是一个简单的面向数据报的传输层协议,它是面向无连接的。 |
可靠性 | 提供可靠的字节流服务。 | 不提供可靠性。 |
是否粘包 | TCP是字节流,包之间没有界限,必须要处理粘包。 | UDP头部有2个字节表示包长,每个包都有长度,UDP最大包长是64k,即2^16。 |
传输速度 | TCP由于在传输数据前要建立连接,提供超时重传等功能,所以速度慢 | UDP不提供这些功能,故而传输速度快。 |
应用场合 | TCP一般用在传输数据量大,要求可靠性的场合。 | UDP一般用在传输数据量小,对可靠性要求不高的场合。 |
应用层协议 | HTTP,Telnet,FTP | DNS,SNMP(简单网络管理系统) |
二. TCP建立连接、释放连接
TCP(Transmission Control Protocol) 传输控制协议。TCP是主机对主机层的传输控制协议,提供可靠的连接服务,采用三次握手确认建立一个连接:
位码即tcp标志位,有6种标示:SYN(synchronous建立联机) ACK(acknowledgement 确认) PSH(push传送) FIN(finish结束) RST(reset重置) URG(urgent紧急)
Sequence number(seq) Acknowledge number(ack)
第一次握手:主机A发送位码为syn=1,随机产生seq number=1234567的数据包到服务器,主机B由SYN=1知道,A要求建立联机;
第二次握手:主机B收到请求后要确认联机信息,向A发送ack number=(主机A的seq+1),syn=1,ack=1,随机产生seq=7654321的包
第三次握手:主机A收到后检查ack number是否正确,即第一次发送的seq number+1,以及位码ack是否为1,若正确,主机A会再发送ack number=(主机B的seq+1),ack=1,主机B收到后确认seq值与ack=1则连接建立成功。
完成三次握手,主机A与主机B开始传送数据。
在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接。
最初两个TCP端口都处于CLOSED状态。服务器B首先建立传输控制块TCB,进入LISTEN状态,等待客户的连接请求。
第一次握手:客户端A发送标志位SYN=1,seq=x(随机产生)的请求包到服务器,客户端并进入SYN_SEND(同步-已发送)状态,等待服务器确认;
第二次握手:服务器B收到A的请求包,如同意连接,必须发送确认包给客户端。确认包除了确认作用外,还有B向A发送请求连接的作用,所以确认报文中SYN=1,ACK=1,seq=y,ack=x+1。此时服务器B进入SYN_RECV状态。
第三次握手:客户端A收到服务器B的确认包,还要给B给出确认。确认包ACK=1,ack=y+1,seq=x+1,此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
第三次seq=x+1的原因是:SYN=1的包不能携带数据,但要消耗一个序号,而ACK=1的包可以携带数据,但如果它不携带数据,则不消耗序号。
完成三次握手,客户端与服务器开始传送数据.
一个完整的三次握手也就是 请求---应答---再次确认
断开连接:断开连接比较麻烦,需要4次挥手。
为何要4次挥手:
因为TCP协议是全双工类型的协议,A和B都需要向对方提出断开连接,双方一去一回,总共四次挥手。
CLOSE_WAIT状态的问题:
在项目实践中,会经常遇到CLOSE_WAIT状态。其主要原因是TCP断开连接时,未完成四次挥手。
CLOSE_WAIT状态只会出现在被动关闭的一方,一般是客户端,也就是客户端接收到服务端的FIN包后,发完ACK包后,由于某些原因,客户端一直未发送FIN包给服务端,这样服务器端进入FIN_WAIT2,而客户端进入CLOSE_WAIT状态。
如果此时不close被动关闭方的socket,两边的状态永远不变,就会导致CLOSE_WAIT过多,也会逐渐耗尽socket资源。
三. TCP拥塞控制
常见的 TCP 拥塞控制算法:
目前 Linux 内核默认的 Reno 算法:基于丢包的拥塞控制算法 ,由于非常著名,所以常常作为教材的重点说明对象。
Google 的 BBR 算法:BBR 算法不将出现丢包或时延增加作为拥塞的信号,而是认为当网络上的数据包总量大于瓶颈链路带宽和时延的乘积时才出现了拥塞。
下面着重讲解 Reno 算法。
它将拥塞控制的过程分为四个阶段:慢启动、拥塞避免、快重传、快恢复,对应的状态图如下:
首先理解几个名词:
拥塞窗口:cwnd。慢开始门限:ssthresh
慢启动:
慢启动阶段思路是不要一开始就发送大量的数据,先探测一下网络的拥塞程度,也就是说由小到大逐渐增加拥塞窗口的大小。在没有出现丢包时每收到一个 ACK 就将拥塞窗口大小加一(单位是 MSS,最大单个报文段长度)。每轮次发送窗口增加一倍,呈指数增长,若出现丢包,则将拥塞窗口减半,进入拥塞避免阶段。简而言之:慢慢探测网络,没有丢包时,从小到大逐渐增加拥塞窗口,指数增长。一旦丢包,拥塞窗口减半,进入拥塞避免。
拥塞避免:
拥塞窗口每轮次加一,呈线性增长;当收到对一个报文的三个重复的ACK 时,认为这个报文的下一个报文丢失了,进入快重传阶段。
快重传:
当发送方收到三个重复ACK时,就要将报文立刻重传,而不是等到超时重传计时器超时再重传。快重传完成后进入快恢复阶段。
快恢复:
将慢启动门限ssthresh修改为当前拥塞窗口值的一半,同时更新拥塞窗口值为ssthresh,然后进入拥塞避免阶段,重复上述过程。
当cwnd < ssthresh时,采用慢开始算法。
当cwnd > ssthresh时,采用拥塞避免算法。
当cwnd == ssthresh时,慢开始和拥塞避免都可使用。