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

news/2024/5/18 12:35:51 标签: 网络, java, udp, tcp/ip, 网络协议

简介

四次挥手示意图

  • 在四次挥手过程中,第一次挥手中的Seq为本次挥手的ISN, ACK为 上一次挥手的 Seq+1,即最后一次数据传输的Seq+1。
  • 挥手信息由客户端首先发起。

实现步骤:

下面是TCP四次挥手的步骤:

  1. 第一次挥手(FIN):主动关闭方发送一个带有FIN(Finish)标志的TCP报文段给被动关闭方,表示主动关闭方已经没有数据要发送了。

  2. 第二次挥手(ACK):被动关闭方接收到第一次挥手的TCP报文段后,发送一个带有ACK(Acknowledgment)和确认序号的TCP报文段作为响应,表示已经收到了关闭请求。

  3. 第三次挥手(FIN):被动关闭方发送一个带有FIN标志的TCP报文段给主动关闭方,表示被动关闭方也没有数据要发送了。

  4. 第四次挥手(ACK):主动关闭方接收到第三次挥手的TCP报文段后,发送一个带有ACK和确认序号的TCP报文段作为响应,表示已经收到了关闭请求。

在完成这四次挥手之后,TCP连接就正式关闭了。需要注意的是,每一次挥手都需要对方的确认才能进行下一步操作,这样可以确保双方都知道连接已经关闭,并且保证数据的完整性和可靠性。

这个过程可以简化为以下步骤:

  1. 主动关闭方发送FIN报文段。
  2. 被动关闭方接收到FIN后,发送ACK报文段作为确认。
  3. 被动关闭方发送FIN报文段。
  4. 主动关闭方接收到FIN后,发送ACK报文段作为确认。

这样,TCP连接就完成了关闭过程。

修改说明:

我将客户端的发送消息和服务端的接收消息做了一些简单的封装:

客户端:

java">    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);
    }

服务端:

java">//接收数据
    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;
    }

 

第一次挥手

客户端发送关闭请求:

java"> //四次挥手,关闭连接
            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);

服务端接收数据:

java"> //四次握手
            //第一次
            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

java">//第二次
            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);

客户端接收

java"> 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);

 

第三次挥手

服务端发送请求关闭给客户端

java">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给服务端

java"> 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给服务端

java">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并且关闭 

java">System.out.println("====================");
            String receiveB4 = serverMsg.receive(datagramSocket, 0, 0);
            System.out.println("接收到的数据段为:" + receiveB4);

            //关闭流
            datagramSocket.close();

完成总结:

  • 基本完成了UDP实现TCP,但仍有欠缺。
  • 实现过程中代码的复用过高,没有进行有效的方法封装。
  • 代码不够成熟,还由一些不完善的地方,没有实现MTU机制。
  • 对UDP和TCP,有了更深入的了解。

http://www.niftyadmin.cn/n/5258123.html

相关文章

3、Kafka 线上集群部署方案怎么做?

文章目录 1、操作系统的选择1.1、I/O 模型的使用1.2、数据网络传输效率1.3、社区支持度 2、磁盘的选择3、磁盘容量的规划3.1、举例思考本问题:3.2、计算一下:3.3、规划磁盘容量时你需要考虑下面这几个元素: 4、带宽规划4.1、计算 总结 1、操作…

《在线业务抗D解决方案-抗D产品的核心优势功能》

●全球资源弹性调配,轻松应对大型攻击:高防能力抗D产品的核心能力,在这方面,我们在全球拥有上万个专用DDoS流量清洗节点,全网防护带宽超10T;结合实时的全局资源弹性调配机制,可为不同行业属性的…

mars3d加载arcgis发布的服务,⽀持4523坐标

问题 1.从这个服务地址加载,具体在哪⾥去转坐标呢? 加个 usePreCachedTilesIfAvailable:false 参数即可 坐标系为4490的arcgis影像服务图层,配置后瓦片加载不出来,没报错 甚至可以跳转 没有看出问题,或者测…

为什么要跳出循环,Java跳出循环方法汇总

目录 一、什么是循环语句 二、为什么要跳出循环? 三、contince语句 四、break语句 一、什么是循环语句 循环语句是一种编程语言中的结构,它允许程序重复执行一段特定的代码。在循环语句中,程序会根据条件一次又一次地执行一段代码&#…

西南科技大学数字电子技术实验五(用计数器设计简单秒表)预习报告

一、计算/设计过程 说明:本实验是验证性实验,计算预测验证结果。是设计性实验一定要从系统指标计算出元件参数过程,越详细越好。用公式输入法完成相关公式内容,不得贴手写图片。(注意:从抽象公式直接得出结…

【日积月累】Spring中的AOP与IOC相关问题详解

Spring中的AOP与IOC 1.前言2.Spring AOP(面向切面编程)2.1 AOP的实现过程2.2 AOP代理模式的类型2.2.1JDK的动态代理2.2.2CGLIB的动态代理 2.3AOP应用常见场景2.3.1日志记录 2.4对AOP的理解 3.Spring IOC(Inversion of Control,控…

算法——前缀和

模板一维前缀和 【模板】前缀和_牛客题霸_牛客网该算法是先预处理一个数组,用空间换时间,将原本时间复杂度为O(n2)降为O(n) 题目解析 题中下标(用i表示)从1开始计数,长度为n的数组,想访问到an 位置&…

SQL server创建联合索引

CREATE INDEX idx_dim_product_dt_ord ON [dim_product] (dt, ord); 在SQL Server中计算月同比和季度同步的SQL查询可能看起来像这样: ### 月度同比 月度同比是与前一年同一月份的数据进行比较。以下是一个基本的例子,假设我们有一个名为sales的表&…