JavaEE——网络编程(UDP套接字编程)

news2024/11/27 20:27:33

文章目录

  • 一、简单理解Socket 套接字
  • 二、UDP 数据报套接字编程
  • 三、编写简单的 UDP 版本服务器客户端
    • 1. 编写 UDP 版本的回显服务器
      • 回显服务器整体代码罗列
    • 2. 编写 UDP 版本的回显客户端
      • 回显客户端整体代码罗列
  • 四、总结与代码运行结果解释

一、简单理解Socket 套接字

概念: Socket 套接字就是操作系统给应用程序提供的网络编程 API。

我们可以认为 socket api 是和传输层密切相关的。

我们知道,在传输层中,提供了两个最核心的协议,UDP TCP。
因此,socket api 中也提供了两种风格。UDP TCP。

在这里我们简单认识一下 UDP 和 TCP

  • UDP: 无连接 不可靠传输 面向数据报 全双工。
  • TCP: 有连接 可靠传输 面向字节流 全双工。

解释 有连接 / 无连接

例:
打电话就是有连接的,需要建立了才能通信。建立连接需要对方来 “接受”
发短信,就是无连接的,直接发送即可无需接受。

解释 可靠传输 / 不可靠传输

在这里,对于可靠传输的定义是:发送方的数据到底是不是发送过去了,还是丢了

所以,在这里:
打电话是一个可靠传输。
发短信是一个不可靠传输。
但要注意的是,可靠不可靠,与有没有连接没有任何关系

解释 面向字节流 / 面向数据报

面向字节流: 数据传输和文件读写类似,是“流式”的。
面向数据报: 数据传输以一个个“数据报”为单位。(一个数据报可能为若干个字节,带有一定的格式)。

解释 全双工

即就是一个通信通道,可以双向传输。(既可以发送,也可以接收)

对应的 半双工 就是指只可以单向传输信息。
至于如何传递信息,与用户的 路由器,交换机配置有关。如图:
在这里插入图片描述

二、UDP 数据报套接字编程

这里给出了两个类用来操作:

  • DatagramSocket

使用这个类表示一个 socket 对象。
在操作系统中,是将这个 socket 对象当成一个文件来处理的。

对于普通文件,对应的硬件设备是 硬盘。 对于socket 文件,对应的硬件设备是 网卡

拥有了 socket 对象就可以与另一台主机进行通信。如果要和多个不同主机交互,就需要创建多个 socket 对象。

DatagramSocket 构造方法:
在这里插入图片描述
在这里就可以看出来,本质上不是 进程 和 端口 建立联系,而是进程中的 socket 对象和 端口 建立联系

DatagramSocket 方法:
在这里插入图片描述
对于 void receive(DatagramPacket p) 方法:
在此处传入的相当于一个空对象,receive 方法内部,会对参数的空对象进行内容填充。从而构造出结果数据。(构造出一个数据报)

  • DatagramPacket

该套接字 API 表示的是 UDP 中传输的一个报文。构造这个对象可以将指定的具体数据传递进去。

DatagramPacket 构造方法:

在这里插入图片描述
DatagramPacket 方法:

在这里插入图片描述

三、编写简单的 UDP 版本服务器客户端

文章中详细解释的是其中较为核心的代码,与整体逻辑还有差异,整体代码会在后面罗列。

1. 编写 UDP 版本的回显服务器

注:这里编写的客户端服务器是一个简单 UDP 版本的服务器,称之为:回显服务器。

一个普通的服务器: 收到请求,根据请求计算响应(业务逻辑),返回响应。

回显服务器: 省略了普通服务器的 “根据请求计算响应”,这里只是为了演示 socket api 的用法。

创建服务器前的初步准备

  1. 我们要知道,网络通信的本质就是操作网卡
  2. 但是网卡的直接操作十分不便,在操作系统内核中就使用了 socket 这样的文件来描述网卡。
  3. 因此要实现网络通信,就必须要先创建出一个 socket 对象

代码如下:

//这里定义成一个私有属性方便后续直接使用
    private DatagramSocket socket = null;

注:尤其对于一个服务器来讲,创建一个 socket 对象的同时,需要让其绑定一个明确的端口号。

因为在服务器在网络传输中处于一个被动的状态,没有一个明确的端口号,客户端就无法寻找到请求的服务器。

