面试官:TCP算是讲完了,那怎么优化呢 20220813

哈喽,大家好,我是指北君。

TCP/IP协议经常在面试中会被问到,基础的会问三次握手和四次挥手,更深一点可能会问TCP如何优化等问题,下面我们来再详细了解一下这些问题。

1. 前言

TCP/IP(Transmission Control Protocol / Internet Protocol)

TCP传输控制协议指一种面向连接的、可靠的、基于字节流的传输层通信协议。

下面我们会先回顾一下其报文格式,三次握手,四次挥手的一些基础知识。

TCP报文格式:

TCP01

各部分的含义如下:

  • 源端口,16bits 0~65525
  • 目标端口 16bits
  • sequence number : 数据序号,32 bits,TCP链接中传送的数据流中的每一个字节都编一个序号,序号字段的值指本报文段所发送的数据的第一个字节的序号。
  • acknowledgement number: 确认序号: 32 bits , 期望收到对方的下一个报文段的数据的第一个字节的序号
    • URG: 紧急bit, URG=1时表示紧急指针字段有效,报文中有紧急字段,应该尽快传送。
    • ACK:确认bit,当ACK=1时,确认字段号生效
    • PSH:推送比特,接收方TCP收到PSH=1的报文段,就尽快地交付给接收应用进程,而不是在等到整个缓存都填满了再上传
    • RST: 复位bit ,当RST=1时,表示TCP连接中出现严重错误,必须释放连接,然后重新建立传输连接。
    • SYN:同步bit, 当SYN=1时,表明这是一个连接请求或者连接接受报文
    • FIN:终止bit, 当FIN=1时,表明此报文的发送端的数据已经发送完毕,并要求释放连接。
  • 接收窗口: 16bit 用来控制对方发送的数据量,TCP连接的一端根据设置的缓存空间大小确定自己的接收窗口大小,然后通知对方确定对方的发送窗口的上线。
  • 检验和: 16bit 检验和字段检查的范围包括首部和数据这两部分。在对方计算检验和时,要在TCP报文段的前面加上12字节的伪首部。
  • 紧急指针字段 16bit, 紧急指针指出在本报文段中的紧急数据的最后一的字节的序号

TCP三次握手:

三次握手的流程基本如下:

  • 客户端发送连接请求,报文中SYN=1 , seq=10000
  • 服务端收到请求之后会告诉客户端收到请求,ACK=1 , 确认序列号在请求序列号上加1, ack=10001,并向客户端请求SYN=1, seq=20000。
  • 客户端收到请求之后告诉服务端收到请求,ACK=1,确认序列号在请求序列号上加1 seq=20001,至此TCP连接建立。

下图示意了一个完整的TCP三次握手

TCP四次挥手:

四次挥手握手的流程基本如下:

  • 客户端传输完成,发送断开连接请求,其中FIN=1,seq=25000。
  • 服务端收到断开请求之后,会立即发送响应,ACK=1, ack=25001,表示收到断开连接的请求。
  • 服务端相关连接处理好之后,同样会向客户端发送断开请求,FIN=1, seq =30000,
  • 客户端等待一段时间之后收到服务端断开连接的请求,会发送响应数据,ACK=1,seq=30001.至此TCP连接断开。

下图为完整的TCP四次挥手完整示意

2.TCP在面试中如何回答

请讲一下对TCP的理解

回答如下:

TCP/IP协议时传输层面相连接的安全可靠的一个传输协议,三次握手机制是为了保证能建立一个安全可靠的连接。

第一次握手是由客户端发起的,客户端回向服务端发送一个报文,在报文里面,SYN=1.表示发起一个新连接。服务段收到此报文之后,就明白客户端需要建立连接,此时会发送确认消息包 标志位 ACK=1。 此时为了保证服务端确认客户端能收到消息,就需要客户端在发送收到消息的响应报文,ACK=1。通过上述的3此握手,客户端和服务端均可以确认能收到互相之间的消息,此时安全连接建立。

四次挥手也是为了保证完全释放TCP连接,四次挥手又传输完数据的一方先发起。假设客户端数据传输完成,则发起断开连接请求FIN=1 给到服务端 并状态设置成FIN_WAIT_1, 服务端收到断开连接请求后,会立马发送一个响应请求, 服务段状态变成CLOSE_WAIT。然后客户端收到服务端的响应之后进入FIN_WAIT_2状态。

