Java网络编程,使用UDP实现TCP(二), 实现数据传输过程

news/2024/5/18 14:26:35 标签: 网络, udp, tcp/ip

简介:

经过了三次握手过程,我们的服务端和客户端已经建立了连接。我们接下来需要做的就是数据的传输。

主要步骤:

  1. 数据发送:客户端或服务器将数据打包成一个或多个数据段,每个数据段都有一个序列号(SEQ)和确认号(ACK)。

  2. 数据接收:接收方接收到数据段后,将根据数据段的序列号(SEQ)进行排序,以确保数据的有序性。

  3. 确认接收:接收方会为每个接收到的数据段发送一个确认(ACK)。这个确认包含了确认号(ACK),该确认号是接收方期望接收的下一个数据段的序列号,也就是最后一个接收到的数据段的序列号加1。

  4. 丢包重传:如果发送方在一定时间内没有接收到某个数据段的确认,它会认为该数据段已经丢失,并进行重传。

  5. 流量控制:接收方可以使用窗口大小字段来控制发送方的发送速度,以防止自己被大量的数据淹没。

  6. 拥塞控制:如果网络发生拥塞,TCP会降低数据的发送速度,以减少网络的拥塞程度。(未实现)

所以在数据传输段过程中,每一段都需要Seq(序列号),ACK(确认号)

实现过程:

  1. 确认号(ACK)是基于之前接收到的最后一个数据包的序列号+1。也就是说,ACK的值是接收方期望接收的下一个数据包的序列号。这是因为,在TCP协议中,ACK的值总是等于接收方已经成功接收的最后一个数据包的序列号+1。
  2. 例如,假设在三次握手的过程中,客户端发送给服务器的最后一个ACK是101(这是第三次握手的ACK),那么在数据传输开始时,如果服务器是第一个发送数据的话,服务器发送的第一个数据包的序列号应该是101,而客户端回复的ACK就应该是102,表示客户端已经接收到了序列号为101的数据包,期望接收的下一个数据包的序列号是102。

服务端发送数据到客户端

  • 注意由于数据的传输是双向的,所以在发送后可以接收客户端发来的数据。
  • 在接收数据后,我们在服务端需要进行超时判断,如若超时会进行超时重传。超时判断我们使用了 setSoTimeout() 方法。
    • 关于超时的时间,根据《TCP/IP详解》卷二 的计时器篇中可知,超时时间严苛来说,需要考虑发送端到接收端接收数据时间接收端数据处理时间接收端发送确认消息到客服端时间。超时时间的选择应该是多次数据传输花费时间的均值
  • 在《TCP/IP详解》卷二 的 tcp 篇详细中规定了数据重传不能超过三次,这里我使用了retryCount这个计数器来计数。
//数据传输开始
            System.out.println("====================");
            System.out.println("数据发送...");
            String SeqD1 = String.valueOf(connectionMarks.getSeq());
            String ACKD1 = String.valueOf(Integer.parseInt(strArr3[1]) + 1);
            String dataMsg = SeqD1 + " " + "我是马尔咖里斯,我是不朽的!" + " " + ACKD1;
            byte[] datasD1 = dataMsg.getBytes();
            DatagramPacket datagramPacketD1 = new DatagramPacket(datasD1, 0,datasD1.length, new InetSocketAddress("localhost",8888));
            
            int maxRetries = 3; // 最大重试次数
            int retryCount = 0; // 当前重试次数
            boolean success = false; // 是否成功标志

            while (!success && retryCount < maxRetries) {
                try {
                    // 设置超时时间
                    datagramSocket.setSoTimeout(50000);

                    // 发送数据
                    datagramSocket.send(datagramPacket);

                    // 接收响应
                    byte[] buffer = new byte[1024];
                    DatagramPacket responsePacket = new DatagramPacket(buffer, buffer.length);
                    datagramSocket.receive(responsePacket);

                    // 处理响应
                    String response = new String(responsePacket.getData(), 0, responsePacket.getLength());
                    System.out.println("接收到响应:" + response);

                    // 设置成功标志
                    success = true;
                } catch (java.net.SocketTimeoutException e) {
                    // 超时后重新发送数据
                    retryCount++;
                    System.out.println("超时,进行第 " + retryCount + " 次重试");
                } catch (IOException e) {
                    // 处理其他异常
                    e.printStackTrace();
                }
            }

            if (!success) {
                System.out.println("重试次数超过最大限制,操作失败");
            }

