TCP 三次握手和四次挥手

devtools/2024/11/23 12:12:58/

一、三次握手 (Three-Way Handshake)

概念
三次握手用于在 TCP 连接建立时,确保客户端和服务器之间的通信信道可靠,并同步双方的初始序列号。

三次握手过程
  1. 第一次握手:客户端向服务器发送 SYN (synchronize) 报文,表示请求建立连接,并携带一个初始序列号 Seq = x

    • 客户端状态:SYN_SENT
  2. 第二次握手:服务器收到 SYN 报文后,回复一个 SYN + ACK 报文,表示同意建立连接,并携带自己的初始序列号 Seq = y 和确认号 Ack = x+1

    • 服务器状态:SYN_RCVD
  3. 第三次握手:客户端收到 SYN + ACK 报文后,回复一个 ACK 报文,确认序列号为 Ack = y+1,并正式建立连接。

    • 客户端状态:ESTABLISHED
    • 服务器状态:ESTABLISHED
举例

假设客户端与服务器建立一个连接:

  1. 客户端服务器:
    客户端发送 SYN (Seq=100),请求建立连接。

  2. 服务器客户端:
    服务器回复 SYN+ACK (Seq=300, Ack=101),同意连接。

  3. 客户端服务器:
    客户端回复 ACK (Seq=101, Ack=301),连接建立成功。

 

二、四次挥手 (Four-Way Handshake)

概念
四次挥手用于在 TCP 连接断开时,确保双方都能完全关闭通信,释放连接资源。

四次挥手过程
  1. 第一次挥手:客户端发送 FIN 报文,表示不再发送数据,但可以接收数据。

    • 客户端状态:FIN_WAIT_1
  2. 第二次挥手:服务器收到 FIN 报文后,回复一个 ACK 报文,表示已收到。

    • 服务器状态:CLOSE_WAIT
    • 客户端状态:FIN_WAIT_2
  3. 第三次挥手:服务器发送 FIN 报文,表示不再发送数据。

    • 服务器状态:LAST_ACK
  4. 第四次挥手:客户端收到 FIN 报文后,回复 ACK 报文,表示已确认,进入 TIME_WAIT 状态,等待一段时间后完全关闭连接。

    • 客户端状态:TIME_WAITCLOSED
    • 服务器状态:CLOSED

举例

假设客户端与服务器断开连接:

  1. 客户端服务器:
    客户端发送 FIN (Seq=200),请求关闭发送方向的连接。

  2. 服务器客户端:
    服务器回复 ACK (Seq=300, Ack=201),确认收到关闭请求。

  3. 服务器客户端:
    服务器发送 FIN (Seq=300),请求关闭发送方向的连接。

  4. 客户端服务器:
    客户端回复 ACK (Seq=201, Ack=301),确认收到关闭请求,进入 TIME_WAIT 状态,稍后关闭。


三、三次握手和四次挥手的关键点

比较点三次握手四次挥手
阶段连接建立连接断开
报文数量3 次4 次
参与方客户端和服务器客户端和服务器
目的确认通信双方的可靠性和同步序列号确保双方都完全关闭连接
状态客户端从 SYN_SENTESTABLISHED客户端进入 TIME_WAIT,服务器直接关闭

四、注意事项

  1. 为什么三次握手而不是两次?

    • 两次握手无法确保客户端和服务器的序列号都能正确同步,可能导致连接不可靠。
    • 三次握手能有效防止服务器处理已经过时的连接请求(如延迟的报文重发)。
  2. 为什么四次挥手而不是三次?

    • TCP 是全双工通信协议,连接的关闭需要双方各自发送 FIN,因此需要四次报文交互。
  3. TIME_WAIT 状态的作用

    • 确保延迟的 TCP 报文能被正确处理,避免影响后续连接。

 

五、TCP 三次握手和四次挥手代码示例

服务器端代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>#define PORT 8080                // 定义服务器监听的端口号
#define BUFFER_SIZE 1024         // 定义缓冲区大小int main() {int server_fd, new_socket;   // 文件描述符:`server_fd`用于监听,`new_socket`用于与客户端通信struct sockaddr_in address; // 服务器地址结构int opt = 1;                // 用于设置 socket 选项int addrlen = sizeof(address);char buffer[BUFFER_SIZE] = {0}; // 缓冲区,用于存储客户端发来的数据// 创建服务器套接字if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {perror("Socket failed");  // 如果创建失败,打印错误信息并退出exit(EXIT_FAILURE);}// 设置套接字选项,允许端口复用if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {perror("setsockopt");exit(EXIT_FAILURE);}// 初始化服务器地址信息address.sin_family = AF_INET;          // 设置地址族为 IPv4address.sin_addr.s_addr = INADDR_ANY;  // 接受所有 IP 地址address.sin_port = htons(PORT);        // 设置端口号,使用网络字节序// 绑定套接字到指定的 IP 和端口if (bind(server_fd, (struct sockaddr*)&address, sizeof(address)) < 0) {perror("Bind failed");  // 如果绑定失败,打印错误信息并退出exit(EXIT_FAILURE);}// 启动监听模式,允许最大连接数为 3if (listen(server_fd, 3) < 0) {perror("Listen");exit(EXIT_FAILURE);}printf("Server listening on port %d...\n", PORT);// 等待客户端连接(阻塞状态),返回一个新的套接字用于通信if ((new_socket = accept(server_fd, (struct sockaddr*)&address, (socklen_t*)&addrlen)) < 0) {perror("Accept");exit(EXIT_FAILURE);}printf("Connection established with client.\n");// 从客户端接收数据int valread = read(new_socket, buffer, BUFFER_SIZE);printf("Received from client: %s\n", buffer);// 向客户端发送数据char* hello = "Hello from server!";send(new_socket, hello, strlen(hello), 0);printf("Message sent to client.\n");// 模拟四次挥手,关闭套接字连接printf("Server: Closing connection.\n");close(new_socket);  // 关闭与客户端的通信套接字close(server_fd);   // 关闭监听套接字printf("Server closed.\n");return 0;
}
客户端代码

 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>#define PORT 8080                // 定义服务器端口号