// 这里的 UdpEchoSever 是定义的服务器类名
// 这里的 port 就是一个服务器端口号
    public UdpEchoSever(int port) throws SocketException {
        socket = new DatagramSocket(port);
    }

形象的解释上面需要端口号的原因:
例如:
假设本人现在开了一家面馆,地址在地球村,美利坚1号,这里的 “1号” 就相当于端口号
假设本人的小面做的不错,口口相传我都在 “地球村,美利坚1号”。但是,如果我现在通过小推车贩卖小面,只是经常在 1号 门口售卖,有时到处跑,此时,客户就很难准确的找到我。
因此,固定的位置就很重要,端口也是如此!

创建服务器的核心原理以及代码

  1. 读取客户端发送过来的请求。

对于 UDP 来说,传输的基本单位是 DatagramPacket

这里要用 receive 方法来接受请求。
这里还需要再次说明一下关键字 receive
这个 receive 方法是一个 输出型参数,所以,这里需要先创建出来一个 空白的 DatagramPacket 对象,交给 receive 来填充(填充的是数据来自于网卡)

            //receive 方法是一个输出型参数,需要先创建好一个空白的 DatagramPacket 对象,交给 receive 方法来填充
        DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096 );
        socket.receive(requestPacket);

此时,接受过来的 DatagreamPacket 是一个特殊的对象,不方便处理,这里需要将其构造成一个字符串类型。

String request = new String(requestPacket.getData(),0, requestPacket.getLength());

解释上述字符串转换代码
如下:
我们已知,上面传递下来的元素是存储在数组中。 在这里插入图片描述
如上图所示,这里的数组不一定是用满的。因此,要构造字符串,构造的就是呢些使用的部分。 即就是调用 getLength()
方法获取实际长度。(0 ~ getLength() 范围的元素)

  1. 根据需求计算响应(这里写的是一个回显服务器,所以请求和响应相同)

获取处理后的元素

String response = process(request);

设计根据需求计算响应方法

	// 这里就是实现了一个回显
    public String process(String request){
        return request;
    }
  1. 将数据返回给客户端

这里将数据返回给客户端是 服务器 的工作。
因此,这里调用的 send 方法是属于 DatagramSocket 的,但是其发送的内容单元是 DatagramPacket 类型
发送回客户端前也就需要将 packet 构建好。

   DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,
                  requestPacket.getSocketAddress());
   // 发送组织好的信息
   socket.send(responsePacket);

但是这里构造的相应对象与 接受时获取对象不同,这里构造的响应对象不可以使用空的字节数组,而是要使用响应的元素构造

  • 简单分析上述对象构造代码
    在这里插入图片描述
    要注意的是,这里获取字符的形式 第二种 最好,以字节的形式获取元素很少会出现元素缺失的情况,而字符相比于字节,单位就大了许多,自然风险也高。
  1. 打印一下处理元素时的中间情况
   System.out.printf("[%s:%d] req: %s; resp: %s\n",requestPacket.getAddress().toString(),requestPacket.getPort()
                    ,request,response);

图示解释各个元素:
在这里插入图片描述

回显服务器整体代码罗列

整体展示 UDP 格式的服务器代码:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

//Udp版本的回显服务器
public class UdpEchoSever {
    //网络通信的本质是操作网卡
    //但是网卡的直接操作非常不方便,在操作系统内核中,就使用了 socket 这样的文件来描述网卡
    //因此要实现网络通信,就必须先创建出一个 socket 对象
    private DatagramSocket socket = null;

    //对于服务起来讲,创建 socket 对象同时,需要让其绑定上一个端口号
    //尤其针对服务器,更需要一个准确的端口号
    //因为服务器在网络传输中是处于被动的状态,没有明确地端口号,客户端就无法寻找到请求的服务器
    public UdpEchoSever(int port) throws SocketException {
        socket = new DatagramSocket(port);
    }

