当前位置: 首页 > news >正文

Linux网络编程 深入解析Linux TCP:TCP实操,三次握手和四次挥手的底层分析

知识点1【TCP编程概述】

1、TCP的概述

客户端:主动连接服务器,和服务器进行通信

服务器:被动被客户端连接,启动新的线程或进程,服务器客户端(并发服务器)

这里重复TCP和UDP特点

TCP(传输控制协议):是一种靠谱的传输层协议,

买电话 买电话卡 开声音 按下接听

知识点2【TCP客户端编程】

1、创建TCP套接字

        SOCK_STREAM

        socket函数创建的TCP套接字,没有端口,且默认是主动连接特性

2、connect函数连接服务器

   #include <sys/types.h>          #include <sys/socket.h>int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

功能介绍

        客户端主动发出与TCP服务器的连接(三次握手)

参数

        sockfd:客户端套接字

        addr:只想服务器的地址结构体

        addrlen:地址结构体的长度

返回值

        成功:0

        失败:返回-1

注意

        TCP客户端通信之前,必须实现 建立和服务器之间的连接

        connect连接成功一个服务器,不能再次连接其他服务器

        inet_addr()(补充函数,平替pton)函数仅支持IPv4

        如果socket没有固定端口,在调用connect时系统自动分配随机端口为源端口

                connect实际上是带阻塞的,需要三次握手

3、send发送消息

#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);

功能介绍

        通过已连接套接字发送数据

参数

        sockfd:已连接套接字(下面accept函数中会介绍)

        buf:带发送数据的缓冲区指针

        len:要发送的字节数

        flags:控制标志

  • MSG_OOB:发送带外数据(紧急数据)。
  • MSG_DONTWAIT:非阻塞发送(立即返回)。
  • MSG_NOSIGNAL:禁止发送SIGPIPE信号。

返回值

        成功:返回实际发送的字节数

        失败:返回-1

注意

        TCP不能发出0长度报文,但是UDP可以。

4、recv接收数据(默认阻塞)

   #include <sys/types.h>#include <sys/socket.h>ssize_t recv(int sockfd, void *buf, size_t len, int flags);

功能介绍

        从已连接套接字接收数据

参数

        sockfd:已连接套接字

        buf:接收数据的缓冲区指针

        len:缓冲区最大容量

        falgs:控制标记

返回值

        成功:返回实际接收到的字节数

        失败:返回-1

注意

        recv如果收到0长度报文,表明对方已经断开连接,因此我们使用send的时候,不能发送0长度报文

        只要一方关闭,另外一方就会收到0长度报文

知识点3【TCP服务器编程】

我们先把服务器的过程形象化:

1、socket() 买手机

2、bind() 买电话卡

3、listen() 打开声音

4、accept() 接听

1、作为服务器的条件

1、需要为服务器绑定一个固定的端口、IP(连接作用)

2、让操作系统知道这是一个服务器,而不是客户端

