【JavaEE】_基于UDP实现网络通信

news2024/12/26 3:09:14

目录

1. 服务器

1.1 实现逻辑

1.2 代码

1.3 部分代码解释

2. 客户端

2.1 实现逻辑

2.2 代码

2.3 客户端部分代码解释

3. 程序运行结果

4. 服务器客户端交互逻辑


此篇内容为实现UDP版本的回显服务器echo server;

普通服务器:收到请求,根据请求计算响应,返回响应;

回显服务器:忽略计算,直接将收到的请求作为响应返回;

如需实现其他功能,修改响应计算方法process内容即可);

具体实现代码如下:

1. 服务器

1.1 实现逻辑

对于网络通信的服务器需要进行的工作为:

1. 读取请求并解析;

2. 根据请求计算响应;

3. 把响应写回客户端;

4. 打印交互详细信息;

1.2 代码

package TestDemo1;

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

public class UDPEchoServer {
    // 创建一个DatagramSocket对象,作为后续操作网卡的基础
     private DatagramSocket socket = null;
     public UDPEchoServer(int port) throws SocketException{
         // 显式指定服务器端口号
         socket = new DatagramSocket(port);
     }
     public void start() throws IOException {
         // 服务器启动方法
         System.out.println("服务器启动");
         // 为保证服务器随时向客户端提供服务
         while(true){
             // 1. 读取请求并解析
             DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);
             // DatagramPacket对象用于承载从网卡读到的数据,收到数据时需要创建一个内存空间保存这个数据,
             // DatagramPacket内部不能自行分配内存空间,需要程序员手动创建
             socket.receive(requestPacket);
             // 完成receive之后,数据以二进制形式存储在DatagramPacket中
             // 需要将二进制数据转成字符串
             String request = new String(requestPacket.getData(),0, requestPacket.getLength());
             // 取0~requestPacket.getLength()区间内的字节构成一个String对象;

             //2. 根据请求计算响应
             String response = process(request);

             //3. 把响应写回客户端
             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 {
        UDPEchoServer server = new UDPEchoServer(9090);
        // 可以在1024~65535(临时端口)中任意选取端口号
        server.start();
    }
}

1.3 部分代码解释

1. 为方便计算请求,将请求的DatagramPacket对象构造成字符串时只需获取DatagramPacket对象中实际有效的部分数据,requestPacket.getLength()获取到的就是收到数据的真实长度而非4096这个最大长度:

2. 已经了解过UDP本身是无连接的,故而对于每一次通信过程,在构造数据报时都需要指定数据报要发给谁;

(1)对于构造的responsePacket对象有3个参数:

DatagramPacket responsePacket = new DatagramPacket(                   
response.getBytes(),
response.getBytes().length,
requestPacket.getSocketAddress());

response.length()获取到的是响应对象字符串的字符个数,

response.getBytes().length获取到的是响应对象字符串的字节个数;

requestPacket.getSocketAddress()获取到的是请求发送方(即客户端)的IP与端口号

(2)注意response.getBytes.length(字节个数)与response.getlength()(字符个数)的区别:

如果response字符串都是英文字符则二者相等,如果包含中文则二者不同;

在进行网络传输时,必然是需要通过字节为单位进行通信的。

3. while(true)会使程序处于快速循环的状态,每循环一次就处理一次请求响应,

当客户端发出请求时,recerive就能顺利读取请求,客户端没有发出请求时,receive就会阻塞

如果客户端发出的请求过多,可以使用多线程冲动调动计算机硬件资源,也可以再多开机器,但多开机器又会涉及到分布式问题;

4. 使用格式化输出Packet的IP与端口号:

5. 前文已经提及socket也是一个文件,但在上文代码中并未进行close操作,却没有造成文件资源泄漏的原因是:

socket是文件描述符表中的一个表项,每次打开一个文件就会占用一个位置,文件描述符在pcb上,是跟随进程的。在上文代码中创建的socket对象在整个程序运行过程中都需要使用,不可以提前关闭,当socket不需要使用时,即代表程序结束了,进程结束了,文件描述符表也销毁了,伴随着销毁都被系统自动回收了。故而不会造成文件资源泄露问题

只有代码中频繁打开文件但不关闭,在一个进程的运行过程中,不断积累打开的文件,逐渐消耗掉文件描述符表中的内容,最后消耗殆尽,才会造成泄露。

对于生命周期很短的进程,无需考虑泄露,在客户端方一般来说影响不大。

2. 客户端

2.1 实现逻辑

对于网络通信的客户端,需要进行的工作是:

1. 从控制台读取数据作为客户端发出的请求;

2. 将请求字符串request构造成请求requestPacket对象,发送给服务器;

3. 尝试读取服务器返回的响应;

4. 将响应responsePacket对象构造成响应response字符串,显示出来;

2.2 代码

package TestDemo1;

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

public class UDPEchoClient {
    private DatagramSocket socket = null;
    private String serverIp = "";
    private int serverPort = 0;
    public UDPEchoClient(String ip, int port) throws SocketException {
        // 客户端的socket对象需令系统自动分配
        socket = new DatagramSocket();
        // UDP本身不持有对端信息,需要在应用程序中记录对端信息(IP与端口)
        serverIp = ip;
        serverPort = port;
    }
    public void start() throws IOException {
        // 客户端启动方法
        System.out.println("客户端启动");
        Scanner scanner = new Scanner(System.in);
        while(true){
            // 1. 从控制台读取数据作为客户端发出的请求
            System.out.println("->");
            String request = scanner.next();

            // 2. 将请求字符串request构造成请求requestPacket对象,发送给服务器
            DatagramPacket requestPacket = new DatagramPacket(
                    request.getBytes(),request.getBytes().length,
                    InetAddress.getByName(serverIp), serverPort);
            socket.send(requestPacket);

            // 3. 尝试读取服务器返回的响应
            DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);
            socket.receive(responsePacket);

            // 4. 将响应responsePacket对象转换为响应字符串response,显示出来
            String response = new String(responsePacket.getData(), 0, responsePacket.getLength());
            System.out.println(response);
        }
    }
    public static void main(String[] args) throws IOException {
        UDPEchoClient client = new UDPEchoClient("127.0.0.1", 9090);
        client.start();
    }
}