    public void start() throws IOException {
        System.out.println("服务器启动");
        //要注意的是服务器不是给一个客户端服务的,需要服务很多的客户端
        while(true){
            //1. 读取客户端发送过来的请求
            //   使用 receive 方法接受请求。
            //receive 方法是一个输出型参数,需要先创建好一个空白的 DatagramPacket 对象,交给 receive 方法来填充
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096 );
            socket.receive(requestPacket);

            //此时 DatagramPacket 是一个特殊的对象,不方便处理,这里可以将数据拿出来,构造成一个字符串
            String request = new String(requestPacket.getData(),0, requestPacket.getLength());

            //2.根据请求计算响应,这里是一个回显服务器,所以请求和响应相同
            String response = process(request);

            //3.将响应数据返回给客户端
            // send 的参数也是 DatagramPacket 需要将这个 packet 构建好
            //      此处构造的响应对象,不可以使用空的字节数组,而是要用响应的元素构造
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,
                    requestPacket.getSocketAddress());
            socket.send(responsePacket);
            //4.打印一下,当前此次相应的中间处理结果
            System.out.printf("[%s:%d] req: %s; resp: %s\n",requestPacket.getAddress().toString(),requestPacket.getPort()
                                ,request,response);
        }
    }

    //这个方法就是"根据需求计算响应"
    public String process(String request){
        return request;
    }

    public static void main(String[] args) throws IOException {
        //这里的端口可以随意设置
        UdpEchoSever sever = new UdpEchoSever(9090);
        sever.start();
    }
}

2. 编写 UDP 版本的回显客户端

对于客户端,我们要知道,就是来和对应的客户端进行通信的。
这里,我们就应该想起前面文章中提到的 网络通信的五元组
在这里插入图片描述
如上图所示,下面我们首先来实现基本设置。

回显客户端的基本设置

    private DatagramSocket socket = null;
    private String severIp = null;
    private int severPort = 0;

//这里是构造方法
    public UdpEchoClient(String severIP,int severPort) throws SocketException {
        //这里不需要设定端口,让操作系统自动分配
        socket = new DatagramSocket();
        this.severIp = severIP;
        this.severPort = severPort;
    }
  • 首先这里构造的 socket 对象,不需要绑定一个固定的显示端口。随机挑选空闲的即可。

  • 其次,这里的
    源IP:127.0.0.1 已知。
    源端口:9090 前面已经设定。
    目的 IP:127.0.0.1 已知(环回IP)。
    目的端口:当前已经随机分配。 已知。

  • 最后使用的协议类型也已经明确。

到这里,基本上已经万事俱备,下面解释后面的操作。

编写客户端核心操作

  1. 从控制台获取要发送的数据

这里的操作比较简单直接展示代码:

        System.out.println("客户端启动");
        Scanner scanner = new Scanner(System.in);
            //1. 从控制台读取要发送的数据
            //打印提示符
            System.out.println("> ");
            String request = scanner.next();
            if(request.equals("exit")){
                System.out.println("bye");
                break;
            }
  1. 构造成 UDP 请求并发送

这里要注意的是,此处要将信息发送出去,同样要调用 send 方法
前面说过,send 方法中传递的元素类型是 packet 类型。所以仍然需要构造 packet 类型,并且需要将 severIP 和 port 传入。

代码如下:

 //   上述 IP 地址是一个字符串,需要 InetAddress.getByName 来进行转换
   DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length,
           InetAddress.getByName(severIp),severPort);
   socket.send(requestPacket);
  1. 读取服务器的 UDP 响应,并解析
    DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096);
    socket.receive(responsePacket);
    String response = new String(responsePacket.getData(),0,requestPacket.getLength());

这里和前面服务器获取元素相同,同样使用 receive 方法将返回的信息填充。
最后转换成 String 类型。

  1. 将解析好的结果显示出来。
   System.out.println(response);

回显客户端整体代码罗列

整体展示 UDP 格式的客户端代码

import java.io.IOException;
import java.net.*;
import java.util.Scanner;

//Udp版本的回显客户端
public class UdpEchoClient {
    private DatagramSocket socket = null;
    private String severIp = null;
    private int severPort = 0;

    //一次通信需要两个 IP 两个端口
    //客户端的 IP 是 127.0.0.1 已知
    //客户端的 端口号 是操作系统自动分配 已知
    //要进行传输,服务器的 IP 和 端口号 也需要传递给 客户端
    public UdpEchoClient(String severIP,int severPort) throws SocketException {
        //这里不需要设定端口,让操作系统自动分配
        socket = new DatagramSocket();
        this.severIp = severIP;
        this.severPort = severPort;
    }

