Java网络编程,使用UDP实现TCP(三), 基本实现四次挥手
简介
四次挥手示意图

- 在四次挥手过程中,第一次挥手中的Seq为本次挥手的ISN, ACK为 上一次挥手的 Seq+1,即最后一次数据传输的Seq+1。
- 挥手信息由客户端首先发起。
实现步骤:
下面是TCP四次挥手的步骤:
-
第一次挥手(FIN):主动关闭方发送一个带有FIN(Finish)标志的TCP报文段给被动关闭方,表示主动关闭方已经没有数据要发送了。
-
第二次挥手(ACK):被动关闭方接收到第一次挥手的TCP报文段后,发送一个带有ACK(Acknowledgment)和确认序号的TCP报文段作为响应,表示已经收到了关闭请求。
-
第三次挥手(FIN):被动关闭方发送一个带有FIN标志的TCP报文段给主动关闭方,表示被动关闭方也没有数据要发送了。
-
第四次挥手(ACK):主动关闭方接收到第三次挥手的TCP报文段后,发送一个带有ACK和确认序号的TCP报文段作为响应,表示已经收到了关闭请求。
在完成这四次挥手之后,TCP连接就正式关闭了。需要注意的是,每一次挥手都需要对方的确认才能进行下一步操作,这样可以确保双方都知道连接已经关闭,并且保证数据的完整性和可靠性。
这个过程可以简化为以下步骤:
- 主动关闭方发送FIN报文段。
- 被动关闭方接收到FIN后,发送ACK报文段作为确认。
- 被动关闭方发送FIN报文段。
- 主动关闭方接收到FIN后,发送ACK报文段作为确认。
这样,TCP连接就完成了关闭过程。
修改说明:
我将客户端的发送消息和服务端的接收消息做了一些简单的封装:
客户端:
public void sendMsg(String dataMsg, DatagramSocket datagramSocket) throws IOException {byte[] bytes = dataMsg.getBytes();DatagramPacket datagramPacketMsg = new DatagramPacket(bytes, 0,bytes.length, new InetSocketAddress("localhost",9999));datagramSocket.send(datagramPacketMsg);}
服务端:
//接收数据public String receive(DatagramSocket datagramSocket, int time1, int time2) throws IOException {//设置超时时间datagramSocket.setSoTimeout(time1);//创建数据包,用于接收数据byte[] bytes3 = new byte[1024];DatagramPacket datagramPacket3 = new DatagramPacket(bytes3, bytes3.length);datagramSocket.receive(datagramPacket3);//停止计时器datagramSocket.setSoTimeout(time2);String s3 = new String(datagramPacket3.getData(), 0, datagramPacket3.getLength());return s3;}
第一次挥手
客户端发送关闭请求:
//四次挥手,关闭连接System.out.println("====================");System.out.println("四次挥手:");System.out.println("第一次挥手: 客户端 -> 服务端");System.out.println("数据发送...");connectionMarks.setFinMark(2);String finMark = String.valueOf(connectionMarks.getFinMark());connectionMarks.setACKMark(1);String ACKFin = String.valueOf(connectionMarks.getACKMark());String SeqFin = String.valueOf(connectionMarks.getSeq());String ACKS1 = String.valueOf(Integer.parseInt(SeqD1) + 1);String dataF1 = finMark + "/" + ACKFin + " " + SeqFin + " " + ACKS1;clientMsg.sendMsg(dataF1, datagramSocket);
服务端接收数据:
//四次握手//第一次System.out.println("====================");String receiveB1 = serverMsg.receive(datagramSocket, 0, 0);System.out.println("接收到的数据段为:" + receiveB1);String[] s1 = receiveB1.split(" ");String[] splitS1 = s1[0].split("/");if (!(splitS1[0].equals("2")|| splitS1[1].equals("1")|| s1[2].equals(String.valueOf(Integer.parseInt(SeqD1) + 1)))){throw new WrongConnectionException("非本次连接");}
第二次挥手
服务端发送第一次挥手的ACK
//第二次System.out.println("====================");System.out.println("服务端 -> 客户端");System.out.println("数据发送...");String SeqB2 = s1[2];String ACKB2 = String.valueOf(Integer.parseInt(s1[1]) + 1);connectionMarks.setACKMark(1);String ackMarkB = String.valueOf(connectionMarks.getACKMark());String dataMsgB2 = ackMarkB+ " " + SeqB2 + " " + ACKB2;byte[] datasB2 = dataMsgB2.getBytes();DatagramPacket datagramPacketB2 = new DatagramPacket(datasB2, 0,datasB2.length, new InetSocketAddress("localhost",8888));//调用对象发送数据datagramSocket.send(datagramPacketB2);
客户端接收
System.out.println("====================");System.out.println("开始接收数据段...");byte[] bytesB2 = new byte[1024];DatagramPacket datagramPacketB2 = new DatagramPacket(bytesB2, bytesB2.length);datagramSocket.receive(datagramPacketB2);String receiveMsgB2 = new String(datagramPacketB2.getData(), 0, datagramPacketB2.getLength());System.out.println("接收到的数据段为:" + receiveMsgB2);
第三次挥手
服务端发送请求关闭给客户端
System.out.println("====================");System.out.println("服务端 -> 客户端");System.out.println("数据发送...");String SeqB3 = SeqB2;String FinMark = splitS1[0];String ACKB3 = ACKB2;String dataMsgB3 = FinMark + "/" + ackMarkB+ " " + SeqB3 + " " + ACKB3;byte[] datasB3 = dataMsgB3.getBytes();DatagramPacket datagramPacketB3 = new DatagramPacket(datasB3, 0,datasB3.length, new InetSocketAddress("localhost",8888));//调用对象发送数据datagramSocket.send(datagramPacketB3);
客户端接收数据,需要校验,如果收到为关闭请求,则发送ACK给服务端
System.out.println("====================");System.out.println("开始接收数据段...");byte[] bytesB3 = new byte[1024];DatagramPacket datagramPacketB3 = new DatagramPacket(bytesB3, bytesB3.length);datagramSocket.receive(datagramPacketB3);String receiveMsgB3 = new String(datagramPacketB3.getData(), 0, datagramPacketB3.getLength());System.out.println("接收到的数据段为:" + receiveMsgB3);String[] splitB3 = receiveMsgB3.split(" ");String[] split2 = splitB3[0].split("/");if (!(split2[0].equals("2")|| split2[1].equals("1")||splitB3[1].equals(ACKS1)||splitB3[2].equals(String.valueOf(Integer.parseInt(SeqFin) + 1)))){throw new WrongConnectionException("非本次连接");}
第四次挥手
客户端接收并发送第三次挥手的ACK给服务端
System.out.println("====================");System.out.println("第四次挥手: 客户端 -> 服务端");System.out.println("数据发送...");String ackMark4 = ACKFin;String SeqB4 = SeqFin;String ACKB4 = String.valueOf(Integer.parseInt(ACKS1) + 1);String dataB4 = ackMark4 + " " + SeqB4 + " " + ACKB4;clientMsg.sendMsg(dataB4, datagramSocket);//关闭流datagramSocket.close();
客户端接收到ACK并且关闭
System.out.println("====================");String receiveB4 = serverMsg.receive(datagramSocket, 0, 0);System.out.println("接收到的数据段为:" + receiveB4);//关闭流datagramSocket.close();
完成总结:
- 基本完成了UDP实现TCP,但仍有欠缺。
- 实现过程中代码的复用过高,没有进行有效的方法封装。
- 代码不够成熟,还由一些不完善的地方,没有实现MTU机制。
- 对UDP和TCP,有了更深入的了解。
相关文章:
Java网络编程,使用UDP实现TCP(三), 基本实现四次挥手
简介 四次挥手示意图 在四次挥手过程中,第一次挥手中的Seq为本次挥手的ISN, ACK为 上一次挥手的 Seq1,即最后一次数据传输的Seq1。挥手信息由客户端首先发起。 实现步骤: 下面是TCP四次挥手的步骤: 第一次挥手&…...
“百里挑一”AI原生应用亮相,百度智能云千帆AI加速器首个Demo Day来了!
作者简介: 辭七七,目前大二,正在学习C/C,Java,Python等 作者主页: 七七的个人主页 文章收录专栏: 七七的闲谈 欢迎大家点赞 👍 收藏 ⭐ 加关注哦!💖…...
PyTorch深度学习实战(25)——自编码器
PyTorch深度学习实战(25)——自编码器 0. 前言1. 自编码器2. 使用 PyTorch 实现自编码器小结系列链接 0. 前言 自编码器 (Autoencoder) 是一种无监督学习的神经网络模型,用于数据的特征提取和降维,它由一个编码器 (Encoder) 和一…...
靠谱的车- 华为OD统一考试(C卷)
靠谱的车- 华为OD统一考试(C卷) OD统一考试(C卷) 分值: 100分 题解: Java / Python / C 题目描述 程序员小明打了一辆出租车去上班。出于职业敏感,他注意到这辆出租车的计费表有点问题…...
Apache Flink(十一):Flink集群部署-Standalone集群部署
🏡 个人主页:IT贫道_大数据OLAP体系技术栈,Apache Doris,Clickhouse 技术-CSDN博客 🚩 私聊博主:加入大数据技术讨论群聊,获取更多大数据资料。 🔔 博主个人B栈地址:豹哥教你大数据的个人空间-豹哥教你大数据个人主页-哔哩哔哩视频 目录 1. 节点划分...
vue的组件传值
Vue中组件之间的数据传递可以使用props和$emit来实现。 1.使用props传递数据:父组件可以通过子组件的props属性向子组件传递数据。 父组件中: <template><div><child-component :message"parentMessage"></child-comp…...
ue5材质预览界面ue 变黑
发现在5.2和5.1上都有这个bug 原因是开了ray tracing引起的,这个bug真是长时间存在,类似的bug还包括草地上奇怪的影子和地形上的影子等等 解决方法也很简单,就是关闭光追(不是…… 就是关闭预览,在材质界面preview sc…...
【SpringCloud篇】Eureka服务的基本配置和操作
文章目录 🌹简述Eureka🛸搭建Eureka服务⭐操作步骤⭐服务注册⭐服务发现 🌹简述Eureka Eureka是Netflix开源的一个基于REST的服务治理框架,主要用于实现微服务架构中的服务注册与发现。它由Eureka服务器和Eureka客户端组成&#…...
模拟目录管理 - 华为OD统一考试(C卷)
OD统一考试(C卷) 分值: 200分 题解: Java / Python / C++ 题目描述 实现一个模拟目录管理功能的软件,输入一个命令序列,输出最后一条命令运行结果。 支持命令: 1)创建目录命令: mkdir 目录名称,如mkdir abc为在当前目录创建abc目录,如果已存在同名目录则不执行任何操作…...
卷王开启验证码后无法登陆问题解决
问题描述 使用 docker 部署,后台设置开启验证,重启服务器之后,docker重启,再次访问系统,验证码获取失败,导致无法进行验证,也就无法登陆系统。 如果不了解卷王的,可以去官网看下。…...
【知识】如何区分图论中的点分割和边分割
转载请注明出处:小锋学长生活大爆炸[xfxuezhang.cn] 以下两个概念在现有中文博客下非常容易混淆: edge-cut(边切割) vertex-partition(点分割)vertex-cut(点切割) edge-partition(边分割) 实际上,初看中文时,真的会搞不清楚。但…...
【华为鸿蒙系统学习】- HarmonyOS4.0开发工具和环境配置问题总结|自学篇
🌈个人主页: Aileen_0v0 🔥热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 💫个人格言:"没有罗马,那就自己创造罗马~" 目录 官方链接 HUAWEI DevEco Studio和SDK下载和升级 | HarmonyOS开发者 安装教程 (…...
第78讲:MySQL数据库Binlog日志的核心概念与应用案例
文章目录 1.Binlog二进制日志的基本概念1.1.什么是Binlog二进制1.2.Binlog日志的三种记录格式1.3.Binlog日志中Event事件的概念 2.开启MySQL的Binlog二进制日志3.查看Binlog二进制日志中的Event事件信息3.1.查看当前数据库有那些Binlog日志3.2.产生一些DDL/DML语句3.3.观察Binl…...
MinGW编译Python至pyd踩坑整理
title: MinGW编译Python至pyd踩坑整理 tags: [Python,CC] categories: [开发记录,Python] date: 2023-12-12 13:48:20 description: sidebar: [‘toc’, ‘related’,‘recent’] 注意需要魔法 用scoop自动安装配置MinGw 需要魔法,不需要手动配置mingw scoop in…...
计算机毕业设计 基于SpringBoot的乡村政务办公系统的设计与实现 Java实战项目 附源码+文档+视频讲解
博主介绍:✌从事软件开发10年之余,专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ 🍅文末获取源码联系🍅 👇🏻 精…...
命令行参数(C语言)
目录 什么是命令行参数 main函数的可执行参数 不传参打印 传参打印 IDE传参 cmd传参 命令行参数的应用(文件拷贝) 什么是命令行参数 概念:命令行参数指的是在运行可执行文件时提供给程序的额外输入信息。它们通常以字符串形式出现&am…...
WT2003H4-16S语音芯片:扭蛋机新潮音乐,娱乐升级无限
在扭蛋机的乐趣世界里,唯创知音的WT2003H4-16S语音芯片,作为MP3音乐解码播放IC,为扭蛋机带来了更智能、更富有趣味的音乐体验,为玩家打开了娱乐升级的无限可能。 1. 机启音乐,欢迎扭蛋之旅 扭蛋机启动时,…...
Go 语言开发工具
Go 语言开发工具 VSCode VScode 安装教程参见:https://www.kxdang.com/topic//w3cnote/vscode-tutorial.html 然后我们打开 VSCode 的扩展(CtrlShiftP): 搜索 go: 点击安装,安装完成后我们就可以使用代码…...
神经网络是如何工作的? | 京东云技术团队
作为一名程序员,我们习惯于去了解所使用工具、中间件的底层原理,本文则旨在帮助大家了解AI模型的底层机制,让大家在学习或应用各种大模型时更加得心应手,更加适合没有AI基础的小伙伴们。 一、GPT与神经网络的关系 GPT想必大家已…...
C++ Qt开发:RadioButton单选框分组组件
Qt 是一个跨平台C图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置,实现图形化开发极大的方便了开发效率,本章将重点介绍QRadioButton单选框组件以及与之交互的QButto…...
《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...
学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...
Rapidio门铃消息FIFO溢出机制
关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...
优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...
Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...
软件工程 期末复习
瀑布模型:计划 螺旋模型:风险低 原型模型: 用户反馈 喷泉模型:代码复用 高内聚 低耦合:模块内部功能紧密 模块之间依赖程度小 高内聚:指的是一个模块内部的功能应该紧密相关。换句话说,一个模块应当只实现单一的功能…...
算法打卡第18天
从中序与后序遍历序列构造二叉树 (力扣106题) 给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。 示例 1: 输入:inorder [9,3,15,20,7…...
人工智能 - 在Dify、Coze、n8n、FastGPT和RAGFlow之间做出技术选型
在Dify、Coze、n8n、FastGPT和RAGFlow之间做出技术选型。这些平台各有侧重,适用场景差异显著。下面我将从核心功能定位、典型应用场景、真实体验痛点、选型决策关键点进行拆解,并提供具体场景下的推荐方案。 一、核心功能定位速览 平台核心定位技术栈亮…...
跨平台商品数据接口的标准化与规范化发展路径:淘宝京东拼多多的最新实践
在电商行业蓬勃发展的当下,多平台运营已成为众多商家的必然选择。然而,不同电商平台在商品数据接口方面存在差异,导致商家在跨平台运营时面临诸多挑战,如数据对接困难、运营效率低下、用户体验不一致等。跨平台商品数据接口的标准…...
Axure Rp 11 安装、汉化、授权
Axure Rp 11 安装、汉化、授权 1、前言2、汉化2.1、汉化文件下载2.2、windows汉化流程2.3、 macOs汉化流程 3、授权 1、前言 Axure Rp 11官方下载链接:https://www.axure.com/downloadthanks 2、汉化 2.1、汉化文件下载 链接: https://pan.baidu.com/s/18Clf…...
