写点什么

TCP 协议

用户头像
IT视界
关注
发布于: 2021 年 06 月 02 日

TCP(Transmission Control Protocol):

面向连接的,可靠的,基于字节流的传输层通信协议

特点:

  • 基于连接的:数据传输之前需要建立连接

  • 全双工的:可以双向传输

  • 字节流:不限制数据大小,打包成报文段,保证有序接收,重复报文自动丢弃

  • 流量缓冲:解决双方处理能力的不匹配

  • 可靠的传输服务:保证可达,丢包时通过重发机制实现可靠性

  • 拥塞控制:防止网络出现恶性拥塞

TCP 报文格式:

Options 可选参数上面的字段是必选的字段;

Source port 源端口发送端随机生成,Dest port 目的端口为服务器的端口;

Sequence number 序列号,Acknowledgment number 应答号码,这两个编号用来保证数据的可靠性传输;

Header length 头长度,Unused 保留字段,竖着的是报文类型标识,如 SYN 同步序列号说明是建立连接的报文,ACK 响应的报文可以是确认握手的报文已经到达或者传送数据的时候确认数据已经收到,FIN 关闭连接的报文;

Receive window 是当前服务器可以接收数据的窗口大小的一个值,会受到网络的影响,大小不是稳定的值;

Urgent data pointer 紧急数据指针,如果报文类型为 URG,说明这个数据需要应用程序优先处理,紧急数据指针指向一个内存空间,紧急处理一下;

Data 真实的业务数据,发送 HTTP 报文的时候,Data 里面放的就是 HTTP 协议的数据;

TCP 连接管理

  1. TCP 连接:四元组[源地址,源端口,目的地址,目的端口]

  2. 确立连接:TCP 三次握手


    a.同步通信双方初始序列号(ISN,initial sequence number)


    b.协商 TCP 通信参数(MSS,窗口信息,指定校验和算法)

如何进行握手?

客户端拿到 IP 地址后,开始三次握手:

刚开始服务器端首先会进入 Listen 的状态,比如 Nginx 监听某一个端口 80,客户端发送一个请求之前会先创建一个数据结构 create tcb(Transmission Control Block),存储发送的端口号以及其他信息,客户端向服务器端发送 SYN 同步序列号报文后,客户端进入 SYN-SENT 状态,当服务器端收到 SYN 报文的时候,进入 SYN-RECEIVED 状态,服务器端在本地创建数据结构 create tcb(Transmission Control Block),存储连接信息,回送一个 ACK 的确认报文,表示同步序列号的报文我收到了,并且同时会发送一个 SYN 的报文给客户端,客户端在收到服务器端回送的 ACK 确认报文后,就进入了 ESTABLISHED 状态,说明客户端连接已经建立了,客户端在收到服务器端发送的 SYN 报文后,会向服务器端发送一个 ACK 确认报文,通知服务器端,我已经收到 SYN 报文了,当服务器端收到 ACK 报文后,进入 ESTABLISHED 状态,建立连接。

客户端和服务器端发送 SYN 报文时会携带随机生成的 seq 序列号,发送 ACK 报文时,会携带对应的 SYN 报文携带的序列号加一的序列号,表示确认的是这个 SYN。

下面做一个实验说明一下三次握手:

在本地安装一个 Nginx 服务器,部署一个静态页面

访问静态页面,看一下客户端与服务端交互之前的三次握手,抓一下和 Nginx 服务器通信之前的报文,怎么进行抓包呢?我们可以使用 tcpdump -help 来查看一下

使用 tcpdump 可以指定对应的网卡,因为是要从本地发送请求到服务器上面,所以它们要在同一个网段,选择与本机同一网段的网卡

ipconfig 查看本机的网段

ifconfig 查看与本机同一网段的网卡

最后执行 tcpdump -i 网卡 -S(相对序列号转化为绝对序列号)-c 3 port 80 (指定监听某个端口)

看三次握手的话 加一个 -c 3 代表抓到三个报文后就停止,回车进入监听状态

本机装了一个 nc 的网络工具 发送 tcp 三次握手的请求

输入服务器的地址和端口,回车,这个时候就建立了三次握手

监听那边抓到三次报文后立刻结束

第一条报文可以看到是由我们主机向服务器发送的报文,Flags 代表报文的类型 S 就是 SYN .就是 ACK seq 随机序列号 win 主机窗口大小 options tcp 用的参数

第二条报文可以看到是服务器向主机发送的报文 可提的就是 ack 后面的序列号就是 第一条报文序列号加一

第三条是主机向服务器发送的确认报文

三次握手时内核的动作:

当 SYN 报文到达后,会进入内核的 SYN 队列,进入 SYN_RECEIVED 状态,并且立刻发送 SYN/ACK 报文到客户端,接下来服务端等待客户端回传一个确认报文,当收到确认报文,内核会把 SYN 队列中的报文出队,入队到 ACCEPT 队列中,应用程序监听到 ACCEPT 内有可用连接,就会到队列中去,建立连接,服务端状态变为 ESTABLISHED

如何查看 tcp 连接:

netstat -tpn -c 1

-t 查看当前 tcp 连接的状态

-p 显示对应的进程

-n 数字形式查看 IP 和 port

-c 任务执行时间间隔 1 代表每秒一次