2.3 客户端部分代码解释

1.DatagramPacket的三个构造方法

第一种:只指定字节数组缓冲区,用于服务器接收请求与客户端接收响应时使用

第二种:指定字节数组缓冲区与InetAddress对象(同时包含IP与端口),用于服务器向客户端发回响应时使用

第三种:指定字节数组缓冲区,同时指定IP与端口号

2. 客户端对服务器发出请求的过程:

对于服务器端口必须是确定的:程序员可以手动分配空闲端口给当前服务器使用即可,

代码为:

socket = new DatagramSocket(port);

客户端一般都采取系统分配的方式:也可以指定端口,但不推荐。一方面,因为指定的端口可能被其他进程占用,如果被占用就会产生端口号冲突,运行就会抛出异常,提示绑定端口失败;另一方面,如果客户端出现了端口冲突,让客户手动解决也是不现实的。

代码为:

socket = new Datagramocket();

3. 端口与进程的关系:

端口号用于标识或区分一个进程,因此在同一台主机上,不允许一个端口同时被多个进程使用;

但是一个进程可以绑定多个端口;

socket和端口是一一对应的,进程与socket是一对多的

3. 程序运行结果

首先启动EchoServer,再启动EchoClient,在客户端输入请求字符串后查看运行结果:

客户端与服务器通信成功。

4. 服务器客户端交互逻辑

1. 服务器先启动,启动后进入循环,执行到receive处阻塞;

2. 客户端开始启动后,进入循环执行scanner.next(),在此处阻塞。

    当客户在控制台输入内容后,next返回作为请求,继而构造请求数据并发送给服务器;

3. 客户端发送数据后,

服务器从receive中返回,解析请求构造字符串,执行process操作计算响应,构造响应后执行send发回给客户端;