(使用listen函数让服务器具备监听功能,使套接字由主动变被动

3、等待客户端的连接到来,使用accept提取到来的客户端

2、listen监听函数

   #include <sys/types.h>          /* See NOTES */#include <sys/socket.h>int listen(int sockfd, int backlog);

功能介绍

        将套接字设为被动监听模式,等待客户端连接

参数

        sockfd:套接字

        backlog:连接队列的最大长度

返回值

        成功:0

        失败:-1

函数功能详解(底层)

        1、将sockfd由主动变被动,并且对sockfd进行监听客户端连接的到来

        2、backlog是连接队列的大小,表示客户端的最大个数

连接队列大小分析:

        1、在listen后,在TCP服务器中,套接字改称为监听套接字,所有客户端想要连接,就需要向这个客户端发送连接请求

        2、客户端发出连接请求(connect),TCP的server就会创建一个连接队列

        3、连接队列又会分为两部分,但是总大小是上面对应的个数

        (1)完成连接——三次握手之后

        (2)未完成连接——三次握手完成之前

图形解析

这里说一个早期的一个攻击手段:SYN洪流攻击

就是一直发送低于三次握手信号(半成品),这样的信号全部在连接队列的未完成连接中存储,服务器也无法处理,当达到连接队列的最大值后,正常连接反而进不去连接队列。

这里

我们写监听函数,并阻塞,查看其网络状态

netstate -anp | grep a.out

可以看到此时处于监听状态(想系统说明此时是一个服务器)

3、accept提取客户端的连接(阻塞

   #include <sys/socket.h>int accept(int socket, struct sockaddr *restrict address,socklen_t *restrict address_len);

函数功能

        accept只能从 连接队列 中 处于 完成连接部分 中提取连接

        将提取到的该客户端的信息存到addr

        将提取到的该客户端从连接队列中删除

参数

        sockfd:监听套接字

        addr:存放的是 客户端 的地址信息

        addrlen:地址结构体的长度的地址

返回值

        成功:返回一个已连接套接字,这个套接字才代表服务器和该客户端的连接端点(真正和客户端的连接)

        解释:如果服务器想要和该客户端通信,就需要向这个 已连接套接字 中读写

失败:返回-1

注意

        调用一次,只能提取一个客户端对应的连接,如果连接队列没有客户端连接,将阻塞

        因为是从 连接队列 中提取的原因,遵循先进先出的原则

        验证一下:通过两个客户端,都连接我们写的服务器,会发现只有一个能发送

TCP服务器代码演示

#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>int main(int argc, char const *argv[])
{//创建监听套接字int fd_sock_lis = socket(AF_INET,SOCK_STREAM,0);if(fd_sock_lis < 0){perror("socket_lis");_exit(-1);}//绑定固定端口  这里我们设置为8000struct sockaddr_in addr_bind;bzero(&addr_bind,sizeof(addr_bind));addr_bind.sin_family = AF_INET;addr_bind.sin_port = htons(8000);addr_bind.sin_addr.s_addr = htonl(INADDR_ANY);int ret_bind = bind(fd_sock_lis,(struct sockaddr *)&addr_bind,sizeof(addr_bind));if(ret_bind < 0){perror("bind");_exit(-1);}//监听 参数:监听套接字,连接队列的个数int ret_listen = listen(fd_sock_lis,10);if(ret_listen == -1){perror("listen");_exit(-1);} //提取客户端连接,已连接套接字的创建,已连接套接字理解为通信端口的一端//服务器通过操作这个已连接套接字与客户端进行联系struct sockaddr_in addr_accept;bzero(&addr_accept,sizeof(addr_accept));int len_accept = sizeof(addr_accept);int fd_sock_connect = accept(fd_sock_lis,(struct sockaddr *)&addr_accept,&len_accept);if(fd_sock_connect < 0){perror("accept");_exit(-1); }//接收数据,从已连接套接字中接收char arr_recv[256] = "";recv(fd_sock_connect,arr_recv,sizeof(arr_recv),0);//处理 客户端端口 的信息int port_client = ntohs(addr_accept.sin_port);char ip_client[16] = "";inet_ntop(AF_INET,&addr_accept.sin_addr.s_addr,ip_client,sizeof(ip_client));//遍历接收到的信息printf("从IP:%s的%d端口,获取的数据是%s\\n",ip_client,port_client,arr_recv);//发送应答send(fd_sock_connect,"ok",sizeof("ok"),0);//关闭 所有套接字close(fd_sock_connect);close(fd_sock_lis);return 0;
}

代码运行结果

知识点4【close关闭套接字】

当客户端用完后会调用close套接字,服务器也要关闭套接字(监听套接字,已连接套接字)

1、作为客户端

close(套接字),断开当前的连接,导致服务器收到0长度报文

2、作为服务器

close(监听套接字),该服务器不能监听新的连接的到来,但是不影响已连接客户端的通信

close(已连接套接字),只是断开当前客户端的连接,不会影响监听套接字(服务器可以继续监听新的连接的到来)

这里我们形象化一下

形象化理解

媒婆 监听套接字

小帅 客户端套接字

小美 已连接套接字

小帅找媒婆介绍对象,媒婆有个名单,把小美介绍给了小帅,小美和小帅分还是合,最后不会影响媒婆招揽其他生意

如果媒婆不想干媒婆这一行了,去转学嵌入式了,也不会影响小帅和小美的关系

3、下面内容前提

握手,挥手都涉及到底层,我们需要用抓包的操作来了解这一过程,我这里用的抓包工具是 wireshark

这是 我使用的抓包工具获取的上面的发送过程

由于我的 图不太好识别

我用我老师当时的抓包图片(很标准的三次握手,数据通信,四次挥手抓包过程

我们分为三个流程 1、三次握手,2、数据通信,3、四次挥手 这里大家先看一下总的流程图,流程图是当时老师画的,我感觉很牛,这里直接使用了

知识点4【三次握手】(重要 背!!)

客户端调用connect连接服务器底层会完成三次握手信号,此时客户端阻塞,当三次握手信号完成,connect才会解除阻塞往下执行

三次握手的发起者客户端

1、TCP的头部

这里先介绍一下 TCP的头部

1、SYN:置1,表示报文是 连接请求报文

2、FIN:置1,表示报文是 关闭请求报文

3、ACK:置1,表示报文是 回应(应答)报文

4、URG:置1,表明紧急指针字段有效,告诉操作系统此报文内有紧急数据,请尽快传送

5、PUSH:置1:推送报文

6、RST:置1:复位连接

注意

序列号:seq,当前报文的编号。

确认序号:当前报文希望接下来对方发送的报文编号

ack(确认序号)数据分析

1、当无数据时,ack = 对方原编号 + 1

2、当 有数据时,ack = 对房源编号 + 接收到数据长度

2、三次握手

分析过程

1、当客户端调用connect后,底层发送SYN连接请求

此时seq = 0,ack = 0

2、服务器收到客户端发出的SYN请求,服务器给客户端回应SYN和ACK应答

此时seq = 1,ack = 1(无数据)

3、客户端接收到服务器发送的SYN请求后,向服务器发送ACK应答

此时seq = 1,ack = 1(原本服务器seq = 0,无数据+1后为1)

形象化理解

小帅给小美表白

小帅:我喜欢你(SYN)

小美:我知道(ACK),我也喜欢你(SYN)

小帅:其实我也知道(ACK)

3、分析通信过程

这里客户端发送的时”hello ack“,服务器应答我们设置的时”ok”

现在我们分析一下(每个红线是一个步骤)

分析过程

1、客户端和服务器经过三次握手后已经 完成连接(连接列表),客户端向服务器发送”hello ack“

这里有一个技巧:连续的线都是一个方向时,线对应的seq和ack一样

此时seq = 1,ack = 1,len = 9

2、服务器收到数据后,及其长度,发出ACK应答

此时seq = 1(与上面的ack相对应),ack = 10( 1+len(9))

3、服务器发送数据“ok”后,客户端接收

此时seq = 1,ack = 10(线 连续且方向一样),len = 2

4、客户端接收数据,发出ACK应答

此时seq = 10,ack = 3

确认序号的作用

1、当发送端数据时500位的时候,接收时仅收到了256位

此时len = 500,但实际发送的时候 接受到的长度的时256

这时,ack的值位 对方原序号码 + 256

接收端收到后,用ack - 原序号吗算出 接收数据长度,发现与发送数据长度不同,底层会处理后,继续发送没有接收的数据

2、检验是否发生错误传输,这里不多介绍

4、四次挥手

这里补充一个概念:FIN标志位置1的前提是调用close函数

服务器和客户端都可以先退出,一般都是客户端先退出

分析过程

1、当客户端调用close(套接字)函数,触发底层发出FIN断开连接的请求。

2、服务器收到FIN关闭请求,做出ACK应答(服务器进入CLOSE_WAIT状态)。

CLOSE_WAIT状态是指对方套接字已经关闭,等待 服务器 已连接套接字关闭,即调用close(对应已连接套接字)

3、服务器应用层调用close函数后,触发底层发送FIN。

4、客户端收到FIN请求,回应ACK。

以上内容依次是四次挥手

下面我想补充说明一些知识点

补充(重要)

1、细心的同学已经发现了,在第三次回收后,客户端的状态时TIME_WAIT,它在等上面?

第四次回收 涉及到服务器到CLOSE的关闭状态的转换,因此需要保证其正确执行

TIME_WAIT等待是有时间时间限制的,在规定时间内,发现服务器没有重发FIN(即二次挥手),就意味着,ACK(第四次挥手)已经成功被服务器收到,客户端也会变为CLOSE状态

如果在规定时间内,客户端又收到了(第三次挥手),表明出现了问题,此时客户端会再次四次挥手,然后重复等待。

2、补充问题(技术面常问问题

(1)为什么要四次挥手,而不能向三次握手一样,在第二次握手中一起将ACK和SYN一起发送给客户端?

因为第三次挥手的触发条件是,等待服务器调用close(已连接套接字),有一个等待的过程,因此不能和ACK一起发送。

(2)客户端都已经调用了close为什么还能执行 四次挥手的操作?

因为close只是关闭客户端中的套接字,此时是半关闭状态,仅关闭这个套接字应用层的数据的收发,而将FIN,ACK这些标志位置1的操作,是在底层实现的,并不是close负责的,因此可以执行四次挥手的操作。

5、状态转换

在图中大家可以看到有很多状态,下面介绍一下这些状态都是什么

下面是状态转换流程图

介绍

红色是服务器的状态转换图,蓝色是客户端的状态转换图

结束

代码重在练习!

代码重在练习!

代码重在练习!

今天的分享就到此结束了,希望对你有所帮助,如果你喜欢我的分享,请点赞收藏夹关注,谢谢大家!!!

相关文章:

  • 第七周作业
  • Neovim插件深度解析:mcphub.nvim如何用MCP协议重构开发体验
  • 数字孪生赋能管理系统,降本增效立竿见影
  • Manus技术架构、实现内幕及分布式智能体项目实战
  • 海量聊天数据处理:基于Spring Boot与SharingJDBC的分库分表策略及ClickHouse冷热数据分离
  • 微服务与事件驱动架构(EDA)
  • 每天五分钟深度学习PyTorch:0填充函数在搭建神经网络中的应用
  • 13.第二阶段x64游戏实战-分析人物等级和升级经验
  • Cocos Creater打包安卓App添加隐私弹窗详细步骤+常见问题处理
  • android测试依赖
  • 【论文阅读21】-PSOSVM-CNN-GRU-Attention-滑坡预测(2024-12)
  • ubuntu24.04上使用qemu+buildroot+uboot+linux+tftp+nfs模拟搭建vexpress-ca9嵌入式linux开发环境
  • FFMPEG-视频解码-支持rtsp|rtmp|音视频文件(低延迟)
  • 【Hot100】 73. 矩阵置零
  • Graham Scan算法求解二维凸包
  • 2024年网站开发语言选择指南:PHP/Java/Node.js/Python如何选型?
  • 从UDS协议学习ISO网络七层架构:汽车诊断网络协议的分层逻辑剖析
  • wordpress SMTP配置qq邮箱发送邮件,新版QQ邮箱授权码获取方法
  • WPF 点击按钮,显示隐藏另一个控件
  • MCP 应用案例-网络设备批量管理
  • 四川省委统战部副部长(正厅级)张荣履新峨眉电影集团“一把手”
  • 特朗普就防卫负担施压日本,石破茂:防卫费应由我们自主决定
  • 轻流科技薄智元:AI时代,打造“工业智造”需要“共生式进化”
  • 新消费观察 | 重点深耕,外资科技企业继续看好中国发展
  • 朝方谴责美国派遣战略轰炸机至朝鲜半岛
  • 新疆大学迎来新校长