客户端发送数据到服务端

注意:在tcp三次握手后数据段的传输也需要添加ACK,和Seq,如前文所述。这里为了便于实现在传回数据时只写了数据返回 “收到消息” 是不符合规定的,需要修改。

 System.out.println("====================");
            System.out.println("开始接收数据段...");
            byte[] bytes1 = new byte[1024];
            DatagramPacket datagramPacketD1 = new DatagramPacket(bytes1, bytes1.length);
            datagramSocket.receive(datagramPacketD1);
            String receiveMsg = new String(datagramPacketD1.getData(), 0, datagramPacketD1.getLength());
            System.out.println("接收到的数据段为:" + receiveMsg);
            String[] split = receiveMsg.split(" ");
            String SeqD1 = split[0];

            System.out.println("====================");
            System.out.println("数据消息确认返回");
            String replyMsg = "收到消息";
            clientMsg.sendMsg(replyMsg, datagramSocket);


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

相关文章

lua基本语法使用

Lua 是一个小巧的脚本语言。Lua由标准C编写而成&#xff0c;几乎在所有操作系统和平台上都可以编译&#xff0c;运行。Lua并没有提供强大的库&#xff0c;这是由它的定位决定的。所以Lua不适合作为开发独立应用程序的语言。 1.基本语法 注解 -- 单行 -- [[ ]] -- 多行 …

单变量线性回归的机器学习代码

本文为学习吴恩达版本机器学习教程的代码整理&#xff0c;使用的数据集为https://github.com/fengdu78/Coursera-ML-AndrewNg-Notes/blob/f2757f85b99a2b800f4c2e3e9ea967d9e17dfbd8/code/ex1-linear%20regression/ex1data1.txt 将数据集和py代码放到同一目录中&#xff0c;使…

MySQL笔记-第14章_视图

视频链接&#xff1a;【MySQL数据库入门到大牛&#xff0c;mysql安装到优化&#xff0c;百科全书级&#xff0c;全网天花板】 文章目录 第14章_视图1. 常见的数据库对象2. 视图概述2.1 为什么使用视图&#xff1f;2.2 视图的理解 3. 创建视图3.1 创建单表视图3.2 创建多表联合视…

数据库处理与分组存储

from sql_connector import ConnectorJavaTest,Connector158 import pandas as pd import re# 定义一个函数&#xff0c;用于清除product_name列下的数字 def remove_digits_and_punctuations(text):pattern r[^\w\s] # 匹配非单词字符和非空白字符return re.sub(pattern, , …

stm32 HAL库 发送接受 到了一定的字符串后就卡在.s文件中

问题介绍&#xff1a; 某个项目开发过程中&#xff0c;串口接收中断&#xff0c;开启了DMA数据传输&#xff0c;开启了DMA中断&#xff0c;开启DMA半满中断。然后程序运行的过程中&#xff0c;接收了一部分数据后就会卡在启动文件的DMA1_Ch4_7_DMA2_Ch3_5_IRQHandler 中断里。…

PHP中什么是Composer?

Composer 是一个用于 PHP 项目依赖管理的工具。它允许你定义、安装和管理 PHP 项目所需的外部库和工具。Composer 是一个命令行工具&#xff0c;通过一个名为 composer.json 的配置文件来管理项目的依赖关系。 主要功能包括&#xff1a; 依赖管理&#xff1a; Composer 可以解…

【JavaScript】JavaScript中的GC算法

1、内存管理 内存&#xff1a;由可读写单元组成&#xff0c;标识一片可操作的空间 管理&#xff1a; 认为的去操作一篇空间的申请、使用和释放 内存管理&#xff1a;开发者主动申请空间、使用空间、释放空间 管理流程&#xff1a; 申请-使用-释放 // 申请 let obj {} //使…

云计算在数据处理中的应用

云计算在数据处理中的应用 一、引言 随着数据规模的爆炸式增长&#xff0c;数据处理成为了一个巨大的挑战。云计算作为一种灵活、可扩展的计算模式&#xff0c;为数据处理提供了强大的支持。本文将探讨云计算在数据处理中的应用。 二、云计算与数据处理 云计算是一种将计算资…