TCP/IP协议

1 TCP/IP协议分层模型

TCP/IP协议包含了⼀系列的协议,也叫TCP/IP协议族(TCP/IP Protocol Suite,或TCP/IP Protocols),简称TCP/IP。 TCP/IP协议族提供了点对点的连结机制,并且将传输数据帧的封装、寻址、传输、路由以及接收⽅式,都予以标准化。
国际标准化组织ISO为了使⽹络应⽤更为普及,推出了OSI参考模型,即开放式系统互联(Open System Interconnect)模型, ⼀般都叫OSI参考模型。 OSI参考模型是ISO组织在1985年发布的⽹络互连模型,其含义就是为所有公司使⽤⼀个统⼀的规范来控制⽹络,这样所有公司遵循相同的通信规范,⽹络就能互联互通了。
OSI模型定义了⽹络互连的七层框架 (物理层、数据链路层、⽹络层、传输层、会话层、表示层、应⽤层) ,每⼀层实现各⾃的功能和协议,并完成与相邻层的接⼝通信。

OSI框架通信协议
应⽤层HTTP、 SMTP、 SNMP、 FTP、 Telnet、 SSH
表示层XDR、 ASN.1、 SMB、 AFP、 NCP
会话层ASAP、 SSH、 RPC、 NetBIOS、 ASP、 Winsock、 BSD Sockets
传输层TCP、 UDP、 TLS、 RTP、 SCTP、 SPX、 ATP、 IL
⽹络层IP、 ICMP、 IGMP、 IPX、 BGP、 OSPF、 RIP、 IGRP、 EIGRP、 ARP、RARP
数据链路层以太⽹、令牌环、 HDLC、帧中继、 ISDN、 ATM、 IEEE 802.11、 FDDI、 PPP
物理层例如铜缆、⽹线、光缆、⽆线电

TCP/IP协议是Internet互联⽹最基本的协议,其在⼀定程度上参考了七层ISO模型。 OSI模型共有七层,从下到上分别是物理层、数据链路层、⽹络层、运输层、会话层、表示层和应⽤层。但是这显然是有些复杂的,所以在TCP/IP协议中,七层被简化为了四个层次。TCP/IP模型中的各种协议,依其功能不同,被分别归属到这四层之中,常被视为是简化过后的七层OSI模型。
TCP/IP协议与七层ISO模型的对应关系:

1. 应用层
应⽤层包括所有和应⽤程序协同⼯作,并利⽤基础⽹络交换应⽤程序的业务数据的协议。⼀些特定的程序被认为运⾏在这个层上,该层协议所提供的服务能直接⽀持⽤户应⽤。应⽤层协议包括HTTP(万维⽹服务)、 FTP(⽂件传输)、 SMTP(电⼦邮件)、SSH(安全远程登陆)、 DNS(域名解析)以及许多其他协议
2. 传输层
传输层的协议,解决了诸如端到端可靠性问题,能确保数据可靠的到达⽬的地,甚⾄能保证数据按照正确的顺序到达⽬的地。传输层的主要功能⼤致如下:

  • 为端到端连接提供传输服务;
  • 这种传输服务分为可靠和不可靠的,其中TCP是典型的可靠传输,⽽UDP则是不可靠传输;
  • 为端到端连接提供流量控制、差错控制、 QoS(Quality of Service)服务质量等管理服务

传输层主要有两个性质不同的协议: TCP传输控制协议UDP⽤户数据报协议

  • TCP协议是⼀个⾯向连接的、可靠的传输协议,它提供⼀种可靠的字节流,能保证数据完整、⽆损
    并且按顺序到达。 TCP尽量连续不断地测试⽹络的负载并且控制发送数据的速度以避免⽹络过载。另
    外, TCP试图将数据按照规定的顺序发送。
  • UDP协议是⼀个⽆连接的数据报协议,是⼀个“尽⼒传递”和“不可靠”协议,不会对数据包是否已经到达⽬的地进⾏检查,并且不保证数据包按顺序到达。

