TCP协议

1. TCP/IP协议的分层模型

**七层OSI模型:**应用层、表示层、会话层、传输层、网络层、数据链路层和物理层
**TCP/IP协议:**应用层(HTTP、FTP、SMIP、Telnet)、传输层(TCP、UDP)、网络层(IP、ICMP、IGMP)、链路层(ARP、RAPP)
在这里插入图片描述

2. HTTP报⽂传输原理

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

2.2 封装和分用

1、数据封装和分⽤的过程⼤致为:发送端每通过⼀层会增加该层的⾸部,接收端每通过⼀层则删除该层的⾸部。
总体来说, TCP/IP分层管理、数据封装和分⽤的好处:分层之后若需改变相关设计,只需替换变动的层。各层之间的接⼝部分规划好之后,每个层次内部的设计就可以⾃由改动。层次化之后,设计也变得相对简单:各个层只需考虑分派给⾃⼰的传输任务。
2、TCP/IP与OSI的区别主要有哪些呢?
OSI参考模型注重“通信协议必要的功能是什么”,
TCP/IP则更强调“在计算机上实现协议应该开发哪种程序

2.3 数据传输过程

实际上,在传输过程中,数据报⽂会在不同的物理⽹络之间传递,还是以⼀个HTTP请求的传输为例,请求在不同物理⽹络之间的传输过程
在这里插入图片描述

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)表达不同的含义:
    (1)当SYN = 1时,当前为连接建⽴阶段,此时的序号为初始序号ISN((Initial Sequence Number),通过算法来随机⽣成序号;
    (2)当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个标志位的说明,如下表所示
    在这里插入图片描述
    在连接建⽴的三次握⼿过程中,若只是单个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值还是越⼤越好,这样可以提⾼⽹络的利⽤率。
  2. 重传机制。设置定时器,等待确认包,如果定时器超时还没有收到确认包,则报⽂重传。
  3. 对⾸部和数据进⾏校验。
  4. 接收端对收到的数据进⾏排序,然后交给应⽤层,接收端丢弃重复的数据
  5. TCP还提供流量控制,主要是通过滑动窗⼝来实现流量控制。

4. TCP的三次握⼿

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) throws InterruptedException {
 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端的保活计时器会复位。计时器的超时时间通常是设置为两小时,若两小时还没有收到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 命令的选项⽐较多,⼤致如下表所示:
在这里插入图片描述
在这里插入图片描述
⼀般情况,可以使⽤netstat -antp指令,去查看TCP的连接信息,包含其进程的PID和名称。在实际的连接状态查看的过程中,有⼀个持续查看的过程,会⽤到Shell脚本中的while循环。⼀段简单的通过while循环查看服务端特定监听端⼝(如18899)的所有TCP连接的Shell脚本,⼤致如下:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值