欢迎来到Cefler的博客😁
🕌博客主页:折纸花满衣
🏠个人专栏:Linux
目录
- 👉🏻再谈端口号
- pidof命令
- 👉🏻UDP协议
- 报文的管理
- 基于UDP协议的应用层协议
- 👉🏻TCP协议-上
- 怎么理解TCP的面向字节流
- TCP的发送和接收缓冲区
- 确认应答(ACK)机制
- 序列号和确认号在发送方和接收方的联系
- 超时重传机制
- 快重传vs超时重传
- TCP的连接管理机制(三次握手和四次挥手)
- 为什么要进行三次握手和四次挥手?
- 四次挥手可以三次挥手吗?
- 三次握手和accept
- 👉🏻TCP协议-下
- 理解TIME_WAIT状态
- 理解 CLOSE_WAIT 状态
- TCP的网络流量控制
- 滑动窗口(慢启动和拥塞控制)
- 拥塞控制
- 延迟应答和捎带应答
- 粘包问题
- TCP异常情况
- 👉🏻telnet命令
👉🏻再谈端口号
我们之前学过端口号,知道端口号是一个逻辑地址,在网络中用于标识一个特定的进程或服务。
在socket编程中,我们进行绑定端口号时,会发现一些端口号所要绑定的权限是有限的,这是因为端口号有分类:
0 - 1023
: 知名端口号, HTTP, FTP, SSH等这些广为使用的应用层协议, 他们的端口号都是固定的.1024 - 65535
: 操作系统动态分配的端口号. 客户端程序的端口号, 就是由操作系统从这个范围分配的
有些服务器是非常常用的, 为了使用方便, 人们约定一些常用的服务器, 都是用以下这些固定的端口号:
- ssh服务器, 使用22端口
- ftp服务器, 使用21端口
- telnet服务器, 使用23端口
- http服务器, 使用80端口
- https服务器, 使用443
在linux环境下,使用cat /etc/services
命令可以查看知名端口号,我们自己写一个程序使用端口号时, 要避开这些知名端口号
pidof命令
pidof
命令用于查找正在运行的程序的进程 ID(PID)。它的常见用法是通过指定程序的名称来查找相应进程的 PID。
通常,pidof
命令的语法是:
pidof [OPTIONS] [PROGRAM]
其中:
OPTIONS
是一些可选参数,用于指定命令的行为。PROGRAM
是要查找的程序的名称。
pidof
命令会返回指定程序的 PID,如果有多个同名的进程正在运行,它会返回这些进程的 PID,每个 PID 之间用空格分隔。
以下是 pidof
命令的一些常用选项:
-s
:只返回一个 PID,如果有多个进程匹配,只返回第一个 PID。-c
:同时显示进程的命令名称。-o
:设置输出格式。-x
:只匹配程序的完整路径名称。
举个例子,如果要查找名为 apache2
的进程的 PID,可以使用以下命令:
pidof apache2
该命令会返回正在运行的 apache2
进程的 PID。
需要注意的是,pidof
命令通常用于在命令行环境下查找进程,对于一些 GUI 程序,可能需要结合其他命令或工具来查找进程的 PID。
👉🏻UDP协议
🥕UDP协议端格式
16位UDP长度表示整个报文能传输的数据最大长度是64KB(包含UDP首部)
前8字节是header
报头部分,当应用层将数据下传到传输层(也就是udp协议这一层)时,会在数据的前8字节封装添加上这8字节的header部分,此时报文的组成是:报文=header(8字节)+明文数据
那么在传输层中,有那么多的报文,操作系统是如何将它们进行管理起来呢?
报文的管理
操作系统对报文进行管理,肯定也要进行:先描述,再组织
所以,自然也需要有一个描述结构体来描述报文的信息。
🥨header的描述结构体
struct udphdr
{
uint32_t src_port;
uint32_t dst_port;
uint32_t len;
uint32_t checksum;
};
🥨header+数据的缓冲区描述结构体
struct sk_buff
{
char* data;
char* tail;
struct sk_buff* next;
};
- data:是一个指向header结构体的起始位置的指针
- tail:是一个指向header结构体的末尾位置的指针
- next:指向下一个header+数据的缓冲区描述结构体的指针
所以操作系统对报文的管理,就是变成了对链表的增删查改。
对这个管理有了理解之后,我们就知道操作系统是怎么将报文的数据收全了,
也能明白为什么UDP协议是面向数据报了,看了下面操作系统读取报文中的数据步骤就明白了:
1.通过data指针可以获得header结构体信息
2.读取header中的len值,就知道整个报文的数据长度是多长
3.因为header的长度是8字节,所以len-8就是数据的长度,所以只要从tail指针后开始读len-8长度,就可以读到完整数据。
所以看下来,为什么说UDP传输数据和接收数据都很死板呢?因为header中的len都已经将整个报文长度都规定死了,所以就是你一次性只能根据这个值进行读写。
UDP具有接收缓冲区. 但是这个接收缓冲区不能保证收到的UDP报的顺序和发送UDP报的顺序一致; 如果缓冲区满了, 再到达的UDP数据就会被丢弃;
基于UDP协议的应用层协议
- NFS: 网络文件系统
- TFTP: 简单文件传输协议
- DHCP: 动态主机配置协议
- BOOTP: 启动协议(用于无盘设备启动)
- DNS: 域名解析协议
当然, 也包括你自己写UDP程序时自定义的应用层协议
👉🏻TCP协议-上
🥕TCP协议端格式
TCP(Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层协议,用于在网络上可靠地传输数据。TCP 协议的数据传输是通过 TCP 段(Segment)来实现的,每个 TCP 段包含了一定的控制信息和数据,以下是 TCP 协议段的格式:
- 源端口号(Source Port):16 位,标识发送方的端口号。
- 目标端口号(Destination Port):16 位,标识接收方的端口号。
- 序列号(Sequence Number):32 位,用于对数据进行排序和重组。它表示该段数据的第一个字节在数据流中的位置。
- 确认序号(Acknowledgment Number):32 位,确认收到的数据的序列号(1000+1=1001),用于实现可靠数据传输,确认序号意味着该序号之前所有序号的报文都已经收到了。
- 数据偏移(Data Offset):4 位,表示 TCP 段头部的长度,以
4 字节为单位
。由于 TCP 头部长度可变,该字段用于指示头部的长度,以便识别数据的开始位置。
当头长度表示为1111,即15,此时TCP 段头部的最大长度就是15x4=60。
- 保留位(Reserved):6 位,保留字段,未使用,置为 0。
- 标志位(Flags):6 位,包括如下标志:
- URG:表示紧急指针(Urgent Pointer)字段是否有效。
- ACK:表示确认号字段是否有效。
- PSH:表示接收端应该尽快将数据交给应用程序处理。
- RST:表示连接复位。
- SYN:表示建立连接。
- FIN:表示释放连接。
- 窗口大小(Window Size):16 位,用于
流量控制
,表示发送端当前可接收的数据量。 - 校验和(Checksum):16 位,用于检验 TCP 段的完整性。
- 紧急指针(Urgent Pointer):16 位,只有当 URG 标志被置为 1 时才有效,用于指示紧急数据的位置。
- 选项(Options):可选字段,长度不定,用于扩展 TCP 头部,例如实现选择性确认、最大报文段长度等。
- 数据(Data):可变长度,携带 TCP 协议传输的应用层数据。
以上是 TCP 协议段的基本格式,它们组成了 TCP 头部,用于在 TCP 连接中进行数据的可靠传输和控制。
需要注意的是,过大的缓冲区可能会导致内存浪费和网络拥塞,而过小的缓冲区则可能导致数据传输速度变慢。因此,在设置缓冲区大小时需要综合考虑多个因素,包括网络带宽、延迟、应用需求等。
怎么理解TCP的面向字节流
TCP(传输控制协议)的“面向字节流”特性可以从以下几个方面来理解:
- 字节流的概念:在TCP中,“流”(stream)指的是流入或流出进程的字节序列。TCP将数据视为一个
连续的
、无结构
的字节流,而不是一系列独立的数据包。这意味着TCP不关心应用程序发送或接收的数据块的大小和边界,只关注字节的连续性和顺序,在TCP看来,数据流中的每一个字节都是独立的,并按照发送的顺序进行传输。 - 数据的分割与重组:在TCP中,发送方会将数据分割成适当大小的数据段(segment),每个数据段都有一个唯一的序列号。接收方在收到这些数据段后,会根据序列号将它们重新组合成原始的字节流。这种机制确保了数据的完整性和顺序性。
- 可靠传输:TCP通过序列号、确认和重传机制来确保数据的可靠传输。接收方在收到数据段后会向发送方发送确认消息(ACK),以确认已经接收到的数据段。如果发送方在一定时间内没有收到确认消息,它会重新发送该数据段,直到接收方确认为止。这种机制确保了数据的可靠传输,即使在网络出现丢包或乱序的情况下也能保持数据的完整性。
- 无边界性:由于TCP是面向字节流的,因此它并不保证接收方应用程序所收到的数据块和发送方应用程序所发出的数据块具有对应大小的关系。也就是说,TCP不关心应用程序发送或接收的数据块的大小和边界,只关注字节的连续性和顺序。这种特性使得TCP在处理大文件或流媒体等需要连续传输大量数据的应用场景时更加高效。
- 流量控制:TCP还通过滑动窗口等机制来实现流量控制,确保发送方不会发送过多的数据导致接收方无法处理。这种机制也依赖于面向字节流的特性,因为滑动窗口是以字节为单位来定义的。
🍅socket通信,read和write来理解面向字节流
创建一个TCP的socket, 同时在内核中创建一个 发送缓冲区
和一个 接收缓冲区
:
- 调用write时, 数据会先写入发送缓冲区中;
- 如果发送的字节数太长, 会被拆分成多个TCP的数据包发出;
- 如果发送的字节数太短, 就会先在缓冲区里等待, 等到缓冲区长度差不多了, 或者其他合适的时机发送出去;
- 接收数据的时候, 数据也是从网卡驱动程序到达内核的接收缓冲区;然后应用程序可以调用read从接收缓冲区拿数据;
- 另一方面, TCP的一个连接, 既有发送缓冲区, 也有接收缓冲区, 那么对于这一个连接, 既可以读数据, 也可以写数据. 这个概念叫做 全双工
由于缓冲区的存在, TCP程序的读和写不需要一 一匹配, 例如:
- 写100个字节数据时, 可以调用一次write写100个字节, 也可以调用100次write, 每次写一个字节;
- 读100个字节数据时, 也完全不需要考虑写的时候是怎么写的, 既可以一次read 100个字节, 也可以一次read一个字节, 重复100次;
TCP的发送和接收缓冲区
TCP的发送缓冲区和接收缓冲区在TCP协议中起着至关重要的作用。它们的主要功能是在网络传输过程中缓存数据,以平衡发送方和接收方之间的数据传输速度差异。
- 发送缓冲区:每个TCP socket连接在内核中都有一个发送缓冲区。当应用进程需要发送数据时,它会将数据写入这个发送缓冲区。然后,TCP协议栈会负责将这些数据从发送缓冲区中取出,并打包成TCP数据包进行发送。发送缓冲区的大小决定了发送方能够连续发送数据的数量,从而影响了数据的传输效率。
- 接收缓冲区:同样地,每个TCP socket连接在内核中也有一个接收缓冲区。当TCP数据包到达接收端时,它们会被暂时存放在这个接收缓冲区中。然后,接收端的应用程序可以从接收缓冲区中读取数据。接收缓冲区的大小决定了接收方能够缓存的数据量,从而影响了数据的接收效率。
TCP的全双工工作模式
以及TCP的流量(拥塞)控制便是依赖于这两个独立的缓冲区以及缓冲区的填充状态。在数据传输过程中,发送方和接收方会根据各自缓冲区的填充情况来调整自己的发送和接收速度,以达到最佳的传输效果。
确认应答(ACK)机制
确认应答(ACK)机制是 TCP 协议中的一个重要特性,用于实现可靠的数据传输。在 TCP 连接中,接收方收到数据后需要向发送方发送确认,以告知发送方已成功接收到数据。确认应答机制主要包括以下几个方面:
-
确认号(Acknowledgment Number):在 TCP 报文头部中,接收方发送的确认报文中会包含一个
确认号
,表示已经成功接收到的数据的序列号(确认序号意味着该序号之前所有序号的报文都已经收到了。)。发送方根据接收到的确认号,可以知道哪些数据已经被成功接收,从而进行相应的重传或发送下一段数据。 -
确认应答的触发条件:接收方收到数据后,会根据 TCP 协议的要求判断何时发送确认。主要情况包括:
- 当接收方收到的数据的序列号连续到达,并且处于接收窗口内时,会立即发送确认。
- 当接收方收到的数据无法按序到达,但是已经缓存的数据可以填补序列号的空缺时,也会发送确认。
- 当接收方收到的数据发生错误,无法被正确处理时,通常会丢弃该数据并不发送确认。
-
累积确认:TCP 使用累积确认的方式来告知发送方收到了哪些数据。即接收方发送的确认号表示已成功接收的数据中,连续序列号的最大值,而不是单独确认每个数据段的接收情况。
-
确认延迟和优化:为了减少网络负载和提高效率,TCP 实现中可能会对确认进行延迟和优化。例如,接收方可能会等待一定时间或一定数量的数据段后才发送确认,以合并多个确认为一个确认,从而减少确认报文的数量。
-
确认超时和重传:如果发送方发送的数据段超过一定时间没有收到确认,就会认为该数据丢失或损坏,会触发超时重传机制,重新发送未确认的数据段。
通过确认应答机制,TCP 可以实现可靠的数据传输,保证数据的完整性
和可靠性
。接收方的确认告知发送方哪些数据已经被成功接收,发送方可以据此进行数据重传或发送下一段数据,从而实现了端到端的可靠传输。
序列号和确认号在发送方和接收方的联系
ACK(Acknowledgment)应答机制在TCP(传输控制协议)中起着至关重要的作用,它确保了数据的可靠传输。在这个机制中,序列号和确认号在发送方和接收方之间建立了紧密的联系。以下是关于这两个元素在ACK应答机制中如何联系和工作的详细说明:
一、序列号
- 发送方:当发送方需要发送数据时,它会将数据分成多个合适大小的数据段(或称为报文段)。每个数据段都会被赋予一个唯一的序列号,这个序列号表示该数据段在整个数据流中的起始位置。通过序列号,接收方可以准确地识别每个数据段,并按顺序重新组装它们。
- 接收方:接收方在收到数据段后,会检查其序列号以确认其位置。如果数据段没有丢失或乱序,接收方将能够按照序列号的顺序重新组装数据。
二、确认号
- 接收方:当接收方成功接收并处理一个数据段后,它会生成一个确认号(Acknowledgment Number)。这个确认号实际上是接收方期望从发送方接收到的下一个数据段的序列号。换句话说,它告诉发送方:“我已经成功接收到了哪个序号之前的所有数据,请从这个序号开始发送后续的数据。”
- 发送方:发送方在收到接收方的确认号后,会检查其值以确定哪些数据段已经被成功接收并确认(根据ACK标志位确认)。如果确认号指示的数据段已经发送但尚未收到确认,发送方可能会重传这些数据段以确保其可靠性。
在ACK应答机制中,序列号和确认号共同工作以确保数据的可靠传输。发送方使用序列号来标识和跟踪其发送的数据段,而接收方则使用确认号来确认其已成功接收的数据段并请求后续数据。这种机制允许发送方和接收方在不可靠的网络环境中进行通信,并确保数据的完整性和顺序性。
此外,为了避免数据包的重复传输和丢失,TCP还使用了超时重传和流量控制等机制。这些机制与ACK应答机制相结合,共同实现了TCP的可靠传输特性。
超时重传机制
TCP(传输控制协议)是一种面向连接的、可靠的传输协议,它通过一系列的机制确保数据的可靠传输。其中,超时重传机制是 TCP 中的重要组成部分之一,用于处理数据包丢失或延迟的情况。
超时重传机制的基本原理如下:
-
发送数据包: 发送方在发送数据包后会启动一个计时器,计时器的时间设置为一个估算的往返时间(RTT)和一个安全边界。
-
等待 ACK: 发送方等待接收到对应数据包的确认 ACK(确认应答),如果在计时器时间内收到了 ACK,则说明数据包已经成功送达,计时器停止并重置。
-
超时未收到 ACK: 如果在计时器时间内未收到 ACK,发送方会认为数据包丢失了,触发超时事件。
-
超时重传: 发送方会重新发送未确认的数据包,并重启计时器。
-
指数退避: 如果连续发生超时重传,则可能表示网络出现了更严重的问题,发送方会执行指数退避算法,即每次超时重传后等待时间会成倍增加,以避免网络拥塞。
这样,通过超时重传机制,TCP 可以在网络发生丢包或延迟的情况下保证数据的可靠传输。这个机制的有效性取决于对 RTT 的准确估计以及合理设置超时时间的能力。TCP 协议中还包含其他一些机制,如快速重传和选择性重传,可以进一步提高数据传输的效率和可靠性。
快重传vs超时重传
快重传(Fast Retransmit)和超时重传(Timeout Retransmit)是TCP协议中用于处理数据包丢失的两种主要机制,它们各自具有不同的特点和适用场景。
快重传:
快重传机制依赖于接收方发送的重复确认(duplicated ACKs)来检测数据包的丢失。当接收方收到一个乱序的数据包(即不是按序到达的)时,它会发送一个针对最后一个按序到达的数据包的ACK。如果发送方连续收到三个或更多个这样的重复ACK,它会认为中间的某个数据包已经丢失,并立即重传该数据包,而不是等待超时。
快重传的优点是它可以更快地恢复丢失的数据包,减少了不必要的等待时间,从而提高了网络性能。此外,由于快重传是基于接收方的反馈进行的,因此它可以更准确地定位丢失的数据包,避免了不必要的重传。
超时重传:
超时重传是TCP协议中最基本的处理数据包丢失的机制。当发送方发送一个数据包后,它会启动一个定时器(通常称为重传定时器)。如果在这个定时器超时之前没有收到接收方的ACK,发送方就会认为数据包已经丢失,并重传该数据包。
超时重传的优点是它不需要接收方的任何反馈就可以进行重传,因此在某些情况下可能更为可靠。然而,它的缺点是可能会导致不必要的延迟,因为发送方可能需要等待较长的时间才能确定数据包是否丢失。此外,如果网络中存在大量的数据包丢失,那么超时重传可能会导致网络拥塞的进一步恶化。
总的来说,快重传和超时重传是TCP协议中用于处理数据包丢失的两种重要机制。它们各自具有不同的优点和缺点,适用于不同的网络环境和场景。在实际应用中,TCP协议会根据网络状况和数据传输的特点来动态地选择使用哪种机制。
TCP的连接管理机制(三次握手和四次挥手)
TCP连接管理机制包括三个阶段:建立连接、数据传输和释放连接。
-
建立连接(Three-way Handshake):
- 客户端向服务器发送一个带有SYN(同步)标志的数据包,表示想要建立连接,并选择一个初始序列号。
- 服务器接收到 SYN 数据包后,回复一个带有 SYN 和 ACK(确认)标志的数据包,表示接受连接请求,并确认客户端的序列号,同时选择自己的初始序列号。
- 客户端接收到服务器的确认后,再发送一个带有 ACK 标志的数据包,表示连接建立完成。至此,TCP连接已经建立。
-
数据传输:
- 连接建立后,双方可以开始传输数据。数据以数据段的形式在网络上传输,每个数据段包含序列号,用于确保数据的有序传输和重组。同时,TCP 使用滑动窗口机制来控制发送方和接收方之间的数据流量,以提高传输效率。
- 在数据传输过程中,如果接收方收到了不按序到达的数据段,它会发送一个带有重复 ACK 标志的数据包,通知发送方重新发送丢失的数据段。
-
释放连接(Four-way Handshake):
- 当数据传输完成或需要关闭连接时,任何一方都可以发起连接释放过程。
- 发起释放连接的一方向对方发送一个带有 FIN(结束)标志的数据包,表示不再有数据要发送了,但仍接收数据。
- 接收到 FIN 数据包的一方发送一个带有 ACK 标志的数据包作为确认。
- 当另一方准备好关闭连接时,它也向对方发送一个带有 FIN 标志的数据包。
- 最后,双方都会发送一个带有 ACK 标志的数据包,确认对方的 FIN 数据包。至此,连接关闭完成。
通过这样的连接管理机制,TCP可以实现可靠的数据传输,并在数据传输完成后安全地释放连接,确保资源的有效利用。
为什么要进行三次握手和四次挥手?
三次握手和四次挥手的设计是为了确保通信的可靠性和安全性:
-
三次握手(建立连接):
- 第一次握手:客户端向服务器发送SYN报文,请求建立连接。如果这个请求丢失了,客户端会再次发送SYN报文,以确保服务器收到连接请求。
- 第二次握手:服务器接收到SYN报文后,会回复一个带有SYN和ACK标志的数据包,表示接受连接请求,并发送自己的SYN。这个响应同时表明服务器接受了客户端的连接请求,并告诉客户端,服务器也想要建立连接。
- 第三次握手:客户端收到服务器的响应后,发送一个ACK标志的数据包给服务器,表示连接建立完成。这样,双方都确认了彼此的接受连接请求,并建立了可靠的通信路径。
为什么要进行三次握手?
- 第一次握手是为了客户端告诉服务器自己的发送数据包能力和初始化序列号。
- 第二次握手是为了服务器告诉客户端自己也接受连接,并发送自己的序列号。
- 第三次握手是为了客户端确认服务器接受连接,并发送自己的序列号。
这样的设计可以确保双方都能确认彼此的发送和接收能力(以最小成本验证全双工),避免了虚假连接或未建立完整连接的情况。
-
四次挥手(释放连接):
- 第一次挥手:一方(例如客户端)发送一个FIN报文给另一方,表示自己不再发送数据,但仍然可以接收数据。
- 第二次挥手:另一方(例如服务器)收到FIN后,回复一个ACK报文,表示确认收到了关闭请求。
- 第三次挥手:另一方发送一个FIN报文给第一方,表示自己也准备好关闭连接。
- 第四次挥手:第一方收到FIN后,发送一个ACK报文给另一方,确认收到关闭请求。
为什么要进行四次挥手?
- 第一次挥手是为了通知对方自己不再发送数据,但仍可以接收数据。
- 第二次挥手是为了确认收到关闭请求,并通知对方自己也准备好关闭连接。
- 第三次挥手是为了通知对方自己也准备好关闭连接。
- 第四次挥手是为了确认收到关闭请求,完成连接的关闭过程。
这样的设计可以确保双方都能正确地关闭连接,并在关闭后不再发送数据,避免了数据丢失或不完整的情况。
四次挥手可以三次挥手吗?
之所以是四次挥手不是三次挥手,是因为客户端想要关闭连接:
1.client:我要关闭;
2.server:我知道了
3.client:我已经关了。
如果是这样的三次挥手,虽然也行但不普适于大部分情况,因为服务器也有要关闭的时候,所以也要考虑告知客户端,服务端要关闭连接的信息,所以有四次挥手:
1.client:我要关闭;
2.server:我知道了
3.server:我也要开始关了,待会不要发消息过来了。
4.client:好的收到,我也已经关了。
当然,如果当客户端要关闭时,服务器也一起关闭时,三次挥手也可以:
1.client:我要关闭;
2.server:我知道了,我也和你一起关闭。
3.client:好的了解,我已经关了。
三次握手和accept
在TCP协议中,三次握手(SYN, SYN-ACK, ACK)过程用于在客户端和服务器之间建立一个可靠的连接。这个过程完成后,连接在TCP层被认为是“已建立”的,但在应用程序层面,连接只有在服务器端调用accept
函数后才被认为是“完全连接”或“全连接”的。
具体来说:
-
三次握手:这是TCP协议用于建立连接的机制。通过交换三个数据包(SYN, SYN-ACK, ACK),客户端和服务器能够协商连接参数,如序列号、确认号、窗口大小等,并确认双方都能够正常通信。在三次握手完成后,TCP连接在传输层被认为是
已建立
的。 -
accept调用:在服务器端,当一个新的连接到达时(即三次握手完成后),服务器会接收到一个连接请求的通知。然而,这个连接在应用程序层面并不是立即可用的,因为服务器可能正在处理其他连接。
accept
函数用于从已完成连接队列
中取出第一个连接,并为这个连接创建一个新的套接字描述符。这个新的套接字描述符代表了与客户端之间的已建立的连接,并且可以用于后续的读写操作。因此,可以说在accept
函数返回后,连接在应用程序层面被认为是“全连接”的。
总结来说,三次握手在TCP层建立了连接,而accept
函数在应用程序层面完成了连接的建立,使得服务器可以开始与客户端进行数据传输。
👉🏻TCP协议-下
理解TIME_WAIT状态
TCP(传输控制协议)中的 TIME_WAIT
状态是一个重要的连接终止状态。当一个TCP连接被关闭时,其中一个端点(通常是主动关闭连接的那一方)会进入 TIME_WAIT
状态。以下是关于 TIME_WAIT
状态的一些关键点:
-
目的:
- 确保旧的报文段都消失:当TCP连接关闭时,发送方发送的最后一个ACK(确认)可能会丢失。如果接收方(被动关闭方)没有收到这个ACK,它可能会重新发送之前的FIN(结束)报文段。
TIME_WAIT
状态确保发送方有足够的时间来重发这个ACK,如果需要的话(否则丢弃)。 - 防止新的连接与旧的连接数据混淆:在TCP中,连接是由源IP地址、源端口、目的IP地址和目的端口这四个值唯一标识的。如果立即重用这些值来建立新的连接,而旧的连接上的数据包仍然在网络中,则可能会导致数据包与新连接混淆。
TIME_WAIT
状态确保旧连接上的数据包有足够的时间消失,从而避免这种情况。
- 确保旧的报文段都消失:当TCP连接关闭时,发送方发送的最后一个ACK(确认)可能会丢失。如果接收方(被动关闭方)没有收到这个ACK,它可能会重新发送之前的FIN(结束)报文段。
-
持续时间:
TIME_WAIT
的持续时间通常是MSL(Maximum Segment Lifetime,最大报文段寿命)的两倍。MSL是TCP报文段在网络中的最大生存时间。在大多数操作系统中,这个时间默认设置为30秒到2分钟之间。
-
资源消耗:
- 由于
TIME_WAIT
状态会持续一段时间,并且每个连接都需要一个条目来跟踪其状态,因此在高并发的系统中,过多的TIME_WAIT
状态可能会导致资源耗尽(如内存)。为了避免这种情况,一些系统允许配置TIME_WAIT
的数量限制或时间限制。
- 由于
-
优化:
- 在某些情况下,可以通过调整TCP参数(如
tcp_fin_timeout
、tcp_tw_reuse
和tcp_tw_recycle
)来优化TIME_WAIT
的行为。但需要注意的是,不恰当地调整这些参数可能会导致问题,如数据包混淆或连接建立失败。
- 在某些情况下,可以通过调整TCP参数(如
-
如何查看:
- 在大多数操作系统中,可以使用如
netstat
、ss
或lsoft
等工具来查看当前处于TIME_WAIT
状态的TCP连接。
- 在大多数操作系统中,可以使用如
主动断开连接的一方,要进入TIME_WAIT状态(服务器所连接的端口号依旧被使用中,这就解释了为什么服务端断开后,马上重连,为什么提示地址已经被使用的原因)setsocketopt函数可以复用地址
总的来说,TIME_WAIT
状态是TCP协议中确保连接正确关闭和数据包不混淆的一个重要机制。但在某些情况下,它可能会导致资源消耗过多,因此需要谨慎处理。
理解 CLOSE_WAIT 状态
TCP的CLOSE_WAIT
状态是TCP连接关闭过程中的一个阶段,主要发生在服务器端。当客户端主动关闭一个TCP连接时,服务器端会经历从ESTABLISHED
(已建立连接)状态到CLOSE_WAIT
状态,再到LAST_ACK
(最后确认)状态,并最终进入CLOSED
(已关闭)状态的过程。
在CLOSE_WAIT
状态下,TCP连接处于半关闭状态。具体来说,当客户端调用close函数并发送一个FIN(结束)报文段给服务器端想要关闭文件描述符时,客户端会进入FIN_WAIT_1
状态,等待服务器端的响应。服务器端收到FIN后,会发送一个ACK(确认)报文段给客户端,并进入CLOSE_WAIT
状态。此时,服务器端知道客户端已经关闭了连接,但自己并没有主动关闭连接,因此连接处于半关闭状态。
在CLOSE_WAIT
状态下,服务器端需要等待应用程序关闭连接。如果服务器端的应用程序没有调用close函数来关闭连接,那么连接将一直保持在CLOSE_WAIT
状态。这可能会导致服务器端出现大量处于CLOSE_WAIT
状态的连接,从而消耗系统资源。
为了避免CLOSE_WAIT
状态过多导致的资源消耗问题,可以采取以下措施:
- 确保服务器端的应用程序在适当的时候调用close函数来关闭连接。
- 设置TCP连接的超时时间,以便在连接长时间处于空闲状态时自动关闭连接。
- 监控并优化服务器端的应用程序代码,确保在不需要连接时及时关闭连接。
需要注意的是,CLOSE_WAIT
状态是服务器端的一个正常状态,它本身并不表示连接有问题。然而,如果服务器端出现大量长时间处于CLOSE_WAIT
状态的连接,则可能表示存在资源消耗过多或连接泄漏等问题,需要进行相应的优化和调整。
Linux系统网络应用的时候越来越卡,因为文件描述符未关闭,一直处于TIME_WAIT状态,要有资源一直维护相关的连接结构体,所以卡顿
TCP的网络流量控制
TCP支持根据接收端的处理能力, 来决定发送端的发送速度. 这个机制就叫做流量控制(Flow Control)
TCP 的流量控制是指在数据传输过程中,控制发送方的发送速率,使其不会导致接收方缓冲区溢出或网络拥塞。TCP 使用滑动窗口(Sliding Window)机制来进行流量控制,主要通过以下方式实现:
-
接收窗口(Receive Window):接收方会在 TCP 头部中的窗口大小字段(Window Size)中指定一个接收窗口大小,表示自己当前能够接收的数据量。发送方根据接收方通告的窗口大小来控制发送数据的速率,以确保不会超出接收方的处理能力。
-
滑动窗口机制:发送方维护一个发送窗口的大小,表示发送方当前可发送但还未收到确认的数据量。接收方在接收数据后会发送确认(
ACK
)给发送方,并在确认中包含自己的窗口大小,发送方根据接收方的窗口大小和确认号调整自己的发送窗口大小。这样,发送窗口可以根据网络和接收方的状态动态地调整大小,以适应网络环境和接收方的处理能力。 -
零窗口控制:如果接收方的缓冲区已满,无法接收更多数据,则会将窗口大小设置为 0,发送方会停止发送数据,直到接收方通告可以接收数据的窗口大小不为零为止。
-
拥塞窗口控制:除了接收窗口控制外,TCP 还通过拥塞窗口控制来避免网络拥塞。拥塞窗口是发送方对网络拥塞状态的一种估计,通过动态调整拥塞窗口的大小来控制发送速率,以避免造成网络拥塞。
综合这些机制,TCP 可以实现可靠的流量控制,保证在不同网络条件下的数据传输的稳定性和高效性。流量控制使得发送方和接收方之间的数据传输能够协调顺利进行,避免了因发送速率过快导致接收方无法及时处理数据而产生的问题。
滑动窗口(慢启动和拥塞控制)
TCP网络流量控制的滑动窗口是一种重要的机制,用于确保发送方和接收方之间的数据传输速度相匹配,避免数据丢失和拥塞。以下是关于TCP网络流量控制的滑动窗口的详细解释:
- 滑动窗口的概念:滑动窗口是指在TCP连接的数据传输过程中,两端系统使用的流量控制机制。在滑动窗口协议中,发送端会维护一个窗口,表示当前可以发送的数据量;接收端也会维护一个窗口,表示当前可以接收的数据量。发送端会按照接收端给出的窗口大小来调节数据发送的速度,而接收端会根据接收到的数据来调节窗口的大小。
- 滑动窗口的作用:滑动窗口主要有两个作用,即可靠性和流量控制。可靠性体现在通过确认机制确保数据包的正确传输,而流量控制则解决了接收端和发送端对数据包处理速度不同的问题,确保双方达成一致。
- 控制流量的机制:TCP协议使用滑动窗口机制来控制流量,主要包括
慢启动
、拥塞避免
和拥塞控制
等阶段。在慢启动阶段,发送方会以较小的发送窗口开始传输数据,然后逐渐增加发送窗口的大小,以便测试网络的拥塞情况。在拥塞避免阶段,发送方会根据网络的拥塞程度来动态调整发送窗口的大小,以保持网络流量的稳定性。如果网络出现拥塞,接收方会发送拥塞通知给发送方,告知网络状况,发送方会根据接收到的拥塞通知来减小发送窗口的大小,以降低网络拥塞。 - 实现机制:发送方和接收方都维护一个
数据池(Buffer
)来处理数据。发送方不断地将要发送的数据送入sender buffer
,接收方不断地从receiver buffer
里获取数据。当接收方的receiver buffer已满时,发送方则暂时停止发送数据。发送方通过ack数据包
的window size字段获知接收方窗口的大小信息,从而确定应该发送多少字节的数据。
总之,TCP网络流量控制的滑动窗口是一种重要的机制,通过动态调整发送窗口的大小来控制数据传输的速度,确保发送方和接收方之间的数据传输速度相匹配,避免数据丢失和拥塞。
拥塞控制
流量控制中的拥塞窗口是TCP协议中用于实现拥塞控制的一个重要机制。它主要涉及到两个方面:一是防止发送方发送数据过快,导致接收方来不及处理,从而造成接收方资源的耗尽;二是防止过多的数据注入到网络中,导致网络拥塞
滑动窗口大小 = min(拥塞窗口大小,对方的窗口大小)
慢启动:2^n指数增长(当数据量大于慢启动阈值后开始线性增长)
当TCP开始启动的时候, 慢启动阈值等于窗口最大值;
在每次超时重发的时候, 慢启动阈值会变成原来的一半, 同时拥塞窗口置回1;
少量的丢包, 我们仅仅是触发超时重传; 大量的丢包, 我们就认为网络拥塞;
当TCP通信开始后, 网络吞吐量会逐渐上升; 随着网络发生拥堵, 吞吐量会立刻下降;
拥塞控制, 归根结底是TCP协议想尽可能快的把数据传输给对方, 但是又要避免给网络造成太大压力的折中方案
延迟应答和捎带应答
延迟应答和捎带应答是在TCP(传输控制协议)中用于优化网络性能和流量控制的两种重要技术。以下是关于这两种技术的详细解释:
-
延迟应答(Delayed Acknowledgment):
- 概念:延迟应答是一种优化技术,其中接收方在收到数据包后并不立即发送ACK(确认)数据包,而是等待一段时间(通常称为“延迟时间”)或直到有数据需要发送给发送方时才发送ACK。
- 目的:通过延迟应答,接收方可以更有效地利用网络带宽,因为它减少了ACK数据包的数量,从而增加了实际数据的传输效率。
- 工作机制:当接收方收到一个数据包时,它会检查其缓冲区,并确定是否还有足够的空间来处理更多的数据。如果缓冲区空间充足,并且还没有达到预定的延迟时间,接收方会等待直到延迟时间结束或需要发送数据给发送方时,再发送ACK。
一定要记得, 窗口越大, 网络吞吐量就越大, 传输效率就越高. 我们的目标是在保证网络不拥塞的情况下尽量提高传输效率
- 优点:通过减少ACK数据包的数量,延迟应答可以减少网络拥塞,并提高数据传输效率。
那么所有的包都可以延迟应答么? 肯定也不是;
数量限制
: 每隔N个包就应答一次;一般N取2,时间限制
: 超过最大延迟时间就应答一次,超时时间取200ms
-
捎带应答(Piggybacking Acknowledgment):
- 概念:捎带应答是一种优化技术,其中接收方在发送自己的数据给发送方时,将ACK数据包与这些数据一起发送,而不是单独发送ACK。
- 目的:捎带应答减少了网络上的数据包数量,从而提高了网络效率。
- 工作机制:当接收方需要发送数据给发送方时,它会在这些数据中包含之前接收到的数据包的ACK。这样,发送方在收到数据时就能同时知道哪些数据包已经被成功接收。
- 优点:通过减少单独发送的ACK数据包数量,捎带应答可以节省网络带宽,并降低网络拥塞的风险。此外,由于减少了数据包的数量,它还可以降低处理数据包所需的计算资源。
这两种技术都是TCP协议为了提高网络性能和流量控制而采用的重要优化措施。在实际应用中,它们通常会根据网络状况和数据传输需求进行动态调整。
粘包问题
粘包问题是指在网络通信中,特别是使用TCP协议进行通信时,发送方发送的若干包数据到达接收方时粘成了一包的现象。从接收缓冲区来看,后一包数据的头紧接着前一包数据的尾。
产生TCP粘包问题的原因主要有以下几个方面:
- 应用程序写入数据的速度快于发送数据的速度,这种情况下多次写入的小数据包可能会打包在一起发送(但并不一定)。
- 接收方缓冲区大小设置不合理,导致多个小数据包粘在一起传输,形成一个大数据包发送。
- 通过虚拟通道传输数据,但通道并没有提供消息边界保护的机制,导致多个数据包合并成一个数据包发送。
- 多个应用程序利用相同的TCP连接并发发送数据,因为TCP本身是流式协议,无法识别边界,所以多个应用程序同时发送数据包到同一个Socket连接上时,这些数据包有可能在接收端粘连在一起,形成一个数据包。
解决TCP粘包问题的方法有很多种,其中一种简单的方法是发送的数据协议定义发送的数据包的结构,包括数据头(数据包的大小,固定长度)和数据内容(数据内容,长度为数据头定义的长度大小)。这样,在发送端先发送数据包的大小,再发送数据内容;在接收端先解析本次数据包的大小N,再读取N个字节,这N个字节就是一个完整的数据内容。通过这种方法,即使数据包在传输过程中发生粘连,接收端也能正确地拆分包并还原数据。
TCP异常情况
TCP异常情况在网络通信中经常发生,以下是对您提到的几种TCP异常情况的解释:
-
进程终止:
- 当一个进程或应用程序终止时,与之相关的TCP连接也会被关闭。进程终止会释放与TCP连接相关的文件描述符和套接字资源。
- 在正常情况下,进程终止会触发TCP的“四次挥手”过程来优雅地关闭连接。这意味着发送方会发送一个FIN(结束)报文段,接收方会回复一个ACK(确认)报文段,然后接收方也会发送一个FIN报文段,最后发送方回复一个ACK报文段来确认连接的关闭。
-
机器重启:
- 当机器重启时,所有正在运行的进程都会被终止,包括与TCP连接相关的进程。
- 与进程终止类似,机器重启也会触发TCP的“四次挥手”过程来关闭所有活动连接(前提是系统按正确流程重启,且没有因为崩溃而突然断电)。然而,如果系统突然崩溃或掉电,那么TCP连接可能无法完成正常的关闭过程。
-
机器掉电/网线断开:
- 当机器掉电或网线断开时,TCP连接会突然中断,无法完成正常的关闭过程。
- 在这种情况下,TCP的
保活定时器
(keepalive timer)会发挥作用。保活定时器用于检测连接是否仍然活跃。如果一方在一定时间内没有收到另一方的任何报文段(包括ACK),那么保活定时器会触发,发送方会发送一个探测报文段来检查连接是否仍然可用。如果多次探测后仍然没有收到响应,那么发送方会认为连接已经失效,并关闭连接。 - 对于接收端来说,如果它长时间没有收到发送方的数据,并且发送了多个探测报文段后仍然没有收到响应,那么它也会认为连接已经失效,并关闭连接。
需要注意的是,TCP协议本身是为了确保数据在网络上的可靠传输而设计的,因此在发生异常情况时,它会尝试通过各种机制来恢复连接或确保数据的完整性和准确性。然而,在某些极端情况下(如机器突然掉电或网络硬件故障),TCP可能无法完成其任务,导致数据丢失或连接中断。
👉🏻telnet命令
telnet
是一个命令行下的网络诊断工具,用于测试 TCP 连接的连通性。你可以使用 telnet
来连接到一个远程服务器或设备,并与之进行交互。然而,需要注意的是,由于安全性的原因,现代操作系统(如 Windows 10 和 macOS)可能不再默认包含 telnet
客户端。
以下是一些基本的 telnet
命令和用法:
- 启动 telnet 客户端
在某些系统上,你可能需要首先安装 telnet
客户端。在 Debian/Ubuntu 系统上,你可以使用 sudo apt-get install telnet
来安装。
2. 连接到远程服务器或设备
使用以下命令连接到远程主机上的指定端口(默认为 23):
telnet hostname_or_ip_address [port]
例如,连接到 IP 地址为 192.168.1.1
的主机的默认 telnet 端口(23):
telnet 192.168.1.1
- 在 telnet 会话中
- 一旦连接成功,你可以输入文本并按下 Enter 键发送到远程主机。
- 你可能会看到来自远程主机的响应。
- 使用
Ctrl
+]
组合键可以进入 telnet 命令模式,然后输入quit
来退出 telnet 会话。
- telnet 命令模式
在 telnet 命令模式下(通过按 Ctrl
+ ]
进入),你可以输入各种命令来控制 telnet 会话,如设置选项、查看状态等。
5. 退出 telnet 会话
- 在 telnet 命令模式下,输入
quit
并按 Enter 键。 - 或者,你可以简单地关闭终端窗口(但这可能不会总是优雅地关闭连接)。
-
注意:
- 由于 telnet 传输的数据是明文的,因此它不适合用于传输敏感信息(如密码)。
- 在生产环境中使用 telnet 时要特别小心,因为它可能会暴露你的网络配置和拓扑。
- 更安全的替代方案包括 SSH(用于加密的远程登录)和 netcat(一个功能强大的网络工具,但也需要谨慎使用)。
如上便是本期的所有内容了,如果喜欢并觉得有帮助的话,希望可以博个点赞+收藏+关注🌹🌹🌹❤️ 🧡 💛,学海无涯苦作舟,愿与君一起共勉成长