目录
[1. 端口号](#1. 端口号)
[2. UDP](#2. UDP)
[3. TCP](#3. TCP)

1. 端口号
再来聊一聊端口号:

接收的报文到了传输层(tcp/udp),要向上交付到应用层,应用层的程序有很多,比如(自己写的服务,http、https...)数据要传个哪个程序?这时就需要port来进行标识 ;
在TCP/IP协议中,用 "源IP","源端口号","目的IP","目的端口号","协议号"这样一个五元组来标识-个通信(可以通过netstat -n查看);IP可以确定哪台机器,Port可以确定哪个应用程序那协议号是干什么的?


如图,IP和Port中间还要经过传输层;经过传输层就需要知道是把报文交给tcp还是udp;
使用命令:netstat -nltp查看
- n选项:属性中能用数字显示的就用数字显示
- t选项:只查看tcp协议
- u选项:只查看udp协议(选项中可以同时带t、u选项)
- p选项:查看服务名称(PID那一列)
- L选项:只查看listen状态的服务
- a选项:查看所有状态的服务(a、l选项可以同时带,效果和a选项效果相同)
端口号的划分
- 0 -1023:知名端口号,HTTP,FTP,SSH等这些广为使用的应用层协议,他们的端口号都是固定的
- 1024 - 65535:操作系统动态分配的端口号,客户端程序的端口号,就是由操作系统从这个范围分配的。
普通用户无法绑定0~1023端口;使用root可以绑定;有些服务器是非常常用的,为了使用方便,人们约定一些常用的服务器,都是用以下这些固定的端口号:
- ssh服务器,使用22端口
- ftp服务器,使用21端口
- telnet服务器,使用23端口
- http服务器,使用80端口
- https服务器,使用443
我们自己写一个程序使用端口号时,要避开这些知名端口号;使用 pidof
查找正在运行的进程的进程ID;
语法:
bash
pidof [选项] <进程名>
示例:
bash
pidof bash
#多进程:返回所有名为 sshd 和 httpd 的进程的 PID
pidof sshd httpd
2. UDP
UDP(用户数据报协议,User Datagram Protocol)是一种用于网络通信的传输层协议,UDP 是一种无连接的协议,它提供了一种简单、高效的数据传输方式,适合于需要快速交互但不一定需要可靠性的数据传输场景;
UDP 的特点 :
- 无连接性:UDP 不建立连接,数据包是独立的,发送方不需要与接收方建立连接。
- 低延迟:由于没有连接建立和维护的过程,UDP 通常能提供较低的延迟,非常适合实时应用(如视频会议、在线游戏等)。
- 不保证可靠性:UDP 不确保数据包的到达,发送的数据可能会丢失或顺序错乱,因此不适合对数据完整性要求高的应用。
- 简单的报文结构:UDP 的报文结构相对简单,头部较小(只有 8 字节),效率较高。头部包括源端口、目的端口、长度和校验和信息。
- 多播和广播支持:UDP 支持一对多的通信方式,例如进行多播或广播数据传输。
- **面向数据报:**应用层交给UDP多长的报文,UDP原样发送,既不会拆分,也不会合并;比如:用UDP传输100个字节的数据,如果发送端调用一次sendto,发送100个字节,那么接收端也必须调用对应的一次recvfrom,接收100个字节,而不能循环调用10次recvfrom,每次接收10个字节;
UDP协议格式

校验和:通过校验和来验证收到的数据是否在传输中被损坏;
端口号为什么是16位?底层协议udp端口号是16位;
无论是tcp还是udp都要考虑两个问题:
- 有效载荷和报头分离的问题
- 有效载荷向上交付的问题
对于udp来说,如何解决有效载荷与报头分离的问题?采用固定报头长度
向上交付的问题呢?目的端口号
在系统中的描述
如何理解报头? 其实就是一个结构化字段
cpp
struct udphdr
{
// 位段
uint32_t src_port:16;
uint32_t dst_port:16;
uint32_t len:16;
uint32_t checksum:16;
}
怎么判断,收到的报文是完整的?
- 如果报文长度不到8字节,那么报文一定不完整(报头都不完整)
- 如果大于8字节,那么就可以从报头中拿到udp的报文长度,长度-8就是数据长度;
比如应用层发消息给另一台主机(消息为hello)
cpp
struct udphdr hdr={ 1234,8888,5,XXXX };
需要对消息封装报头:
udp在封装时包含:1.描述结构体 2.保存数据和报头的缓冲区;
这是一个报文的情况,那多个报文呢?
就需要进行管理,在struct sk_buff中还有一个指针(struct sk_buff*)

对报文的管理就变成了对链表的增删查改;
缓冲区
- UDP没有真正意义上的 发送缓冲区,调用sendto会直接交给内核,由内核将数据传给网络层协议进行后续的传输动作;
- UDP具有接收缓冲区。但是这个接收缓冲区不能保证收到的UDP报的顺序和发送UDP报的顺序一致;
如果缓中区满了,再到达的UDP数据就会被丢弃;
UDP的socket既能读,也能写,这个概念叫做全双工;
如何理解"缓冲区"?udp的缓冲区其实就是一个队列,也就是上述管理udp数据的链表;
使用注意事项
我们注意到,UDP协议首部中有一个16位的最大长度,也就是说一个UDP能传输的数据最大长度是64K(包含UDP首部)然而64K在当今的互联网环境下,是一个非常小的数字,如果我们需要传输的数据超过64K,就需要在应用层手动的分包,多次发送,并在接收端手动拼装;
3. TCP
TCP(传输控制协议)广泛用于互联网和其他网络中;主要有以下特点:传输层协议、有连接、可靠传输、面向字节流;
有连接怎么理解?
在使用TCP时需要创建一个listen套接字,然后accept接受客户端连接,然后返回一个普通的套接字;TCP是可靠传输,为例保证消息的送达,需要和每个客户端建立一个连接,通过连接进行一对一的通信;
怎么理解listen套接字?
举个例子:
你和朋友出去玩,到了饭点想找饭店去吃饭,此时看到一家店,外边站着一个人拉着你们进到店里面吃饭,将你们带到店里后,又出去拉其他的客人,进到店里你和朋友又会有其他服务员进行服务(比如:找包间、点菜,上菜);
- 拉客的人 --- Listen 套接字:
Listen 套接字负责监听外部连接请求,类似于拉客的人,他们在外面主动接待顾客,等待新客人的到来。当有新顾客出现时,拉客的人将其引导入店,类似于 Listen 套接字准备接受来自客户端的连接。
- 顾客 --- 连接请求(客户端):
顾客是连接请求的发起者,他们希望进入饭店就餐。在 TCP 中,客户端通过发送连接请求(SYN)来与你的服务器建立连接。
- 饭店内部的服务员 --- 普通套接字:
进入餐厅后,顾客会与服务员进行互动,进行下单和就餐。普通套接字用于与已经建立连接的客户端进行双向通信。一旦把顾客引入店里,拉客的人就不再参与沟通,服务员负责提供后续的餐饮服务(即数据交换)
- 点菜、上菜的过程 --- 数据传输:
在就餐过程中,顾客与服务员之间进行信息交互,类似于数据包的收发。在 TCP 中,这个过程涉及到数据的传输、确认及重传等操作。
- 餐厅的管理 --- 连接的管理和关闭:
当顾客用餐完毕后,服务员会处理结账并告知顾客如何离开,类似于通过"四次挥手"的过程安全地关闭连接。当一方完成通信时,会发送 FIN 信号,另一方确认接收到这个信号后,结束连接。
缓冲区
tcp具有发送和接受缓冲区,也是全双工通信;
比如:应用层发送一个 "hello" 发送给其他主机,调用 send/write 本质就是将数据写入到发送缓冲区,发送数据时就会从发送缓冲区中拿取数据进行发送;目标主机接收到后会将接收到的数据存放到接收缓冲区;上层调用 read/recv 读取缓冲区中的数据进行处理;

在接受数据时,如果接受缓冲区满了,缓冲区就不再接受数据,再来数据就会直接丢弃
这种设计并不会影响tcp的通信,被丢弃的数据也会通过一定方法,让发送方知道;如果此时还一直保持发送,显然这样是合理的,浪费流量;对于这些问题,需要通过协议来解决;
TCP协议格式
TCP可以做到可靠传输,那它如何确保数据可靠的送到呢?------协议;下面是它的协议格式:

4位首部长度: 0000 取值范围 [0~15],15字节连tcp报头的标准长度都不够; 这里需要乘首部的基本单位(4字节)那么tcp首部长度范围是[0~60];tcp首部最长是60字节;
而标准报头的长度是20字节(一行32位,4字节,5行20字节),减去标准报头20字节,选项最多可以是40字节(选项可以忽略);
如何封装报头呢?
tcp封装报头的过程和udp相似,有一个struct tcphdr结构体struct sk_buffer中维护两个指针,data和tail,封装报头,只需知道报头的大小,然后让data指针向前移到sizeof(tcphdr)字节,将报头数据填充,封装报头;
校验和:通过校验和来验证收到的数据是否在传输中被损坏;
32位序号: 在实际场景中,服务端可能同时接收到许多条消息的,由于收到网络的影响,消息很可能不是按序的到达(收到的数据顺序可能是乱的),这样就会导致数据损坏不能使用;因此它需要保证数据按序到底;如何保证?------**32位序列号;接受方根据报头中的32位序号对数据进行排序,**这样就可以确保接收到的报文是按照顺序的;
32位确认序号: 历史自己发送的哪些报文已经被对方收到,确认序号有这样的规定;**只要收到一个确认序号,就表示接收方已经成功接收了该序号之前的所有字节数据;**比如发送4次,序号分别是100、200、300、400;确认序号通常是201、301、401、501;在应答时前三个报文都可能丢失,但只要收到401就表示401之前的报文都已被接收;
为什么要分32位序号和确认序号,都使用序号不行吗?
因为tcp通信是全双工的,client在向server端发送数据的同时,server端也可能向client端发送数据;

在实际常见中,应答其实是可以和发送的数据合二为一的(1、2步合二为一) ;每次发送消息(包括确认应答)都是一个完整的TCP报文,都包含以下结构:

那么就可以做到,在发送消息的同时,进行消息的确认应答;tcp保证可靠性的同时,还会进行各种提高效率的设定,但这些都是内核自主完成的,用户毫不知情;
标记位

在进行通信时,server 端一定会同时收到各种各样不同类型的报文,这些报文都有类型(建立连接、断开连接、发送数据...)因此就需要有标记位标记消息类型;
SYN建立连接请求,ACK应答,FIN断开连接;
**URG表示紧急数据:**比如服务端收到很多消息,这些消息的处理都需要进行排队,如果来了紧急任务,那么就需要优先对紧急任务进行处理(插队)只要UGR标志位被置为1,就表明是紧急数据;
有标记位,那紧急数据在哪?
这时就需要用到紧急指针,紧急指针是紧急数据在有效载荷中的偏移量,只要URG标记位无效,紧急指针就无效;标记位被置1,就读取紧急指针;找到了紧急数据,那紧急数据的大小是多少?------使用时一般设定为1字节,也就是说允许插队,但不允许大量插队;
比如:
1表示终止或暂停上传行为,2表示...;
服务器压力打,可以设置紧急数据,
recv中的flag字段可以设置接收紧急数据;询问服务状态:0表示状态良好、1表示压力有点大,但还正常、2表示压力有点大,快要撑不住了...;
PSH:表示推送,催促尽快将数据向上交付; 当接收方上层应用层处理比较耗时------卡住了
接受缓冲区就会满,那么接收方在响应时,窗口大小就会置为0,发送方此时就能知道接受方暂时无法继续接受数据,就会停止发送新的数据,问题是,发送方如何知道接受方什么时候能接收数据?发送方会定期发送询问请求(请求只有报头,没有任何数据,报头数据采用上次报头的数据)接收方收到请求就要响应应答,应答时就会附带自己窗口大小;经过多次的询问,如果窗口大小依然是0,这时就可以添加PSH,以前不带PSH,就像一直在问好了没,带上PSH只后就是,好了没赶快把数据向上层交付(表示表单的语义),PSH不仅仅在这种情况下使用,需要数据尽快交付的场景下都可以使用比如: xshell,连接远端机器,发送指令给ssh,每次发送都会携带PSH,就是为了让指令被快速响应;
**RST:**TCP建立连接需要进行3次握手,但是3次握手也可能会失败,此时就可能会导致,建立连接认知不一致;比如:client向server发送数据,在server处理请求的时候,client端把网线拔了,client端建立的连接就直接释放了;但server并不知道,继续给client发消息;会儿client端恢复网络,client就会疑惑,不是三次握手吗?我还没发请求怎么就应答了,这时client也可以向server发送RST(重置请求);RST对于双方都是对等的;
面向字节流

用户发送数据,其实只是把数据拷贝到发送缓冲区,对于TCP的缓冲区,可以把发送缓冲区理解成一个char类型的数组(面向字节流以字节为基础单位,和字符数组很像);比如:发送4个字节,发送的最后字节的序号就是3,(也就是32位序号)对方收到后返回的一般都是4(表示下一次从4开始发,并不代表下一次的序号就是4)比如:第二次继续发4个字节,发送的最后一个字节位置是7,那发送tcp报头中的序号就是7;
接收端收到后,在上层读取时,读取的就是一个一个的字节,这也就是面向字节流;
字节感受到了,流在哪?
注意TCP发送的报文中并没有明确有效载荷的长度;TCP根本就不管报文的分隔;它只管把报头和有效载荷的分离,然后把有效载荷直接无脑的放到接收缓冲区中;所以在接收缓冲区中,可能已经积攒了几十个历史报文的数据;上层读取时,就需要对这些报文进行分割处理(http/https),在这个过程中缓冲区有拿出数据的,有加入数据的;有出有进就形成了流动的概念;
确认应答机制
TCP为了保证可靠传输,对于每条消息都有一个确认应答机制;

任意一方向另一方发送消息时,接收方都要应答(表示收到)应答机制可以保证,发送的数据被对方已经收到;当然,确认应答的功能不止于此,这里仅需了解有确认应答机制,在下文会进行补充;
流量控制
如果接收方来不及接受数据,发送方会根据接收方的接收能力调整发送速度,暂时停止发送数据或者减少发送数据量,以避免数据丢失或网络拥塞;可以避免流量的浪费;
这个控制的过程由发送方的tcp协议做的(OS)上层用户不需要关心;
如何进行流量控制?发送方要知道接收方的接受能力;
在TCP报头中有一个16位的窗口大小, 用来进行流量控制的字段,表示自己接收缓冲区剩余空间的大小;流量控制的过程由发送方和接受方OS共同协商完成;
超时重传

发送方发送出去消息,如果没有收到应答,就会再次发送数据,超出重传机制可以确保消息被对方接收到;重传机制对于双方来说是对等的;不管是数据还是应答,只要丢了,发送方都会超时重传,这种情况会造成主机B收到两个报文(一般主机B会使用最新的)因此主机B需要有去重操作(根据序号去重);发送方一旦把数据发送出去一段时间内,已经发送的数据不能被移除;应该被暂时保存起来;
网络状态是变化的;如果超时重传等待时间太久------效率就会变低;如果超时重传等待时间太短------过于频繁的进行重传浪费流量;因此TCP为了保证无论在任何环境下都能比较高性能的通信,因此会动态计算这个最大超时时间;Linux中(BSD Unix和Windows也是如此),超时以500ms为一个单位进行控制,每次判定超时重发的超时时间都是500ms的整数倍;如果重发一次之后,仍然得不到应答,等待 2*500ms 后再进行重传;如果仍然得不到应答,等待 4*500ms 进行重传。依次类推,以指数形式递增累计到一定的重传次数,TCP认为网络或者对端主机出现异常,强制关闭连接;
连接管理
在正常情况下, TCP要经过三次握手建立连接, 四次挥手断开连接;

server端一定允许同时存在很多个已经完成三次握手的连接;OS需要对多个连接进行管理;描述对象,再组织管理;
cpp
struct links {
int start_seq; // TCP连接的起始序列号
std::string src_ip; // 源IP地址
std::string dst_ip; // 目标IP地址
int srcport; // 源端口号
int dstport; // 目标端口号
uint64_t timestamp; // 连接的时间戳,通常用于记录连接建立的时间
int status; // 连接状态,可以用来表示连接的不同状态(如连接中、已连接、关闭等)
int urg_data_ptr; // 指向紧急数据的指针,表示紧急数据的序列号
struct links *next; // 指向下一个连接的指针,用于构建链表结构
};
双方如果建立连接,那么在双方的0s中就要构建类似于这样的连接结构体,client和server建立连接后,client要维护链接,server也要维护链接;接收方收到紧急数据,把数据加到缓冲,struct links中:urg_data_ptr = 缓冲区中原始报文的地址 + 偏移量,上层读取缓冲区数据没有读到紧急指针,假如只读了10字节:urg_data_ptr = urg_data_ptr - 10;类似这样更新一下紧急指针,双方维护连接是有成本的:时间 + 空间;
链接时会有状态的变化,比如:把一个请求发送过去后,就可以设置状态
- #define SYN_SENT 1
- #define SYN_RCVD 2
为什么要三次握手,不能是1次、2次、5次?
- 确认通信信道是否通畅(对于client和server双方来说,都要确保数据能正常收发)
- 最小成本验证全双工;
- 降低SYN泛洪攻击的风险:客户端先建立连接,服务端后建立连接,增加了攻击成本;
- 确保建立可靠的连接:3次握手可以确保双方可以正常的进行通信,同时可以确保对方可以收到消息,在任何一个阶段都有确切的结果(知道对方收没收到消息);
- 数据同步确认:确认序号、状态同步、滑动窗口大小同步等;
四次挥手,为什么要四次挥手?

tcp协议是对等的,client端向server端建立连接;server端也要向client端建立连接;
从数据传输角度:
- client向server发送消息
- server向client发送消息
client向server端发送断开连接(更像是告知),client端数据发送完了,再也不向server发送数据了;c->s信道关闭;
但是server端并不一定已经把所有的数据发送完,server还需要向client发送数据,s->c信道不会关闭;server端发送完数据才会断开连接;
为了保证可靠的通信,就必须要保证两个朝向上的数据都发完了;所以也就有了四次挥手;当然也存在三次挥手的情况,双方同时把数据发送完的情况比较少;或者双方断开连接的意愿都非常强的情况下可能会三次挥手;
滑动窗口
一发一收会很影响性能,那么我们一次发送多条数据,就可以大大的提高性能;也是流量控制的解决方案和重传机制也有关系;

滑动窗口在哪里?滑动窗口是发送缓冲区的一个区域

怎么理解滑动窗口?

[win_start,win_end]:滑动窗口,滑动窗口本质就是下标的移动 ;
滑动窗口多大,应该由谁决定?当前窗口大小,由对方的接受能力决定
如何更新?
- win_start = 确认序号
- win_end = win_start + win(窗囗大小)
在最开始,还没有发送的时候,滑动窗口应该多大?窗口大小在三次握手的时候已经协商完毕;
TCP首部中,有一个16位窗口字段,就是存放了窗口大小信息;那么问题来了,16位数字最大表示65535,那么TCP窗口最大就是65535字节么?
实际上,TCP首部40字节选项中还包含了一个窗口扩大因子M,实际窗口大小是 窗口字段的值左移 M位;窗口大小*M;
滑动窗口的大小不会变没?变大?变小?为零?可以
发送方收到响应,得知win大小一直在变小,收到一个应答,win_start就向左移动一次;win_end一直不变,窗口的大小不断变小;直至为零(接收缓冲区满了);

比如:发送5条消息,序号分别是500、600、700、800、900;对应应答如上图;上层不把数据取走,响应时win大小一直在变小;上层读取缓冲区的数据后,缓冲区有空间了,就会发送窗口更新的通知;
滑动窗口可以向左移动吗?绝对不可能
窗口一直向右滑动,越界了怎么办?不会,tcp对缓冲区做了很复杂的处理可以直接认为它是一个环形结构即可;
当然它也可以延迟应答,避免重复的应答;
延迟应答
发送5条消息,序号分别是500、600、700、800、900;正常情况下需要一个一个的应答,但是它还可以延迟应答,等消息接收完之后应答确认序号1001(1001以前的数据都已接收),只应答一次;
如果接收数据的主机立刻返回ACK应答, 这时候返回的窗口可能比较小. 假设接收端缓冲区为1M. 一次收到了500K的数据; 如果立刻应答, 返回的窗口就是500K;但实际上可能处理端处理的速度很快,10ms之内就把500K数据从缓冲区消费掉了;在这种情况下, 接收端处理还远没有达到自己的极限,即使窗口再放大一些, 也能处理过来;如果接收端稍微等一会再应答,比如等待200ms再应答, 那么这个时候返回的窗口大小就是1M;窗口越大, 网络吞吐量就越大, 传输效率就越高. 我们的目标是在保证网络不拥塞的情况下尽量提高传输 效率;那么所有的包都可以延迟应答么? 肯定也不是;
- 数量限制: 每隔N个包就应答一次;
- 时间限制: 超过最大延迟时间就应答一次;
具体的数量和超时时间, 依操作系统不同也有差异; 一般N取2, 超时时间取200ms;

捎带应答
在延迟应答的基础上,可以发现,很多情况下,客户端服务器在应用层也是 "一发一收" 的. 意味着客户端给服务器说 了 "How are you", 服务器也会给客户端回一个 "Fine, thank you";那么这个时候ACK就可以搭顺风车,和服务器回应的 "Fine, thank you" 一起回给客户端;
快重传
报文丢失了怎么办?滑动窗口内的数据丢失分三种:1.最左侧的丢失 2.中间的丢失 3.最右侧的丢失;很简单,根据应答的确认序号即可知道,确认序号表示:在次之前所有的数据已经接收到;
比如:发送消息的序号:500、600、700、800、900;但应答的是600(一次发99字节),那说明600序号以前的数据已经全部收到,也就是说发送序号为600的消息丢失了,后3个不知道是否丢失,然后立马补发600序号的消息,如果收到了1000的应答,那么说明消息补发完毕;
拥塞控制
因为网络上有很多的计算机,可能当前的网络状态就已经比较拥堵,在不清楚当前网络状态下,贸然发送大量的数据,是很有可能引起雪上加霜的;TCP引入 慢启动|机制,先发少量的数据,探探路,摸清当前的网络拥堵状态,再决定按照多大的速度传输数据;

第一次发送一条报文,第二次发送2条,第三次发送4条,2^n-1;
此处引入一个概念程为:拥塞窗口,发送开始的时候,定义拥塞窗口大小为1;每次收到一个ACK应答,拥塞窗口加1;每次发送数据包的时候,将拥塞窗口和接收端主机反馈的窗口大小做比较,取较小的值作为实际发送的窗口;滑动窗口大小 = min(拥塞窗口大小,对方窗口大小);
拥塞窗口就是一个数字,发送数据量超过拥塞窗口的时候,很大概率会引起网络拥塞;
刚开始窗口大小较小,可以成倍的增加,到后边窗口变大就不能成倍增加,增长策略如下:

为了不增长的那么快,因此不能使拥塞窗口单纯的加倍,此处引入一个叫做慢启动的阈值,当拥塞窗口超过这个阈值的时候,不再按照指数方式增长,而是按照线性方式增长,在每次超时重发的时候,慢启动阈值会变成原来的一半,同时拥塞窗口置回1;
少量的丢包,仅仅是触发超时重传;大量的丢包,就认为是网络拥塞;当TCP通信开始后,网络吞吐量会逐渐上升;随着网络发生拥堵,吞吐量会立刻下降;拥塞控制,归根结底是TCP协议想尽可能快的把数据传输给对方,但是又要避免给网络造成太大压力的折中方案;实际发送情况不一定按照图中的规律(需要考虑对方接受能力);
粘包问题
粘包问题只会出现在TCP通信中,站在传输层的角度, TCP是一个一个报文过来的. 按照序号排好序放在缓冲区中;
站在应用层的角度,看到的只是一串连续的字节数据;
应用程序看到了这么一连串的字节数据,就不知道从哪个部分开始,到哪个部分,是一个完整的应用层数据包;
UDP没有粘包问题吗?
UDP协议中有自描述字段(报文长度)可以知道报文长度是多少,对于UDP, 如果还没有上层交付数据, UDP的报文长度仍然在. 同时, UDP是一个一个把数据交付给应用 层. 就有很明确的数据边界. 站在应用层的站在应用层的角度, 使用UDP的时候, 要么收到完整的UDP报文, 要么不收. 不会出现"半 个"的情况;
那么如何避免粘包问题呢? 归根结底就是一句话, 明确两个包之间的边界.
- 定长报文
- 自描述字段(报文长度)
- 使用明显的分隔符进行区分
这个过程就是应用层自定义协议;
应用层协议很大部分是为了解决粘包问题 ,其次就是序列化和反序列化;
序列化与反序列化:就算将缓冲区中的数据取出来,得到一个完整的报文,但是报文依然只是一个字符串;在程序中使用时一般都是结构体对象,因此还需要对字符串进行转化,这个转化的过程就是反序列化,将结构化的对象转化为字符串就叫序列化;
这里不做过多介绍;知道有这个概念就行;
TIME_WAIT状态
TIME_WAIT状态是 TCP 连接的一种状态,主要用于确保数据的可靠传输和连接的正确关闭;主动关闭连接的一方(即发送了 FIN 报文的一方)进入此状态;
比如:

服务端进入了TIME_WAIT状态;TIME_WAIT状态的持续时间通常是 2 倍的最大段生存时间(Maximum Segment Lifetime, MSL),通常设定为 2 到 4 分钟。这个时间可以根据操作系统的不同而有所变化;
为什么会有这个状态,或者说它的作用是什么?
- 确保数据完整性:当一个 TCP 连接关闭后,可能还有一些数据在网络中尚未到达接收方。TIME_WAIT状态确保在连接关闭后,发送方仍然保持一段时间的状态,以便接收任何可能的重传数据。
- 避免延迟重用端口:在 TIME_WAIT状态期间,TCP 不会立即重用该连接的端口号,这样可以避免在新连接中出现混淆,确保新连接不会接收到旧连接的数据。
对于客户端而言,进入 TIME_WAIT状态通常影响较小,主要原因:客户端的端口是随机分配的,其次是该状态的持续时间不会很长,因此影响较小;
使用setsockopt可以设置重复的使用端口号,来解除TIME_WAIT的影响
cpp
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
使用示例:
cpp
int optval = 1; // 设置选项值为 1,表示启用 SO_REUSEADDR
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &optval, sizeof(optval)
重复使用不会影响新服务接收到的数据吗?
可能会,但概率很小,几乎不会出现,这种情况出现的不频繁,其次就是缓冲区存放的数据一般不会重叠覆盖,因为起始的序号都是随机的,差别比较大,这也降低了数据混淆的概率;
基于TCP协议的应用层协议:
- HTTP
- HTTPS
- SSH
- Telnet
- FTP
- SMTP
当然, 也包括你自己写TCP程序时自定义的应用层协议;
总结
以上便是本文的全部内容,希望对你有所帮助,感谢阅读!