现在 State 是 ESTABLISHED,我们在创建一个 TCP 连接,看一下状态

telnet 192.168.109.200 80

执行命令如果出现 telnet 不是内部或外部命令时,代表着计算机没有开启 telnet 服务,打开控制面板,找到启用或关闭 Windows 功能,勾选 Telnet Client,点击确定进行安装

连接成功

如果把 80 端口的连接关闭会发生什么呢?

State 变为 TIME_WAIT,刚才说握手的时候是没有 TIME_WAIT 状态的,那这个状态是怎么出来的呢,这里就要看一下断开连接的时候的四次挥手了

大家都知道,建立连接的时候会消耗很多系统资源,所以连接不用的话,要去把它关掉,避免浪费系统资源,关闭连接是双向的全双工的一个协议,客户端可以直接关,服务端也可以直接关

现在我们把主动关闭的叫客户端,被动关闭的叫服务端,带大家来看一下四次挥手的过程

四次挥手:

客户端执行 CLOSE 命令,发送一个 FIN 报文到服务器端,客户端变为 FIN_WAIT_1 状态,服务器端收到报文之后,立刻回传一个响应报文,服务器端进入 CLOSE_WAIT 状态,这时服务器端开始收尾工作,把还需要传给客户端的数据传过去,当客户端收到服务器端发送的 ACK 报文,客户端状态变为 FIN_WAIT_2,接收服务器端传过来的数据,当服务器端收尾完成,执行 CLOSE 命令,向客户端发送一个 FIN 报文,状态变为 LAST_ACK,当客户端收到 FIN 报文后立刻回传 ACK 报文,当服务器端收到 ACK 报文,则释放连接,客户端在发送完 ACK 报文后,变为 TIME_WAIT(2MSL),客户端等待两个 MSL 的时间,即一个报文来回的时间,然后再释放资源

为什么客户端要等待两个 MSL 的时间呢?

假设一下,当客户端在发出 ACK 之后就释放了资源,但是 ACK 报文丢失了,服务器端就不会关闭,而是持续重复发送 FIN 报文,客户端也没法响应

第二种情况,客户端释放了资源,但是还有报文在发送过程中,由于释放了资源,端口号就会被其他进程使用,当报文到达,就会对新建立的连接造成混乱

字节流的协议

TCP 把应用交付的数据仅仅看成是一连串的无结构的字节流,TCP 并不知道字节流的含义,TCP 并不关心应用程序一次将多大的报文发送到 TCP 的缓存中,而是根据对方给出的窗口值和当前网络拥堵的程度来决定一个报文段应该包含多少个字节。

MSS:Max Segment Size,默认 536byte 实际数据

假设传输 20 个字节,实际传输的数据要比 20 个字节大的多,这里为了方便理解,就使用 20 个字节来讲解,这 20 个字节有可能会发生这种情况,网络速度比较快的情况下,有一部分数据已经到了应用程序,有一部分还在传输的路上,看到这里就知道,数据并不是一次发过去的,而是把数据分割为很多 TCP 的报文段,报文段的大小就是 MSS,MSS 是会根据窗口大小与网络状态去动态的调整的,TCP 协议不会在意发多大的数据,无论多大它都会切割成小的报文传输,报文传输过程中会经过非常多的路由器,并且顺序都是不固定的,最终到达服务器,所以报文的顺序是乱的,因此 TCP 还会进行排序,保证顺序和传进来时是一样的,怎么排序呢?其实就是根据报文的 Sequence number 序列号来实现的,并且在网络的传输过程中还会发生丢包,如路由器的缓存区满了,还在向路由器加数据,就会丢失,客户端一定时间内还没有接收到 TCP 协议发送的 ACK 消息的话,就会重传,有的时候数据没有丢失,而是在路径中没有到达,这时重传就会发生数据重复,因此 TCP 协议还实现了去重工作,收到了和之前一样的数据会进行丢弃。

数据可靠性传输

停止等待协议:

每发送一个报文都需要回一个应答,接收到才能发送下一条报文,这种效率很低

重传机制

1.ACK 报文丢失

ACK 报文丢失,客户端一段时间没有收到 ACK 报文,客户端会认为 ACK 报文丢失了,会进行重传 M1 报文

2.请求报文丢失

请求报文丢失,一样没有收到 ACK 报文,进行重传

滑动窗口协议与累计确认(延时 ACK)

滑动窗口大小同通过 TCP 三次握手和对端协商,且受网络状况影响

取代了一条一条报文去发送,而是发送窗口大小的报文,只要客户端收到末尾报文的 ACK 就能确定之前的都到了,但在数据传输过程中会出现丢包的情况,如果 3 和 5 丢失了,那么接收 4 的 ACK 肯定是不正确的,所以 TCP 会回传连续的最后一个报文的 ACK,服务器端会丢弃 3、4 和 5 报文,客户端就会知道 1 和 2 已经发送到服务器端,在缓存中删除 1 和 2,从 3 向后取窗口大小的报文发送到服务器端。

发布于: 2021 年 06 月 02 日阅读数: 12
用户头像

IT视界

关注

还未添加个人签名 2021.04.02 加入

致力于帮助所有选择IT行业的人们,希望能通过我的文章使大家少走弯路,踏上坦途

评论

发布
暂无评论
TCP协议