【网络通信】TCP三次握手、四次挥手

news/2024/5/5 18:32:21

TCP(传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。在TCP/IP协议族中,TCP协议负责在两个网络节点之间建立可靠的连接,并保证数据包的顺序传输和数据的完整性。

 

1.TCP三次握手

TCP三次握手(Three-way Handshake)是TCP/IP协议中用于建立TCP连接的过程。这个过程确保了两个通信节点的初始化序列号(ISN)是同步的,同时也交换了彼此的初始窗口大小等参数。三次握手的过程如下:

  1. 第一次握手(SYN)

    • 客户端发送一个SYN(同步序列编号)标志的TCP段,以便开始一个新的连接。这个段中包含客户端的初始序列号(client_isn)。
  2. 第二次握手(SYN-ACK)

    • 服务器接收到客户端的SYN请求后,需要确认客户端的SYN,同时自己也发送一个SYN请求。服务器在响应中包含它自己的初始序列号(server_isn),并将确认序号设置为客户端的初始序列号加1,即client_isn + 1
  3. 第三次握手(ACK)

    • 客户端收到服务器的SYN-ACK响应后,再次发送一个确认响应,这个响应中包含确认序号,设置为服务器的初始序列号加1,即server_isn + 1

完成三次握手后,TCP连接就建立成功了,接下来就可以开始数据的传输了。

这个握手过程保证了双方都知道对方已经准备好接收和发送数据,同时也防止了因网络中遗留的、失效的连接请求而错误地建立连接。

在代码中实现TCP三次握手通常不需要手动编写底层的网络通信代码,因为TCP三次握手是由操作系统和网络协议栈自动完成的。当你使用高级编程语言(如Python、Java、C++等)中的套接字(Socket)库时,底层的TCP握手过程是由操作系统负责的。

在C语言中,使用套接字API(通常称为BSD套接字API)可以实现TCP客户端和服务器。以下是一个简单的例子,展示了如何在C语言中实现TCP服务器和客户端。

TCP服务器 (server.c):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>int main() {int server_fd, new_socket;struct sockaddr_in address;int opt = 1;int addrlen = sizeof(address);char buffer[1024] = {0};const char* hello = "Hello from server";// 创建socket文件描述符if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {perror("socket failed");exit(EXIT_FAILURE);}// 绑定socket到地址if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {perror("setsockopt");exit(EXIT_FAILURE);}address.sin_family = AF_INET;address.sin_addr.s_addr = INADDR_ANY;address.sin_port = htons(8080);if (bind(server_fd, (struct sockaddr*)&address, sizeof(address)) < 0) {perror("bind failed");exit(EXIT_FAILURE);}// 监听是否有客户端连接if (listen(server_fd, 3) < 0) {perror("listen");exit(EXIT_FAILURE);}// 接受客户端连接if ((new_socket = accept(server_fd, (struct sockaddr*)&address, (socklen_t*)&addrlen)) < 0) {perror("accept");exit(EXIT_FAILURE);}// 读取数据read(new_socket, buffer, 1024);printf("Message from client: %s\n", buffer);// 发送数据send(new_socket, hello, strlen(hello), 0);printf("Hello message sent\n");// 关闭连接close(new_socket);close(server_fd);return 0;
}

TCP客户端 (client.c):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>int main() {int sock = 0;struct sockaddr_in serv_addr;const char* hello = "Hello from client";char buffer[1024] = {0};// 创建socket文件描述符if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {printf("\n Socket creation error \n");return -1;}serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(8080);// 将IPv4地址从文本转换为二进制形式if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {printf("\nInvalid address/ Address not supported \n");return -1;}// 连接到服务器if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {printf("\nConnection Failed \n");return -1;}// 发送数据send(sock, hello, strlen(hello), 0);printf("Hello message sent\n");// 读取数据read(sock, buffer, 1024);printf("Message from server: %s\n", buffer);// 关闭连接close(sock);return 0;
}

在上述代码中,服务器和客户端都使用套接字API来创建TCP连接。服务器首先创建一个套接字并绑定到指定的地址和端口,然后监听是否有客户端连接。客户端创建一个套接字并连接到服务器的地址和端口。连接成功后,双方可以通过套接字发送和接收数据。

要编译和运行这些程序,你需要有一个C编译器,如GCC。可以使用以下命令编译服务器和客户端:gcc -o server server.c    gcc -o client client.c

然后,首先运行服务器:./server

在另一个终端中运行客户端:./client

客户端和服务器将通过TCP三次握手建立连接,然后客户端发送一个消息给服务器,服务器回复一个消息给客户端,最后双方关闭连接。

 

2.TCP四次挥手

TCP四次挥手(Four-way Handshake)是TCP/IP协议中用于终止一个TCP连接的过程。当一个连接的双方完成数据传输后,它们需要通过四次挥手来优雅地关闭连接。这个过程确保了所有在传输中的数据都被正确地处理,并且双方都同意断开连接。

四次挥手的过程如下:

  1. 第一次挥手(FIN)

    • 连接的一端(假设是客户端)发送一个FIN(结束)标志的TCP段,表示它已经完成发送数据,并希望关闭到另一端(服务器)的连接。
  2. 第二次挥手(ACK)

    • 服务器接收到这个FIN请求后,发送一个ACK(确认)响应,确认序号为收到FIN的序号加1。此时,服务器仍然可以发送数据,但客户端不再发送数据。
  3. 第三次挥手(FIN)

    • 一段时间后,服务器也完成了数据发送,发送一个FIN段给客户端,请求关闭连接。
  4. 第四次挥手(ACK)

    • 客户端收到服务器的FIN请求后,发送一个ACK响应,确认序号为收到服务器FIN的序号加1。在发送完这个ACK后,客户端会等待足够长的时间(通常是2倍的最大段生命周期MSL),以确保服务器收到确认包。

完成四次挥手后,TCP连接就正式关闭了。在这个过程中,每个方向的连接都是独立关闭的,这就是为什么需要四个步骤来完全终止连接。

需要注意的是,在四次挥手过程中,主动关闭连接的一端(如客户端)在发送完最后一个ACK后进入TIME_WAIT状态,而不是立即关闭连接。这是为了确保对方能够收到最后的ACK确认包,如果对方没有收到,会重新发送FIN请求。TIME_WAIT状态持续的时间通常是2倍的最大段生命周期MSL,这是一个保守的等待时间,确保在网络中遗留的TCP段都已经过期。

 

TCP连接中如何处理同时的双向关闭?

在TCP连接中,如果双方同时发送FIN包来关闭连接,这种情况被称为同时关闭(Simultaneous Close)。这种情况下,四次挥手的过程会稍微有所不同,但基本的原理是相同的:确保双方都同意关闭连接,并且所有在传输中的数据都被正确地处理。

同时关闭的过程如下:

  1. 双方同时发送FIN包

    • 连接的双方都发送一个FIN包给对方,表示自己已经完成了数据的发送,并希望关闭连接。
  2. 双方同时接收对方的FIN包,并回应ACK

    • 每一方收到对方的FIN包后,发送一个ACK包作为响应。这时,每个方向上的数据传输都已经关闭,但是双方都还在等待对方的最终确认。
  3. 双方再次发送ACK包

    • 在接收到对方的ACK包后,每一方都再次发送一个ACK包作为最终确认。这个ACK包的目的是确认对方的FIN包,以及告知对方自己的ACK包已经被接收。
  4. 双方关闭连接

    • 在发送完最后一个ACK包后,双方都可以关闭连接。在关闭连接之前,每一方都会等待足够长的时间(通常是2倍的最大段生命周期MSL),以确保对方能够收到最后的ACK包。

在同时关闭的情况下,连接的双方都会进入TIME_WAIT状态,以确保所有的TCP段都已经过期,从而避免了旧的连接信息干扰新的连接。TIME_WAIT状态对于防止网络中的旧连接信息(例如延迟的TCP段)对后续连接造成影响是非常重要的。

在实际的网络编程中,同时关闭的情况并不常见,因为通常一方(如客户端)会主动发起关闭,而另一方(如服务器)则会响应这个关闭请求。然而,了解同时关闭的处理方式对于理解TCP协议的完整性和健壮性是很有帮助的。

 

在四次挥手过程中,如果网络出现问题,应该如何处理?

在TCP四次挥手过程中,如果网络出现问题,TCP协议提供了多种机制来处理这些情况,确保连接能够可靠地关闭。以下是一些可能的问题及其处理方式:

  1. 丢失FIN包

    • 如果一方发送的FIN包在网络上丢失,发送方会等待接收方的ACK确认。如果在一定时间内没有收到确认,发送方会重新发送FIN包。
  2. 丢失ACK包

    • 如果一方发送的ACK包在网络上丢失,接收方会等待对方的下一个TCP包。如果对方也处于等待状态,那么双方可能会陷入死锁。为了解决这个问题,TCP使用了一个超时重传机制,发送方在等待ACK超时后会重新发送FIN包。
  3. 连接中的一方崩溃

    • 如果连接中的一方崩溃,另一方将不会收到预期的ACK包。在这种情况下,发送方会重传FIN包,但由于对方已经崩溃,重传不会得到响应。最终,发送方会认为对方已经关闭,并在超时后关闭自己的连接。
  4. 网络分区

    • 如果网络分区导致双方无法通信,每一方都可能认为对方已经崩溃。在这种情况下,每一方都可能开始关闭连接的过程,但由于无法通信,四次挥手无法完成。当网络恢复时,可能会出现重复的FIN包和ACK包,但TCP协议能够处理这种情况,确保连接最终能够正确关闭。
  5. 延迟的TCP段

    • 在连接关闭后,如果网络中仍然有延迟的TCP段,这些段可能会干扰后续的连接。为了防止这种情况,TCP在关闭连接后进入TIME_WAIT状态,等待足够长的时间以确保所有的TCP段都已经过期。

TCP协议的设计考虑了网络的不可靠性,并通过上述机制来确保连接的可靠性。这些机制包括超时重传、序列号和确认号的使用、以及TIME_WAIT状态等。因此,即使在网络出现问题的情况下,TCP仍然能够尽可能地保证连接的正确关闭。

 

在TCP连接关闭过程中,如何处理并发的多个连接请求?

在TCP连接关闭过程中,服务器通常需要处理并发的多个连接请求。这是通过多线程或多进程技术来实现的,允许服务器同时处理多个客户端的连接和关闭请求。以下是一些处理并发连接请求的方法:

  1. 多线程

    • 服务器可以为每个新的连接创建一个线程。当客户端连接到服务器时,服务器接受连接并创建一个新的线程来处理该连接。这样,每个连接都有自己的线程,可以在独立的上下文中进行通信和关闭。
  2. 多进程

    • 类似于多线程,服务器可以为每个连接创建一个新的进程。这种方法在操作系统级别提供了隔离,但相对于多线程,进程之间的通信开销更大。
  3. 非阻塞IO和事件驱动

    • 使用非阻塞IO和事件驱动的编程模型,如使用epoll(Linux)、kqueue(BSD)、IOCP(Windows)等,可以允许服务器在一个线程或进程中处理多个连接。这种模型通过在事件发生时(如数据到达、连接建立或关闭)进行回调,从而实现高效的并发处理。
  4. 线程池或进程池

    • 为了避免频繁创建和销毁线程或进程的开销,服务器可以使用线程池或进程池。预先创建一定数量的线程或进程,然后将它们分配给新的连接。这种方法可以平衡资源使用和性能。
  5. 异步IO

    • 异步IO允许服务器发起IO操作(如接收数据、发送数据、接受连接等)而无需等待操作完成。这样,服务器可以在单个线程或进程中处理多个连接,而不会因为等待IO操作而阻塞。

在处理并发的多个连接请求时,服务器还需要维护连接的状态,确保每个连接的正确关闭。这通常涉及到连接池的管理、连接超时的处理、以及异常情况下的资源清理等。

总之,处理并发的多个连接请求需要服务器具备高效的网络IO模型和资源管理策略。选择合适的方法取决于服务器的具体需求、性能目标以及运行环境。

 

结语

以上就是TCP三次握手,四次挥手的知识,本次分享到此结束。

最后的最后,还请大家点点赞,点点关注,谢谢大家!

 


http://www.mrgr.cn/p/67427865

相关文章

Redis系列5:深入分析Cluster 集群模式

1 背景 前面我们学习了Redis高可用的两种架构模式&#xff1a;主从模式、哨兵模式。 解决了我们在Redis实例发生故障时&#xff0c;具备主从自动切换、故障转移的能力&#xff0c;终保证服务的高可用。 但是这些其实远远不够&#xff0c;随着我们业务规模的不断扩展&#xff0…

Anaconda中安装pyecharts

学习Python的过程中发现conda无法使用pyecharts https://pypi.org/project/pyecharts/#files 下载后将whl文件复制到到本地conda\scripts目录下 随后打开conda 切换到script目录下,输入命令 pip install pyecharts-2.0.5-py3-none-any.whl 等待安装完成 在pycharm中配置Python…

【ZYNQ】zynq启动模式及程序固化

一、前言 由于zynq含有arm cpu ,其启动模式由ps主导&#xff0c;与纯逻辑的fpga不相同&#xff0c;此处做一个记录。 二、zynq启动模式 关于zynq的启动模式详细内容可以参考官方文档&#xff1a;ug585-Zynq 7000 SoC Technical Reference Manual&#xff0c;第六章。 2.1 启…

网络拓扑—FTP服务搭建

均使用Windows Server 2003进行搭建目录FTP服务搭建网络拓扑配置网络FTPPC安装FTP服务配置FTP服务FTP用户配置—1PC机访问FTP站点IE浏览器访问终端访问FTP用户配置—2PC机访问ftp站点IE浏览器访问终端访问 FTP服务搭建 网络拓扑//交换机忽略不计 FTP服务IP:192.168.1.1 PC机IP…

【研发管理】产品经理知识体系-产品创新中的市场调研

导读&#xff1a;在产品创新过程中&#xff0c;市场调研的重要性不言而喻。它不仅是产品创新的起点&#xff0c;也是确保产品成功推向市场的关键步骤。对于产品经理系统学习和掌握产品创新中的市场调研相关知识体系十分重要。 目录 概述&#xff1a;市场调研重要性 1、相关概…

10:00面试,10:08就出来了,问的问题有点变态。。。

从小厂出来&#xff0c;没想到在另一家公司又寄了。 到这家公司开始上班&#xff0c;加班是每天必不可少的&#xff0c;看在钱给的比较多的份上&#xff0c;就不太计较了。没想到8月一纸通知&#xff0c;所有人不准加班&#xff0c;加班费不仅没有了&#xff0c;薪资还要降40%…

【Java--数据结构】链表经典OJ题详解(上)

欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗~ 如有错误&#xff0c;欢迎指出~ 目录 谈谈头插、头删、尾插、头插的时间复杂度 反转一个单链表 链表的中间结点 返回倒数第k个结点 合并两个链表 谈谈头插、头删、尾插、头插的时间复杂度 头插和头删的时…

顺序栈--代码题

数据结构 顺序栈代码题设计一个进制转换程序,使用顺序栈设计一个把十进制转化为十六进制的接口,实现当键盘输入一个非负的十进制时,可以在终端输出对应的十六进制数。 /***************************************************************************************** file n…

完美运营版商城/拼团/团购/秒杀/积分/砍价/实物商品/虚拟商品等全功能商城

源码下载地址&#xff1a;完美运营版商城.zip 后台可以自由拖曳修改前端UI页面 还支持虚拟商品自动发货等功能 挺不错的一套源码 前端UNIAPP 后端PHP 一键部署版本

aws安装jenkins步骤

一、aws安装jdk11 1.1 aws安装jdk11 1、切换root,更新yum, sudo su yum update exist 2、安装JDK1.8版本 yum -y list java-1.8.0* #(安装jdk11,yum -y list java-11*) yum install java-1.8.0-openjdk-devel.x86_64 #(安装jdk11,yum -y list java-11-openjdk-deve…

嵌入式4-24

作业&#xff1a; 整理思维导图 定义一个矩形类Rec&#xff0c;包含私有属性length&#xff0c;width&#xff0c;有以下成员函数&#xff1a; void set_length(int l); //设置长度 void set_width(int w); //设置宽度 int get_length(); //获取长度 int get_width(); //获取宽…

【GIS教程】ArcGIS做日照分析(附练习数据下载)

我国对住宅日照标准的规定是:冬至日住宅底层日照不少于1小时或大寒日住宅层日照不少于2小时(通常以当地冬至日正午12时的太阳高度角作为依据)。因冬至日太阳高度角最低&#xff0c;照射范围最小&#xff0c;如果冬至日12&#xff1a;00建筑物底层能够接收到阳光&#xff0c;那么…

Golang | Leetcode Golang题解之第42题接雨水

题目&#xff1a; 题解: func trap(height []int) (ans int) {n : len(height)if n 0 {return}leftMax : make([]int, n)leftMax[0] height[0]for i : 1; i < n; i {leftMax[i] max(leftMax[i-1], height[i])}rightMax : make([]int, n)rightMax[n-1] height[n-1]for i…

详解MySQL C API 相关接口(大白话就是:MySQL的c语言怎么写)

文章目录 1、C API 官方文档2、初始化 MYSQL3、连接 MySQL设置连接字符集&#xff08;使得客户端编码方式匹配&#xff09; 4、下发 mysql 指令5、获取 mysql 查询结果(保存起来)获取行与列遍历存储结果 6、释放 MYSQL\_RES 对象7、关闭 MySQL 连接8、总结 1、C API 官方文档 …

vue3 删除对象中的属性,可以使用js里的delete,但需注意ts定义对象类型!

如上如&#xff0c;当使用delete 删除stateData中的属性时&#xff0c; 报错&#xff0c;意思为 TypeScript 错误“‘delete’ 运算符的操作数必须是可选的 什么原因呢&#xff1f;是因为我偷懒 缺少了ts定义类型 方法一&#xff1a; &#xff08;不推荐&#xff09; delete …

嵌入式笔记4.1 GPIO 功能复用

目录一、了解 MCU(GPIO)具有的所有复用功能通过查看 MCU 的数据手册可以知道 MCU 的所有引脚的功能:例 STM32L431:例 stm32f103:复用、重映射、多路复用(多功能引脚)GPIO复用(AF - Alternate Function)重映射(Remapping)多路复用(Multi-function)常见引脚功能一览…

【八股】Redisson分布式锁

Redisson分布式锁 主要了解了Redisson分布式锁实现的三个功能&#xff1a; 1.可重入 -> 防止死锁 2.可重试&#xff08;i.e. 非阻塞获取锁&#xff09; 3.自动续约 1. 可重入 原理&#xff1a; 利用Redis的Hash结构&#xff0c;记录了使用当前锁的线程id和重用次数&#…

如何分析和优化慢sql语句

前言 sql查询速度比较慢容易成为性能瓶颈,这时我们可以优化我们的sql语句或数据库表 一般sql语句执行很慢的种类分为: 1.聚合查询 2.多表查询 3.表数据量过大查询 4.深度分页查询 这四种的前三种都可以通过优化sql语句来优化sql查询速度 正文 聚合查询 我们可以通过尝…

初始C++

1. C关键字(C98) C总计63个关键字&#xff0c; C语言32个关键字 ps&#xff1a;下面我们只是看一下C有多少关键字&#xff0c;不对关键字进行具体的讲解。后面我们学到以后再 细讲。 2. 命名空间 在C/C中&#xff0c;变量、函数和后面要学到的类都是大量存在的&#xff0c;…

ps/lr如何为一个型号相机的raw使用其他相机的预设

首先单独下载camera raw,进到C:\ProgramData\Adobe\CameraRaw\CameraProfiles\Camera中获取想要的相机型号的预设dcp文件 去 https://liquidtelecom.dl.sourceforge.net/project/dcptool/dcptool/dcpTool V1.11.0/dcpTool_1_11_0.zip?viasf=1 下载dcp编译工具dcpTool cd C:\U…