    public void start() throws IOException {
        System.out.println("客户端启动");
        Scanner scanner = new Scanner(System.in);
        //这里不只是一个客户端访问服务器
        while(true){
            //1. 从控制台读取要发送的数据
            //打印提示符
            System.out.println("> ");
            String request = scanner.next();
            if(request.equals("exit")){
                System.out.println("bye");
                break;
            }
            //2. 构造成 UDP 请求,并发送
            //   构造这个 Packet 的时候,需要将 severIP 和 port 传入。但是此处的 IP 需要的是一个 32 位的整数形式
            //   上述 IP 地址是一个字符串,需要 InetAddress.getByName 来进行转换
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length,
                    InetAddress.getByName(severIp),severPort);
            socket.send(requestPacket);
            //3. 读取服务器的 UDP 响应,并解析
            DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096);
            socket.receive(responsePacket);
            // 将返回回来的信息构造为 String 字符串
            String response = new String(responsePacket.getData(),0,requestPacket.getLength());
            //4. 将解析好的结果显示出来
            System.out.println(response);
        }
    }

    public static void main(String[] args) throws IOException {
        UdpEchoClient client = new UdpEchoClient("127.0.0.1",9090);
        client.start();
    }
}

四、总结与代码运行结果解释

  1. 首先解释运行结果。
  • 启动客户端 / 服务器
    在这里插入图片描述
  • 运行示例
    在这里插入图片描述
    在这里插入图片描述
  1. 总结
    简单说明客户端和服务器之间的相互交流。
  • 说明服务器情况
    在这里插入图片描述

  • 说明客户端情况
    在这里插入图片描述

有关 UDP 的使用以及相关工作逻辑到此已经基本解释完毕,文笔浅薄,如有不足之处欢迎指出。

码子不易,您小小的点赞是对我最大的鼓励!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/875459.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

实践-传统深度学习

简介与安装 2 训练自己的数据集整体流程3 数据加载与预处理4 搭建网络模型5 学习率对结果的影响6 Drop-out操作7 权重初始化方法对比8 初始化标准差对结果的影响9 正则化对结果的影响10 加载模型进行测试 TensorFlow:每一步都需要自己做。 Keras:做起来更…

Redis_缓存2_缓存删除和淘汰策略

14.5 缓存数据的删除和替换 14.5.1 过期数据 可以使用ttl查看key的状态。已过期的数据,redis并未马上删除。优先去执行读写数据操作,删除操作延后执行。 14.5.2 删除策略 redis中每一个value对应一个内存地址,在expires,一个内…

关于海外应用ASO优化的一些热知识

应用程序市场挤满了人,我们不能依靠运气来帮助应用程序在全球应用市场上被发现,其中大约有 500 万个应用程序争夺相同的客户。ASO是应用营销活动的重要组成部分,其目的是提高应用程序在Google Play Store和Apple App Store中的可见度。 1、为…

怎么截取视频制成GIF?教你简单的GIF制作方法

怎么将一段视频截取片段制作成GIF动图呢?很多小伙伴在看到一段视频中的搞笑片段,想要将其制作成GIF表情包来在社交平台上发送、交流,那样会非常有趣,GIF动图能够很好的表达我们的情绪,因此该怎么制作呢?教大…

安利5个:在线帮助文档协同与团队知识库管理工具!

在线帮助文档协同与团队知识库管理工具是帮助团队共享和管理知识的重要工具。下面是我推荐的五个工具,并对它们的优点和缺点进行了简要描述。其中,我特别推荐Baklib这个工具。 Confluence: 优点:Confluence是Atlassian公司的产品&#xff0…

OLED透明屏批发:解读未来显示技术的创新与机遇

在数字化时代,OLED透明屏作为一种创新的显示技术,吸引了广泛的关注和应用。本文将深入探讨OLED透明屏的优势和应用领域,分析其在批发市场中的需求和机遇。 一、OLED透明屏的优势和应用领域 OLED透明屏的定义和原理: OLED透明屏采…

KU Leuven TU Berlin 推出“RobBERT”,一款荷兰索塔 BERT