总体来说, TCP协议传输效率低,但可靠性强; UDP协议传输效率⾼,但可靠性略低,适⽤于传输可靠性要求不⾼、体量⼩的数据(⽐如QQ聊天数据)
3. 网络层
TCP/IP协议⽹络层的作⽤是在复杂的⽹络环境中为要发送的数据报找到⼀个合适的路径进⾏传输。
简单来说,⽹络层负责将数据传输到⽬标地址,⽬标地址可以是多个⽹络通过路由器连接⽽成的某⼀个地址。另外,⽹络层负责寻找合适的路径到达对⽅计算机,并把数据帧传送给对⽅,⽹络层还可以实现拥塞控制、⽹际互连等功能。⽹络层协议的代表包括: ICMP、 IP、 IGMP等。
4. 链路层
链路层有时也称作数据链路层或⽹络接⼝层,⽤来处理连接⽹络的硬件部分。该层既包括操作系统硬件的设备驱动、 NIC(⽹卡)、光纤等物理可⻅部分,还包括连接器等⼀切传输媒介。在这⼀层,数据的传输单位为⽐特。其主要协议有ARP、 RARP等。

2 HTTP报文传输原理

2.1 HTTP报文传输

利⽤TCP/IP进⾏⽹络通信时,数据包会按照分层顺序与对⽅进⾏通信。发送端从应⽤层往下⾛,接收端从链路层往上⾛。从客户端到服务器的数据,每⼀帧数据的传输的顺序都为:应⽤层->运输层->⽹络层->链路层->链路层->⽹络层->运输层->应⽤层。
以⼀个HTTP请求的传输为例,请求从HTTP客户端(如浏览器)和HTTP服务端应⽤的传输过程,⼤致如下图所示:

2.2 封装和分用

数据通过互联⽹传输的时候不可能是光秃秃的不加标识,如果这样数据就会乱。

  • 数据在发送的时候,需要加上特定标识,加上特定标识的过程叫做数据的封装
    • 在数据封装时,数据经过每个层都会打上该层特定标识,添加上头部。在传输层封装时,添加的报⽂⾸部时要存⼊⼀个应⽤程序的标识符,⽆论TCP和UDP都⽤⼀个16位的端⼝号来表示不同的应⽤程序,并且都会将源端⼝和⽬的端⼝存⼊报⽂⾸部中。
    • 在⽹络层封装时, IP⾸部会标识处理数据的协议类型,或者说标识出⽹络层数据帧所携带的上层数据类型,如TCP、 UDP、 ICMP、 IGMP等等。 具体来说,会在IP⾸部中存⼊⼀个⻓度为8位的数值,称作协议域: 1表示为ICMP协议、 2表示为IGMP协议、 6表示为TCP协议、 17表示为UDP协议、等等。 IP⾸部还会标识发送⽅地址(源IP)和接收⽅地址(⽬标IP)。
    • 在链路层封装时,⽹络接⼝分别要发送和接收IP、 ARP和RARP等多种不同协议的报⽂,因此也必须在以太⽹的帧⾸部中加⼊某种形式的标识,以指明所处理的协议类型,为此,以太⽹的报⽂帧的⾸部也有⼀个16位的类型域,标识出以太⽹数据帧所携带的上层数据类型,如IPv4、 ARP、 IPV6、PPPoE等等。
  • 在数据使⽤的时候再去掉特定标识,去掉特定标识的过程就叫做分⽤
  • 数据封装和分⽤的过程⼤致为:发送端每通过⼀层会增加该层的⾸部,接收端每通过⼀层则删除该层的⾸部。

总体来说, TCP/IP分层管理、数据封装和分⽤的好处:分层之后若需改变相关设计,只需替换变动的层。各层之间的接⼝部分规划好之后,每个层次内部的设计就可以⾃由改动。层次化之后,设计也变得相对简单:各个层只需考虑分派给⾃⼰的传输任务。
TCP/IP与OSI的区别主要有哪些呢?

  • OSI参考模型注重 “通信协议必要的功能是什么”
  • TCP/IP则更强调 “在计算机上实现协议应该开发哪种程序”

2.3 数据传输过程

实际上,在传输过程中,数据报⽂会在不同的物理⽹络之间传递,还是以⼀个HTTP请求的传输为例,请求在不同物理⽹络之间的传输过程,⼤致如下图所示:

数据包在不同物理⽹络之间的传输过程中,⽹络层会通过路由器去对不同的⽹络之间的数据包进⾏存储、分组转发处理。构造互连⽹最简单的⽅法是把两个或多个⽹络通过路由器进⾏连接。路由器可以简单理解为⼀种特殊的⽤于⽹络互连的硬件盒,其作⽤是为不同类型的物理⽹络提供连接: 以太⽹、令牌环⽹、点对点的链接和FDDI(光纤分布式数据接⼝)等等。
物理⽹络之间通过路由器进⾏互连,随着增加不同类型的物理⽹络,可能会有很多个路由器,但是对于应⽤层来说仍然是⼀样的, TCP协议栈为⼤家屏蔽了物理层的复杂性。 总之,物理细节和差异性的隐藏,使得互联⽹TCP/IP传输的功能变得⾮常强⼤。