客户端执行到receive处等待服务器的响应;

4. 客户端获取到从服务器返回的数据后,从receive中返回,继而显示响应内容;

5. 服务器完成一次循环后又执行到receive处,客户端完成依次循环后又执行到scanner.next处,二者均进入阻塞状态;

注意:当服务器程序在普通私有ip计算机上运行时,若不在一个局域网中,无法实现跨主机访问。

如果服务器程序在特殊的计算机:云服务器上,就拥有了公有ip,可以实现跨主机访问。

此部分内容后续详解。

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

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

相关文章

Unity中ShaderGraph下获取主灯

文章目录 前言一、ShaderGraph获取主灯1、创建ShaderGraph2、创建一个自定义方法(Custom Function)节点3、新建两个 Vector3 类型的输出变量4、选择自定义节点程序体为 string 类型5、编写程序体6、我们输出主光方向看看效果7、我们输出主光颜色看看效果…

ios适配虚拟home键

在H5开发过程中遇到一个兼容性问题。iphone手机的虚拟home键会对屏幕底部的内容造成遮挡。要处理此问题,需要清楚安全区域这个概念。 安全区域 根据刘海和虚拟Home键,Apple为其设备提供了屏幕安全区域的视觉规范 竖屏:竖屏的时候&#xff…

「优选算法刷题」:查找总价格为目标值的两个商品

一、题目 购物车内的商品价格按照升序记录于数组 price。请在购物车中找到两个商品的价格总和刚好是 target。若存在多种情况,返回任一结果即可。 示例 1: 输入:price [3, 9, 12, 15], target 18 输出:[3,15] 或者 [15,3]示例…

OpenCV-Python(49):图像去噪

目标 学习使用非局部平均值去噪算法去除图像中的噪音学习函数cv2.fastNlMeansDenoising()、cv2.fastNlMeansDenoisingColored等 原理 在前面的章节中我们已经学习了很多图像平滑技术,比如高斯平滑、中值平滑等。当噪声比较小时,这些技术的效果都是很好…

【Qt之模型视图】1. 模型和视图架构

1. 模型/视图架构是什么及有什么用 MVC(Model-View-Control)是一种源自Smalltalk的设计模式,通常用于构建用户界面。 MVC由三种类型的对象组成。模型是应用对象,用来表示数据;视图是模型的用户界面,用来显…

Miracast无线投屏的操作步骤

家里有台老爷机,估计在10年以上了。内核屏显还是HD 4400。今天原本在尝试挂蓝牙音箱,没整成。意外地激活了无线投屏。Miracast是一个wifi本身的功能包,可以让台式机通过wifi与屏幕互联,不必通过hdmi线。 Step1.确认无线投屏能力&…

【大数据分析与挖掘技术】Mahout推荐算法

目录 一、推荐的定义与评估 (一)推荐的定义 (二)推荐的评估 二、Mahout中的常见推荐算法 (一)基于用户的推荐算法 (二)基于物品的推荐算法 (三)基于S…

【C++干货铺】C++异常处理机制

个人主页点击直达:小白不是程序媛 C系列专栏:C干货铺 代码仓库:Gitee 目录 C语言传统的处理错误的方式 C处理异常方式 异常的使用 异常的抛出和捕获 异常的重新抛出 异常安全 异常规范 自定义异常体系 C标准库中的异常体系 ​编辑…

【Linux系统编程二十八】基于条件变量的阻塞队列(生产消费模型)

【Linux系统编程二十八】基于条件变量的阻塞队列(生产消费模型) 一.同步问题二.条件变量1.实现原理2.等待的前提3.使用接口①.【定义条件变量】②.【初始化条件变量】③.【让线程去条件变量下等待】④.【为什么第二个参数是锁?】条件变量和锁的关系是什么…

swift基础语法

swift学习笔记 参考教程 https://www.runoob.com/swift/swift-data-types.html swift代码规范 https://juejin.cn/post/7129465308376465422 1 环境搭建 必须要有苹果电脑且安装Xcode 2 基本语法 Swift是类型安全的语言,编译时会进行类型检查 import Cocoa var m…