荷兰语是大约24万人的第一语言,也是近5万人的第二语言,是继英语和德语之后第三大日耳曼语言。来自比利时鲁汶大学和柏林工业大学的一组研究人员最近推出了基于荷兰RoBERTa的语言模型RobBERT。 谷歌的BERT(来自Transformers的B idirectional …

搞什么副业可以月入过万?

现在的社会发展非常快,因此出现了许多非常有用而充满娱乐性的兼职职位。这些兼职职位可以让你放松自己的身心,同时还能获得高额报酬。下面兼职网将提供一些正规的兼职职位,可以助你月入数万。 第一种:网络直播主播 网络直播现在非…

redis — 基于Spring Boot实现redis延迟队列

1. 业务场景 延时队列场景在我们日常业务开发中经常遇到,它是一种特殊类型的消息队列,它允许把消息发送到队列中,但不立即投递给消费者,而是在一定时间后再将消息投递给消费者。延迟队列的常见使用场景有以下几种: 在…

Linux目录结构(快速了解)

linux目录结构 核心 1.Linux一切皆文件 2.只有一个顶级目录,而windows分C盘、D盘等 目录结构 目录含义(与windows进行比对) Linux含义windows/bin所有用户可用的基本命令存放的位置windows无固定的命令存放目录/bootlinux系统启动的时候需要…

JaCoCo助您毁灭线上僵尸代码 | 京东物流技术团队

一. 现状问题 随着需求不断迭代,业务系统的业务代码突飞猛进,在你自豪于自己的代码量产出很高时,有没有回头看看线上真正的客户使用量又有多少呢? 费事费力耗费大量人力,成本上线的功能,可能一年没人使用…

一文讲透!请收下这份“完美”地图贴图的制作攻略

3D渲染中,我们称传入材质的纹理为贴图。贴图一词强调其用途,当某个纹理用于在材质中实现法线效果时,我们称之为法线贴图。而在EasyV中,我们地图组件填充样式中的自定义上传的图片称之为「地图贴图」,主要用于地图表面/…

多线程进阶

多线程进阶 本章博客主要是围绕一些多线程相关的面试题,讨论的内容都是往年同学遇到的原题,以后面试也大概率会遇到的!!! 常见的锁策略 锁策略指的不是某个具体的锁,是一个抽象的概念,描述的…

VS2019 设置注释和取消注释选选定内容

Microsoft Visual Studio Professional 2019 由于老是忘记,换了电脑就不行了,原来默认的太繁琐。 每次都去设置选定内容,老是不行。 应该设置在切换块注释 上面这样就可以按一个组合键实现注释和不注释了。

考研408 | 【计算机网络】 传输层

导图 传输层的功能 传输层的两个协议 传输层的寻址与端口 UDP协议 UDP的主要特点 UDP首部格式: UDP校验: TCP协议 TCP协议的特点 TCP报文段首部格式 TCP连接管理 TCP的连接建立 SYN洪泛攻击 TCP的连接释放 TCP可靠传输 序号: 确认&#xff1…

【Rust】Rust学习 第十二章一个 I/O 项目:构建一个命令行程序

本章既是一个目前所学的很多技能的概括,也是一个更多标准库功能的探索。我们将构建一个与文件和命令行输入/输出交互的命令行工具来练习现在一些你已经掌握的 Rust 技能。 Rust 的运行速度、安全性、单二进制文件输出和跨平台支持使其成为创建命令行程序的绝佳选择…

回归预测 | MATLAB实现基于LSSVM-Adaboost最小二乘支持向量机结合AdaBoost多输入单输出回归预测

回归预测 | MATLAB实现基于LSSVM-Adaboost最小二乘支持向量机结合AdaBoost多输入单输出回归预测 目录 回归预测 | MATLAB实现基于LSSVM-Adaboost最小二乘支持向量机结合AdaBoost多输入单输出回归预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 1.Matlab实现L…

C++ 动态规划经典案例解析之最长公共子序列(LCS)_窥探递归和动态规划的一致性

1. 前言 动态规划处理字符相关案例中,求最长公共子序列以及求最短编辑距离,算是经典中的经典案例。 讲解此类问题的算法在网上一抓应用一大把,即便如此,还是忍不住有写此文的想法。毕竟理解、看懂都不算是真正掌握,唯…

侯捷 C++ part2 兼谈对象模型笔记——6 多态 虚机制

6 多态 虚机制 6.1 虚机制 当类中有虚函数时(无论多少个),其就会多一个指针—— vptr 虚指针,其会指向一个 vtbl 虚函数表,而 vtbl 中有指针一一对应指向所有的虚函数 有三个类依次继承,其中A有两个虚函…

​LeetCode解法汇总617. 合并二叉树

目录链接: 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目: https://github.com/September26/java-algorithms 原题链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 描述: 给你两棵二…