3 TCP协议的报文格式

3.1 报文格式说明

传输层TCP协议提供了⼀种⾯向连接的、可靠的字节流服务,其数据帧格式,⼤致如下图所示:

1. 源端⼝号
源端⼝号表示报⽂的发送端⼝,占16位。源端⼝和源IP地址组合起来,可以标识报⽂的发送地址。
2. ⽬的端⼝号
⽬的端⼝号表示报⽂的接收端⼝,占16位。⽬的端⼝和⽬的IP地址相结合,可以标识报⽂的接收地址。
TCP协议是基于IP协议的基础上传输的, TCP报⽂中的源端⼝号+源IP,与TCP报⽂中的⽬的端⼝号+⽬的IP⼀起,组合起来唯⼀性的确定⼀条TCP连接。
3. 序号(Sequence Number)
TCP传输过程中,在发送端出的字节流中,传输报⽂中的数据部分的每⼀个字节都有它的编号。序号(Sequence Number)占32位,发起⽅发送数据时,都需要标记序号。
序号(Sequence Number)的语义与SYN控制标志(Control Bits)的值有关。根据控制标志(Control Bits)中的SYN是否为1,序号(Sequence Number)表达不同的含义:

  • 当SYN = 1时,当前为连接建⽴阶段,此时的序号为初始序号ISN((Initial Sequence Number),通过算法来随机⽣成序号;
  • 当SYN = 0时在数据传输正式开始时,第⼀个报⽂的序号为 ISN + 1,后⾯的报⽂的序号,为前⼀个报⽂的SN值+TCP报⽂的净荷字节数(不包含TCP头)。⽐如,如果发送端发送的⼀个TCP帧的净荷为12byte,序号为5,则发送端接着发送的下⼀个数据包的时候,序号的值应该设置为5+12=17。

在数据传输过程中, TCP协议通过序号(Sequence Number)对上层提供有序的数据流。发送端可以⽤序号来跟踪发送的数据量;接收端可以⽤序号识别出重复接收到的TCP包,从⽽丢弃重复包;对于乱序的数据包,接收端也可以依靠序号对其进⾏排序。
4. 确认序号(Acknowledgment Number)
确认序号(Acknowledgment Number)标识了报⽂接收端期望接收的字节序列。如果设置了ACK控制位,确认序号的值表示⼀个准备接收的包的序列码,注意,它所指向的是准备接收的包,也就是下⼀个期望接收的包的序列码。
举个例⼦,假设发送端(如Client)发送3个净荷为1000byte、起始SN序号为1的数据包给Server服务端, Server每收到⼀个包之后,需要回复⼀个ACK响应确认数据包给Client。ACK响应数据包的ACK Number值,为每个Client包的为SN+包净荷,既表示Server已经确认收到的字节数,还表示期望接收到的下⼀个Client发送包的SN序号,具体的ACK值如下图左边的正常传输部分所示。

在上图的左边部分, Server第1个ACK包的ACK Number值为1001,是通过Client第1个包的SN+包净荷=1+1000计算得到,表示期望第2个Client包的SN序号为1001; Server第2个ACK包的ACKNumber值为2001,为Client第2个包的SN+包净荷=2001,表示期望第3个Server包的SN为2001,以此类推。
如果发⽣错误,假设Server在处理Client的第⼆个发送包异常, Server仍然回复⼀个ACKNumber值为1001的确认包,则Client的第⼆个数据包需要重复发送,具体的ACK值如上图右边的正常传输部分所示。
只有控制标志的ACK标志为1时,数据帧中的确认序号ACK Number才有效。 TCP协议规定,连接建⽴后,所有发送的报⽂的ACK必须为1,也就是建⽴连接后,所有报⽂的确认序号有效。如果是SYN类型的报⽂,其ACK标志为0,故没有确认序号。
5. 头部⻓度
该字段占⽤4位,⽤来表示TCP报⽂⾸部的⻓度,单位是4bit位。其值所表示的并不是字节数,⽽是头部的所含有的32bit的数⽬(或者倍数),或者4个字节的倍数,所以TCP头部最多可以有60字节(4*15=60)。没有任何选项字段的TCP头部⻓度为20字节,所以其头部⻓度为5,可以通过20/4=5计
算得到。
6. 保留字段
头部⻓度后⾯预留的字段⻓度为6位,作为保留字段,暂时没有什么⽤处。
7. 控制标志 (Control Bits)
共6个bit位,具体的标志位为: URG、 ACK、 PSH、 RST、SYN、 FIN。 6个标志位的说明,如下表所示

标志位说明
URG占 1 位,表示紧急指针字段有效。 URG 位指示报⽂段⾥的上层实体(数据)标记为“紧急”数据。当 URG=1时,其后的紧急指针指示紧急数据在当前数据段中的位置(相对于当前序列号的字节偏移量), TCP 接收⽅必须通知上层实体。
ACK占 1 位,置位 ACK=1 表示确认号字段有效; TCP 协议规定,接建⽴后所有发送的报⽂的 ACK必须为 1; 当ACK=0 时,表示该数据段不包含确认信息。当 ACK=1 时,表示该报⽂段包括⼀个对已被成功接收报⽂段的确认序号 Acknowledgment Number,该序号同时也是下⼀个报⽂的预期序号。
PSH占1位,表示当前报⽂需要请求推(push)操作; 当PSH=1时,接收⽅在收到数据后⽴即将数据交给上层,⽽不是直到整个缓冲区满。
RST占1位,置位RST=1表示复位TCP连接; ⽤于重置⼀个已经混乱的连接,也可⽤于拒绝⼀个⽆效的数据段或者拒绝⼀个连接请求。如果数据段被设置了RST位,说明报⽂发送⽅有问题发⽣。
SYN占 1 位,在连接建⽴时⽤来同步序号。当 SYN=1 ⽽ ACK=0 时,表明这是⼀个连接请求报⽂。对⽅若同意建⽴连接,则应在响应报⽂中使 SYN=1 和 ACK=1。 综合⼀下, SYN 置 1 就表示这是⼀个连接请求或连接接受报⽂。
FIN占 1 位,⽤于在释放 TCP 连接时,标识发送⽅⽐特流结束,⽤来释放⼀个连接。当 FIN =1 时,表明此报⽂的发送⽅的数据已经发送完毕,并要求释放连接。

在连接建⽴的三次握⼿过程中,若只是单个SYN置位,表示的只是建⽴连接请求。如果SYN和ACK同时置位为1,表示的建⽴连接之后的响应。
8. 窗⼝⼤⼩
⻓度为16位,共2个字节。此字段⽤来进⾏流量控制。流量控制的单位为字节数,这个值是本端期望⼀次接收的字节数。
9. 检验和
⻓度为16位,共2个字节。对整个TCP报⽂段,即TCP头部和TCP数据进⾏校验和计算,接收端⽤于对收到的数据包进⾏验证。
10. 紧急指针
⻓度为16位, 2个字节。它是⼀个偏移量,和SN序号值相加表示紧急数据最后⼀个字节的序号。
以上⼗项内容是TCP报⽂⾸部必须的字段,也称固有字段,⻓度为20个字节。接下来是TCP报⽂的可选项和填充部分。
11. 选项和填充
可选项和填充部分的⻓度为4n字节(n是整数),该部分是根据需要⽽增加的选项。如果不⾜4n字节,要加填充位,使得选项⻓度为32位(4字节)的整数倍,具体的做法是在这个字段中加⼊额外的零,以确保TCP头是32位(4字节)的整数倍。
最常⻅的选项字段是MSS(Maximum Segment Size最⻓报⽂⼤⼩),每个连接⽅通常都在通信的第⼀个报⽂段(SYN标志为1的那个段)中指明这个选项字段,表示当前连接⽅所能接受的最⼤报⽂段的⻓度。
由于可选项和填充部分不是必须的,所以TCP报⽂⾸部最⼩⻓度为20个字节。
⾄此, TCP报⽂⾸部的字段,就全部介绍完了。 TCP报⽂⾸部的后⾯,接着的是数据部分,不过数据部分是可选的。在⼀个连接建⽴和⼀个连接终⽌时,双⽅交换的报⽂段仅有TCP⾸部。如果⼀⽅没有数据要发送,也使⽤没有任何数据的⾸部来确认收到的数据,⽐如在处理超时的过程中,也会发送不带任何数据的报⽂段。

3.2 可靠性保障

  • 1.应⽤数据分割成TCP认为最适合发送的数据块。这部分是通过MSS(最⼤数据包⻓度)选项来控制的,通常这种机制也被称为⼀种协商机制, MSS规定了TCP传往另⼀端的最⼤数据块的⻓度。需要注意的是, MSS只能出现在SYN报⽂段中,若⼀⽅不接收来⾃另⼀⽅的MSS值,则MSS就定为536字节。⼀般来讲, MSS值还是越⼤越好,这样可以提⾼⽹络的利⽤率。
    1. 重传机制。设置定时器,等待确认包,如果定时器超时还没有收到确认包,则报⽂重传。
    1. 对⾸部和数据进⾏校验。
    1. 接收端对收到的数据进⾏排序,然后交给应⽤层,接收端丢弃重复的数据
    1. TCP还提供流量控制,主要是通过滑动窗⼝来实现流量控制。

4 TCP的三次握手

TCP连接的建⽴时,双⽅需要经过三次握⼿,⽽断开连接时,双⽅需要经过四次分⼿,那么,其三次握⼿和四次分⼿分别做了什么呢?⼜是如何进⾏的呢?
通常情况下,建⽴连接的双⽅,由⼀端打开⼀个监听套接字(ServerSocket)来监听来⾃请求⽅的TCP(Socket)连接,当服务器端监听开始时,必须做好准备接受外来的连接,在Java中该操作通过创建⼀个ServerSocket服务监听套接字实例来完成,此操作会调⽤底层操作系统(如Linux)的C代码中三个函数socket()、 bind()、 listen() 来完成。开始监听之后,服务器端就做好接受外来连接的准备,如果监听到建⽴新连接的请求,会开启⼀个传输套接字,称之为被动打开(Passive Open)。⼀段简单的服务端监听新连接请求,并且被动打开(Passive Open)传输套接字的Java示例代码,具体如下:
TCP连接的建⽴时,双⽅需要经过三次握⼿,⽽断开连接时,双⽅需要经过四次分⼿,那么,其三次握⼿和四次分⼿分别做了什么呢?⼜是如何进⾏的呢?
通常情况下,建⽴连接的双⽅,由⼀端打开⼀个监听套接字(ServerSocket)来监听来⾃请求⽅的TCP(Socket)连接,当服务器端监听开始时,必须做好准备接受外来的连接,在Java中该操作通过创建⼀个ServerSocket服务监听套接字实例来完成,此操作会调⽤底层操作系统(如Linux)的C代码中三个函数socket()、 bind()、 listen() 来完成。开始监听之后,服务器端就做好接受外来连接的准备,如果监听到建⽴新连接的请求,会开启⼀个传输套接字,称之为被动打开(Passive Open)。⼀段简单的服务端监听新连接请求,并且被动打开(Passive Open)传输套接字的Java示例代码,具体如下:

public class SocketServer {
    public static void main(String[] args) {
        try {
            //创建服务器 socket
            ServerSocket serverSocket = new ServerSocket(8080);
            //循环监听等待客户端的连接
            while (true){
                //监听到客户端连接,传输套接字被动开启
                Socket socket = serverSocket.accept();
                //开启线程进行连接的IO处理
                ServerThread thread = new ServerThread(socket);
                thread.start();
            }
        } catch (Exception e) {
            //处理异常
            e.printStackTrace();
        }
    }
}

客户端在发起连接建⽴时, Java代码通过创建Socket实例,调⽤底层的connect(…)⽅法,主动打开(Active Open)Socket连接。套接字监听⽅在收到请求之后,监听⽅和发起⽅(客户端)之间就会建⽴⼀条的连接通道,该通道由双⽅IP和双⽅端⼝所唯⼀确定。
⼀段简单的客户端连接主动打开(Active Open)的Java示例代码,具体如下:

public class SocketClient {
    public static void main(String[] args) {
        try {
            //和服务器创建连接
            Socket socket = new Socket("localhost", 8080);
            //写入给监听方的输入流
            OutputStream os = socket.getOutputStream();
            //读取监听方的输入流
            InputStream is = socket.getInputStream();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

TCP连接的建⽴时,双⽅需要经过三次握⼿,具体过程如下:
(1)第⼀次握⼿:Client进⼊SYN_SENT状态,发送⼀个SYN帧来主动打开传输通道,该帧的SYN标志位被设置为1,同时会带上Client分配好的SN序列号,该SN是根据时间产⽣的⼀个随机值,通常情况下每间隔4ms会加1。除此之外,SYN帧还会带⼀个MSS(最⼤报⽂段⻓度)可选项的值,表示客户端发送出去的最⼤数据块的⻓度。
(2)第⼆次握⼿:Server端在收到SYN帧之后,会进⼊SYN_RCVD状态,同时返回SYN+ACK帧给Client,主要⽬的在于通知Client,Server端已经收到SYN消息,现在需要进⾏确认。Server端发出的SYN+ACK帧的ACK标志位被设置为1,其确认序号AN(Acknowledgment Number)值被设置为Client的SN+1;SYN+ACK帧的SYN标志位被设置为1,SN值为Server端⽣成的SN序号;SYN+ACK帧的MSS(最⼤报⽂段⻓度)表示的是Server端的最⼤数据块⻓度。
(3)第三次握⼿:Client在收到Server的第⼆次握⼿SYN+ACK确认帧之后,⾸先将⾃⼰的状态会从SYN_SENT变成ESTABLISHED,表示⾃⼰⽅向的连接通道已经建⽴成功,Client可以发送数据给Server端了。然后,Client发ACK帧给Server端,该ACK帧的ACK标志位被设置为1,其确认序号AN(Acknowledgment Number)值被设置为Server端的SN序列号+1。还有⼀种情况,Client可能会将ACK帧和第⼀帧要发送的数据,合并到⼀起发送给Server端。
(4)Server端在收到Client的ACK帧之后,会从SYN_RCVD状态会进⼊ESTABLISHED状态,⾄此,Server⽅向的通道连接建⽴成功,Server可以发送数据给Client,TCP的全双⼯连接建⽴完成。
三次握⼿的交互过程,具体如下图所示:

Client和Server完成了三次握⼿后,双⽅就进⼊了数据传输的阶段。数据传输完成后, 连接将断开,连接断开的过程需要经历四次挥⼿。

5 TCP的四次挥手

业务数据通信完成之后,TCP连接开始断开(或者拆接)的过程,在这个过程中连接的每个端的都能独⽴地、主动的发起,断开的过程TCP协议使⽤了四路挥⼿操作。
四次挥⼿具体过程,具体如下:
(1)第⼀次挥⼿:主动断开⽅(可以是客户端,也可以是服务器端),向对⽅发送⼀个FIN结束请求报⽂,此报⽂的FIN位被设置为1,并且正确设置Sequence Number(序列号)和Acknowledgment Number(确认号)。发送完成后,主动断开⽅进⼊FIN_WAIT_1状态,这表示主动断开⽅没有业务数据要发送给对⽅,准备关闭SOCKET连接了。
(2)第⼆次挥⼿:正常情况下,在收到了主动断开⽅发送的FIN断开请求报⽂后,被动断开⽅会发送⼀个ACK响应报⽂,报⽂的Acknowledgment Number(确认号)值为断开请求报⽂的Sequence Number (序列号)加1,该ACK确认报⽂的含义是:“我同意你的连接断开请求”。之后,被动断开⽅就进⼊了CLOSE-WAIT(关闭等待)状态,TCP协议服务会通知⾼层的应⽤进程,对⽅向本地⽅向的连接已经关闭,对⽅已经没有数据要发送了,若本地还要发送数据给对⽅,对⽅依然会接受。被动断开⽅的CLOSE-WAIT(关闭等待)还要持续⼀段时间,也就是整个CLOSE-WAIT状态持续的时间。主动断开⽅在收到了ACK报⽂后,由FIN_WAIT_1转换成FIN_WAIT_2状态。
(3)第三次挥⼿:在发送完成ACK报⽂后,被动断开⽅还可以继续完成业务数据的发送,待剩余数据发送完成后,或者CLOSE-WAIT(关闭等待)截⽌后,被动断开⽅会向主动断开⽅发送⼀个FIN+ACK结束响应报⽂,表示被动断开⽅的数据都发送完了,然后,被动断开⽅进⼊LAST_ACK状态。
(4)第四次挥⼿:主动断开⽅收在到FIN+ACK断开响应报⽂后,还需要进⾏最后的确认,向被动断开⽅发送⼀个ACK确认报⽂,然后,⾃⼰就进⼊TIME_WAIT状态,等待超时后最终关闭连接。处于TIME_WAIT状态的主动断开⽅,在等待完成2MSL的时间后,如果期间没有收到其他报⽂,则证明对⽅已正常关闭,主动断开⽅的连接最终关闭。
被动断开⽅在收到主动断开⽅的最后的ACK报⽂以后,最终关闭了连接,⾃⼰啥也不管了。
四次挥⼿的全部交互过程,具体如下图所示:

6 常见面试题

1. 为什么关闭连接的需要四次挥⼿,⽽建⽴连接却只要三次握⼿呢?

关闭连接时,被动断开⽅在收到对⽅的FIN结束请求报⽂时,很可能业务数据没有发送完成,并不能⽴即关闭连接,被动⽅只能先回复⼀个ACK响应报⽂,告诉主动断开⽅: “你发的FIN报⽂我收到了,只有等到我所有的业务报⽂都发送完了,我才能真正的结束,在结束之前,我会发你FIN+ACK报⽂的,你先等着”。所以,被动断开⽅的确认报⽂,需要拆开成为两步,故总体就需
要四步挥⼿。
⽽在建⽴连接场景中,Server端的应答可以稍微简单⼀些。当Server端收到Client端的SYN连接请求报⽂后,其中ACK报⽂表示对请求报⽂的应答,SYN报⽂⽤来表示服务端的连接也已经同步开启了,⽽ACK报⽂和SYN报⽂之间,不会有其他报⽂需要发送,故⽽可以合⼆为⼀,可以直接发送⼀个SYN+ACK报⽂。所以,在建⽴连接时,只需要三次握⼿即可。

2. 为什么连接建⽴的时候是三次握⼿,可以改成两次握⼿吗?

三次握⼿完成两个重要的功能:⼀是双⽅都做好发送数据的准备⼯作,⽽且双⽅都知道对⽅已准备好;⼆是双⽅完成初始SN序列号的协商,双⽅的SN序列号在握⼿过程中被发送和确认。
如果把三次握⼿改成两次握⼿,可能发⽣死锁。两次握⼿的话,缺失了Client的⼆次确认ACK帧,假想的TCP建⽴的连接时⼆次挥⼿,可以如下图所示:

在假想的TCP建⽴的连接时⼆次握⼿过程中,Client发送Server发送⼀个SYN请求帧,Server收到后发送了确认应答SYN+ACK帧。按照两次握⼿的协定,Server认为连接已经成功地建⽴了,可以开始发送数据帧。这个过程中,如果确认应答SYN+ACK帧在传输中被丢失,Client没有收到,Client将不知道Server是否已准备好,也不知道Server的SN序列号,Client认为连接还未建⽴成功,将忽略Server发来的任何数据分组,会⼀直等待Server的SYN+ACK确认应答帧。⽽Server在发出的数据帧后,⼀直没有收到对应的ACK确认后就会产⽣超时,重复发送同样的数据帧。这样就形成了死锁。

3. 为什么主动断开⽅在TIME-WAIT状态必须等待2MSL的时间?

原因之⼀: 主动断开⽅等待2MSL的时间,是为了确保两端都能最终关闭。假设⽹络是不可靠的,被动断开⽅发送FIN+ACK报⽂后,其主动⽅的ACK响应报⽂有可能丢失,这时候的被动断开⽅处于LAST-ACK状态的,由于收不到ACK确认被动⽅⼀直不能正常的进⼊CLOSED状态。在这种场景下,被动断开⽅会超时重传FIN+ACK断开响应报⽂,如果主动断开⽅在2MSL时间内,收到这个重传的FIN+ACK报⽂,会重传⼀次ACK报⽂,后再⼀次重新启动2MSL计时等待,这样,就能确保被动断开⽅能收到ACK报⽂,从⽽能确保被动⽅顺利进⼊到CLOSED状态。只有这样,双⽅都能够确保关闭。反过来说,如果主动断开⽅在发送完ACK响应报⽂后,不是进⼊TIME_WAIT状态去等待2MSL时间,⽽是⽴即释放连接,则将⽆法收到被动⽅重传的FIN+ACK报⽂,所以不会再发送⼀次ACK确认报⽂,此时处于LAST-ACK状态的被动断开⽅,⽆法正常进⼊到CLOSED状态。
原因之⼆: 防⽌“旧连接的已失效的数据报⽂”出现在新连接中。主动断开⽅在发送完最后⼀个ACK报⽂后,再经过2MSL,才能最终关闭和释放端⼝,这就意味着,相同端⼝的新TCP新连接,需要在2MSL的时间之后,才能够正常的建⽴。2MSL这段时间内,旧连接所产⽣的所有数据报⽂,都已经从⽹络中消失了,从⽽,确保了下⼀个新的连接中不会出现这种旧连接请求报⽂。

4. 如果已经建⽴了连接,但是Client端突然出现故障了怎么办?

TCP还设有⼀个保活计时器,Client端如果出现故障,Server端不能⼀直等下去,这样会浪费系统资源。每收到⼀次Client客户端的数据帧后,Server端都的保活计时器会复位。计时器的超时时间通常是设置为2⼩时,若2⼩时还没有收到Client端的任何数据帧,Server端就会发送⼀个探测报⽂段,以后每隔75秒钟发送⼀次。若⼀连发送10个探测报⽂仍然没反应,Server端就认为Client端出了故障,接着就关闭连接。如果觉得保活计时器的两个多⼩时的间隔太⻓,可以⾃⾏调整TCP连接的保活参数。

7 netstat查看连接状态

Netstat是⼀款命令⾏⼯具,⽤于列出系统中所有的TCP/IP的连接情况,包括TCP、UDP以及UNIX套接字,⽽且,该⼯具也能列出处于监听状态的服务端监听套接字。
总体来说,Netstat是⼀个的⾮常有⽤的⼯具,可以⽤于查看路由表、实际的⽹络连接、甚⾄每⼀个⽹络接⼝设备的状态信息。举个例⼦,使⽤netstat -ant指令查看当前Linux系统中所有的TCP/IP⽹络的连接信息,⼤致的结果如下:

对以上netstat -ant命令的展示结果中的列,具体的介绍如下:
(1)Proto列表示套接字所使⽤的协议,⽐如tcp、udp、udpl、raw等。
(2)Recv-Q列、Send-Q列分别表示⽹络接收队列、发送队列中的字节数,其中的字⺟Q是Queue的缩写。具体来说,Recv-Q表示套接字连接的本地接收缓冲区中没有被应⽤进程取⾛的字
节数,其统计单位是字节,该值表示套接字总共还有多少字节的数据,没有从内核空间的套接字缓存区拷⻉到⽤户空间缓冲区。Send-Q表示套接字连接的发送队列中,对⽅没有收到的数据或者说没有被对⽅确认(Ack)的数据,其统计单位也是字节。
(3)Local Address、Foreign Address两列⽤于展示套接字连接的本地地址、对端地址,地址中包含了套接字的IP和端⼝号。如果netstat命令中使⽤了-n(–numeric)选项,地址和端⼝会以数字的形式展示,否则,地址将被解析为规范主机名(FQDN),并且⼀些默认的端⼝号将被解析为相应的协议名称,例如,地址127.0.0.1会被解析为localhost,端⼝80会被解析为http。
(4)State列⽤于展示套接字连接的状态,如果是TCP连接,此列将展示TCP连接的11种状态的其中的某种状态。
对上命令结果中各列的具体含义,还可以通过Linux命令man netstat 查看。netstat 命令的选项⽐较多,⼤致如下表所示:

选项说明
-a或–all显示所有Socket套接字连接,包括服务端监听套接字。
-c或–continuous持续列出Socket套接字连接的状态。
-e或–extend显示Socket套接字连接其他相关信息。
-g或–groups显示多重⼴播功能群组中的成员名单。
-h或–help显示该命令的在线帮助。
-i或–interfaces显示⽹络接⼝(⽹卡)信息。
-l或–listening显示监听中的服务端监听Socket套接字。
-n或–numeric直接以数字的形式展示IP地址和端⼝。如果不加该选项,地址将被解析为规范主机名(FQDN),⼀些默认的IP地址和端⼝号将被解析为相应的规范名称,例如127.0.0.1会被解析为localhost, 80端⼝会被解析为http。
-o或–timers显示计时器。
-p或–programs显示正在使⽤Socket套接字的PID()进程ID和进程名称。
-r或–route显示路由表。
-s或–statistice显示⽹络统计信息。
-t或–tcp显示TCP传输协议的连接信息。
-u或–udp显示UDP传输协议的连接信息。
-w或–raw显示RAW传输协议的连线信息。

⼀般情况,可以使⽤netstat -antp指令,去查看TCP的连接信息,包含其进程的PID和名称。在实际的连接状态查看的过程中,有⼀个持续查看的过程,会⽤到Shell脚本中的while循环。⼀段简单的通过while循环查看服务端特定监听端⼝(如18899)的所有TCP连接的Shell脚本,⼤致如下:

8 Wireshark抓包

wireshark是⾮常流⾏的⽹络封包分析软件,功能⼗分强⼤。可以截取各种⽹络封包,显示⽹络封包的详细信息。
wireshark是开源软件,可以放⼼使⽤。 可以运⾏在Windows和Mac OS上。
下载网址:
https://www.wireshark.org/download.html
使⽤wireshark的⼈必须了解⽹络协议,否则就看不懂wireshark了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值