从方法论到最佳实践,深度解析企业云原生 DevSecOps 体系构建

作者:匡大虎 引言 安全一直是企业上云关注的核心问题。随着云原生对云计算基础设施和企业应用架构的重定义,传统的企业安全防护架构已经不能够满足新时期下的安全防护要求。为此企业安全人员需要针对云原生时代的安全挑战重新进行系统性的威胁分析并构…

5G_射频测试_测试模式解读(三)

Downlink test models FR1 test model 1.1 (NR-FR1-TM1.1)(满PRB,QPSK)FR1 test model 1.2 (NR-FR1-TM1.2)( QPSK/boosted/40% QPSK)FR1 test model 2 (NR-FR1-TM2)(64QAM 只有1个PRB 功率最低)FR1 test model 2a (NR-FR1-TM2a) )(256QAM 只…

Eureka使用详解

介绍主要特点主要功能与常用服务注册中心的比较Eureka与Zookeeper的区别和联系Eureka与Nacos的区别与联系Eureka与Consul的区别与联系 安装部署Eureka与CAP理论Eureka实现实时上下线Eureka常用注解Eureka架构模式 介绍 Eureka是一个基于REST的服务,主要用于AWS云中…

python222网站实战(SpringBoot+SpringSecurity+MybatisPlus+thymeleaf+layui)-帖子详情页实现

锋哥原创的SpringbootLayui python222网站实战: python222网站实战课程视频教程(SpringBootPython爬虫实战) ( 火爆连载更新中... )_哔哩哔哩_bilibilipython222网站实战课程视频教程(SpringBootPython爬虫实战) ( 火…

Element-UI 多个el-upload组件自定义上传,不用上传url,并且携带自定义传参(文件序号)

1. 需求: 有多个(不确定具体数量)的upload组件,每个都需要单独上传获取文件(JS File类型),不需要action上传到指定url,自定义上传动作和http操作。而且因为不确定组件数量&#xff0…

SpringMVC-.xml的配置

文章目录 一、对pom.xml的配置二、对web.xml1.第一种方式2. 第二种方式 三、对SpringMVC.xml的配置 一、对pom.xml的配置 <!-- 打包成war包--><packaging>war</packaging> <dependencies><!-- SpringMVC--><dependency><gro…

Shiro框架:Shiro用户访问控制鉴权流程-Aop注解方式源码解析

目录 1.Spring Aop嵌入点解析 2.Shiro框架Aop切面逻辑解析 2.1 通过注解实现切点 2.2 通过增强逻辑执行校验过程 2.2.1 增强实现类AopAllianceAnnotationsAuthorizingMethodInterceptor 2.2.1.1 类图解析 2.2.1.2 实现增强方法 2.2.1.3 Shiro校验逻辑实现 2.2.1.3.1 …

代码随想录27期|Python|Day33|贪心算法|1005.K次取反后最大化的数组和|134. 加油站|135. 分发糖果

1005. K 次取反后最大化的数组和 思路比较简单&#xff0c;把所有的负数绝对值大的全部取反之后再在新的数组里把绝对值最小的重复取反即可。 class Solution(object):def largestSumAfterKNegations(self, nums, k):""":type nums: List[int]:type k: int:rt…

VS里那些实用的调试(debug)技巧

前言——————希望现在在努力的各位都能感动以后享受成功的自己&#xff01; 首先我们要来了解什么是bug——————bug本意是“昆虫”或“虫子”&#xff0c;现在⼀般是指在电脑系统或程序中&#xff0c;隐藏着的⼀些未被发现的缺陷或 问题&#xff0c;简称程序漏洞。 “…

Java导出Excel并合并单元格

需求&#xff1a;需要在导出excel时合并指定的单元格 ruoyi excel 项目基于若伊框架二次开发&#xff0c;本着能用现成的就不自己写的原则&#xff0c;先是尝试了Excel注解中needMerge属性 /*** 是否需要纵向合并单元格,应对需求:含有list集合单元格)*/public boolean needMer…