服务端数据传输完成之后会发动断开连接请求,并将状态设置成LAST_ACK,客户端收到请求之后发送最终确认关闭请求给服务端,进入TIMW_WAIT状态,一段时间之后客户端连接close。 服务端收到最终相应之后也会由LAST_ACK状态变成CLOSE状态,

为何挥手需要4次

TCP有半关闭的特性,其连接的一段结束它的发送后还能接收来自另一端数据的能力。双方都可以在数据传输完成之后发起断开连接的请求,对方确认进入半关闭状态之后,另一边也没有数据发送时则发送断开连接通知,对方确认后就完全关闭TCP连接。 尽量让双方安全完成数据传输。

挥手报文丢失的几种情况

第一次挥手丢失报文

假设客户端发起断开连接请求后,客户端一直没有收到服务端的ACK响应报文,那么客户端会触发超时重传机制,超过一定的重传次数之后,直接进入close状态

第二挥手丢失报文

服务端收到断开连接请求,并发送了响应报文,但是客户端一直都没有收到报文。这种情况和第一种情况相似,客户端会触发超时重传

第三次挥手丢失报文

服务端数据传输完成后,发送FIN断开连接通知之后,服务端进入LAST_ACK状态,但是客户端没有收到请求,一直没有响应。此时服务端会触发超时重传机制。

第四次挥手丢失报文

客户端收到服务端的FIN报文之后,会发送响应报文,并进入TIMW_WAIT状态,一段时间后客户端TCP连接关闭。 但是服务端没有收到响应报文,服务端会出发超时重传机制,最后close。

3. TCP的优化

全队列溢出优化

当大量TCP请求进入到服务端时,服务端会将已经返回响应的请求的连接存放到半连接队列(SYN queue),当服务端收到客户端发来的ACK包之后,连接建立,此时会将连接从SYN-queue中取出来放到全连接队列(accept queue),等待进程调用accept函数时将连接取出来。

这两个队列都有长度限制,超过长度限制之后,内核对直接丢弃链接或者返回RST包。Linux默认时丢弃连接, 也可以设置向客户端发送RST复位报文,通知客户端连接失败。(修改tcp_abort_on_overflow的值为1).

通常情况下tcp_abort_on_overflow应该保持默认值,这样有利于应对突发流量。

同时我们可以适当增大全连接队列的长度限制(Tocmate中可以设置acceptCount,Nginx可以调整tcp_max_syn_backlog的值)

TCP Fast Open

当TCP连接使用完之后,客户端再次向服务器请求建立连接,报文中可以记录此前的Fast Open Cookie。服务器对Cookie进行校验之后,如果Cookie有效,就可以将数据给到程序处理,相当于绕过了三次握手,可以更快的建立连接。

linux中可以设置tcp_fastopen 来打开Fast Open功能。(PS: Fast Open需要客户端和服务端同时支持才有效)

连接复用

在客户端发起建立建立线连接时,可以复用处于TIME_WAIT状态的连接,linux中可以设置tcp_tw_reuse参数。

传输过程中调节缓冲区大小

系统会为每个连接建立缓冲区, 接收缓冲区可以根据系统空闲内存的大小来调节接收窗口。

发送缓冲区的调节功能时自动开启的,接收缓冲区设置tcp_moderate_rcvbuf=1表示开启调节功能。

高并发服务中,可以调整缓冲区的动态调整可以达到最大宽带延积。如果服务时网络IO型的话,可以调大tcp_mem的上限,让TCP连接可以使用更多的系统内存,有利于提高并发。

总结

针对TCP的优化,有如下的的一些建议:

  1. 服务端配置
    • 可以使用新版本的服务器
    • 适当增大TCP的全连接队列最大限制。
    • TCP快速打开
  2. 应用程序
    • 减少数据发送,压缩要发送的数据
    • 减少数据发送距离,部署CDN等
    • 尽量重用TCP连接

以上是本篇的内容,关于TCP的优化,还有什么更好的点,我们可以评论区走一波哈!

Java Geek Tech wechat
欢迎订阅 Java 技术指北,这里分享关于 Java 的一切。