#define BUFFER_SIZE 1024         // 定义缓冲区大小int main() {int sock = 0;                // 客户端套接字文件描述符struct sockaddr_in serv_addr; // 服务器地址结构char* hello = "Hello from client!";  // 要发送给服务器的消息char buffer[BUFFER_SIZE] = {0};      // 缓冲区,用于接收服务器的消息// 创建客户端套接字if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {printf("Socket creation error\n"); // 如果创建失败,打印错误信息并退出return -1;}// 初始化服务器地址信息serv_addr.sin_family = AF_INET;        // 设置地址族为 IPv4serv_addr.sin_port = htons(PORT);      // 设置端口号,使用网络字节序// 将服务器的 IP 地址从文本转换为二进制形式if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {printf("Invalid address or Address not supported\n"); // 如果转换失败,打印错误信息并退出return -1;}// 连接服务器(此时完成三次握手)if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {printf("Connection failed\n"); // 如果连接失败,打印错误信息并退出return -1;}printf("Connected to server.\n");// 发送消息到服务器send(sock, hello, strlen(hello), 0);printf("Message sent to server: %s\n", hello);// 接收来自服务器的回复int valread = read(sock, buffer, BUFFER_SIZE);printf("Received from server: %s\n", buffer);// 模拟四次挥手,关闭套接字连接printf("Client: Closing connection.\n");close(sock);  // 关闭客户端套接字printf("Client closed.\n");return 0;
}

关键点

  1. 三次握手

    • 客户端调用 connect() 时,完成了 SYNACK 报文的交互。
    • 服务器调用 accept() 时,确认连接建立。
  2. 四次挥手

    • 客户端调用 close() 发出 FIN,服务器回复 ACK
    • 服务器随后调用 close() 发出 FIN,客户端回复 ACK,关闭连接。

 


http://www.ppmy.cn/devtools/136288.html

相关文章

yolo基础---Labelimg工具安装与使用

前言 今后的一段时间&#xff0c;会慢慢更新yolo的使用&#xff0c;基础版本以yolov5为主&#xff0c;选yolov5主要是这个很经典&#xff0c;比较适合入门&#xff0c;后面会重点更新yolov10的使用与创新 文章目录 1、安装1、labelimg工具介绍2、labelimg工具下载3、安装 2、使…

Kafka 2.8 源码导读

Kafka 是一个分布式流处理平台&#xff0c;广泛用于实时数据流的处理和传输。Kafka 2.8 版本引入了一些新特性和改进。 以下是 Kafka 2.8 源码的导读&#xff0c;帮助你理解其核心组件和实现机制。 1. 源码结构 Kafka 的源码主要分布在以下几个模块中&#xff1a; clients/…

前端三剑客(三):JavaScript

目录 前言 1. JavaScript 简介 2. 前置知识 2.1 JS 第一个程序 2.2 JS 的引入方式 3. JS 基础语法 3.1 变量 3.1.1 命名规则 3.2 数据类型 3.3 运算符 3.4 数组 3.4.1 创建数组 3.4.2 访问数组元素 3.4.3 添加数组元素 3.4.4 修改数组元素 3.4.5 删除数组元素 …

排序算法(四)--快速排序

文章目录 引言快速排序的基本原理C语言实现快速排序代码解析 快速排序 C语言实例 引言 快速排序&#xff08;Quicksort&#xff09;作为一种高效的排序算法&#xff0c;以其平均时间复杂度为O(n log n)而著称。 快速排序的基本原理 快速排序的核心思想是分治法&#xff08;D…

直播服务器多设备同显方案

在直播行业中&#xff0c;服务器多设备同显方案已成为一种创新且高效的技术应用。这一技术不仅能够满足大规模同步直播的需求&#xff0c;还能显著提升观众的观看体验和参与度。本文将深入探讨直播服务器多设备同显方案的技术细节、实施步骤以及在不同场景下的应用价值。 直播服…

基于无线传感器网络的无线温湿度采集系统(附详细使用教程+完整代码+原理图+完整课设报告)

&#x1f38a;项目专栏&#xff1a;【Zigbee课程设计系列文章】&#xff08;附详细使用教程完整代码原理图完整课设报告&#xff09; 前言 &#x1f451;由于无线传感器网络&#xff08;也即是Zigbee&#xff09;作为&#x1f310;物联网工程的一门必修专业课&#xff0c;具有…

LRU缓存

什么是LRU缓存? LRU&#xff08;Least Recently Used&#xff09;是最近最少使用算法&#xff0c;是操作系统中用于分页置换的算法&#xff0c;如果要向内存中添加分页&#xff0c;并且内存分页已满的情况下&#xff0c;就选出最近一段时间最不常用的分页进行置换&#xff08;…

七次课掌握 Photoshop:绘画与修饰

Photoshop 提供了丰富的绘画和修饰工具&#xff0c;帮助我们在数字图像中进行创作和润色。熟练掌握这些工具和方法&#xff0c;可以提升我们的图像处理和设计水平。 一、绘画类工具 1、画笔工具 快捷键&#xff1a;B 画笔工具 Brush Tool是 Photoshop 中最基本的绘画工具&#…