Java 网络编程(一)—— UDP数据报套接字编程

概念

在网络编程中主要的对象有两个:客户端和服务器。客户端是提供请求的,归用户使用,发送的请求会被服务器接收,服务器根据请求做出响应,然后再将响应的数据包返回给客户端。

作为程序员,我们主要关心应用层和传输层,我们编写的程序属于应用层,需要调用传输层的接口来进行数据的传输。Java给我们提供了两套接口,一套是属于UDP 协议的,另一套是属于 TCP 协议的。本篇文章重点讲解UDP 数据报套接字编程。

Socket套接字,是由系统提供用于网络通信的技术,是基于TCP/IP协议的网络通信的基本操作单元。
基于Socket套接字的网络程序开发就是网络编程。

UDP数据报套接字

DatagramSocket

DatagramSocket 简单来理解就是定位你所在的位置,用于接收和发送数据报

构造方法:

方法名说明
DatagramSocket()无参构造方法,不指定端口号,由操作系统自行分配
DatagramSocket(int port)port 就是端口号,这个构造方法就是由程序员自行指定端口号

接收和发送数据包的方法:

方法名说明
send(DatagramPacket p)发送数据包
receive(DatagramPacket p接收数据包,这里是输出型参数,传输层把数据内容填充到你传入的数据包中

什么是输出型参数?
该参数在方法内部会被改变,并且会影响到方法外部的实参。

关闭方法:

方法名说明
close()关闭资源

注意了网络编程使用 Socket ,也是和内存、文件一样都会消耗资源的。

DatagramPacket

DatagramPacket 就是数据报,也就是你发送和接收的数据报,这里数据报和数据包不作区分,大家知道就好了。

构造方法:

字节数组就是用来填充数据的,也是输出型参数。
offset 是指定偏移量
length 是指定要填充多少个字节
address 就是传入地址,SocketAddress 就是一个完整的地址(包含IP 和端口号),InetAddress 只是包含 IP地址,port 就是我们熟悉的端口号。

其他方法:

方法名说明返回值
getAddress()获得该数据包的 IP 地址InetAddress
getPort()获得该数据包端口号int
getSocketAddress()获得该数据包的完整地址(包含IP地址和端口号)SocketAddress
getData()或者数据内容byte[]
getLength()获得数据的长度,以字节为单位int

InetSocketAddress

构造方法:

方法名说明
InetSocketAddress(InetAddress addr, int port)创建一个 Socket 地址,包含IP地址和端口号

其他方法:

方法名说明注意
getByName(String host)将主机名转化为机器能识别的IP地址静态方法

这个方法有什么用?
我们知道一个IP地址我们习惯用十进制来表示,类似”xxx.xxx.xxx.xxx",我们通常传入的这个IP地址是一个字符串,这个方法就能将这个字符串转化为机器能识别的二进制的 IP 地址。

回显服务器编写

这里简单介绍一下,回显服务器就是你发什么我就回什么,例如客户端发送一个 hello,服务器直接返回 hello,这就是回显服务器,此服务器是用来我们学习套接字的。现在我来带领大家完成服务器代码的编写。

首先创建一个服务器的类,这里定义为 UdpEchoServer,Echo 就是回显的意思。
在类里面先定义字段 DatagramSocket socket

    private DatagramSocket socket;//给服务器指定一个端口号public UdpEchoServer(int port) throws SocketException {socket = new DatagramSocket(port);}

在构造方法这里要指定对应的端口号,服务器的位置一定要固定下来,防止客户端那边找不到服务器。


启动程序

服务器是 7 * 24 小时为用户提供的服务的,所以这里我们直接写一个死循环 while(true) {}
每一次循环都是在处理一次请求。

首先我们要接收客户端的数据包,先创建好一个空的数据包来接收数据,这里为什么不传入地址,因为我们这个数据包只是用来接收数据的,并且就在服务器中使用,不需要添加地址。

//构建请求数据包
DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);

然后接收:

//获取请求
socket.receive(requestPacket);//输出型参数

如果没有数据可以接收的话,服务器程序会一直在这里阻塞住。

解析数据包并计算请求,由于这里是回显服务器,所以我们直接构造出 String,然后形式上进行响应的处理:

//解析请求数据包
String request = new String(requestPacket.getData(), 0, requestPacket.getLength());//计算响应值
//这里是回显服务器,直接返回原数据
String response = process(request);
    //计算响应,服务器的核心代码区域private String process(String request) {return request;}

在真实的服务器代码中,我们在响应这里的处理是服务器的核心逻辑,由于是回显服务器,也就显得没有什么感受。


之后就要把响应的数据包发送回客户端那边。

注意:由于 UDP 是不会保存对端的 IP地址和端口号的,所以我们在构建响应数据包的时候,一定要传入目的 IP 和 目的端口号,这里的目的 IP 和 目的端口号可以从请求的数据包获取,因为请求的数据包保存了客户端的IP 和 端口号。

//构建响应数据包
//UDP 不存放对端的源 IP 和 源端口,所以需要传入对方的地址
DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length,
requestPacket.getSocketAddress());//发送响应数据包
socket.send(responsePacket);

还要注意数据的长度一定是response.getBytes().length,不要写出字符串的长度,因为我们的数据是字节,字节的大小和字符的大小是不一样的。


最后我们可以打印一个日志:

//打印日志
System.out.printf("[%s : %d] request: %s response: %s\n",requestPacket.getAddress().toString(),requestPacket.getPort(),request,response);

因为 getAddress() 的返回值是 InetAddress ,所以要使用 toString() 转化为字符串进行打印。

最终代码

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;//服务器程序
public class UdpEchoServer {private DatagramSocket socket;//给服务器指定一个端口号public UdpEchoServer(int port) throws SocketException {socket = new DatagramSocket(port);}//服务器启动运行程序public void start() throws IOException {System.out.println("服务器启动...");//服务器持续运行//每次循环处理一次请求while(true) {//构建请求数据包DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);//获取请求socket.receive(requestPacket);//输出型参数//解析请求数据包String request = new String(requestPacket.getData(), 0, requestPacket.getLength());//计算响应值//这里是回显服务器,直接返回原数据String response = process(request);//构建响应数据包//UDP 不存放对端的源IP 和 源端口,所以需要传入对方的地址DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length,requestPacket.getSocketAddress());//发送响应数据包socket.send(responsePacket);//打印日志System.out.printf("[%s : %d] request: %s response: %s\n",requestPacket.getAddress().toString(),requestPacket.getPort(),request,response);}}//计算响应,服务器的核心代码区域private String process(String request) {return request;}public static void main(String[] args) throws IOException {UdpEchoServer server = new UdpEchoServer(9090);server.start();}
}

客户端编写

客户端是一定要知道请求是发到哪一个服务器上的,所以我们需要保存好服务器的IP 地址和端口号。

private DatagramSocket socket;
private String serverIP;
private int serverPort;

构造方法:注意一定要传入服务器的 IP地址 和 端口号,socket 使用的是 DatagramScoket 的无参构造方法。

为什么使用的是 DatagramScoket 的无参构造方法?
首先我们作为程序员不知道用户那边的主机的端口使用情况,如果固定用户的端口号,正好用户此时已经有进程占用了这个端口号,这时候我们的客户端程序是跑不起来的,这就是端口冲突。
为了避免端口的冲突,我们不指定端口号,而是交给用户主机的操作系统自行指定端口号。

    public UdpEchoClient(String serverIP, int serverPort) throws SocketException {this.serverPort = serverPort;this.serverIP = serverIP;socket = new DatagramSocket();//不用指定客户端的端口号,让用户自己的操作系统自己去安排端口号,避免端口号冲突}

启动程序

这里我们直接让用户从控制台输入要发送的数据,我们构建好请求数据包并发送到服务器上

//构建请求数据包
DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length,
InetAddress.getByName(serverIP),serverPort);//发送数据包
socket.send(requestPacket);

这里我们就使用了InetAddress.getByName()这个方法将本身是 String 类型的IP地址转化为机器能识别的IP地址。
然后要注意数据的长度一定是request.getBytes().length,不要写出字符串的长度,因为我们的数据是字节,字节的大小和字符的大小是不一样的。

最后就是传入目的地址也就是服务器的源IP和源端口号,然后发送给服务器那边。


接着就是接收服务器的响应,我们构建一个空的响应数据包来接收:

//接收数据包
DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);
socket.receive(responsePacket);

最后就是解析并打印响应内容了。

//打印响应数据
String response = new String(responsePacket.getData(),0,responsePacket.getLength());
System.out.println("响应:" + response);

最终代码

import java.io.IOException;
import java.net.*;
import java.util.Scanner;//客户端程序
public class UdpEchoClient {private DatagramSocket socket;private String serverIP;private int serverPort;public UdpEchoClient(String serverIP, int serverPort) throws SocketException {this.serverPort = serverPort;this.serverIP = serverIP;socket = new DatagramSocket();//不用指定客户端的端口号,让用户自己的操作系统自己去安排端口号,避免端口号冲突}public void start() throws IOException {System.out.println("欢迎来到客户端...");Scanner scan = new Scanner(System.in);while(true) {//用户从控制台输入数据System.out.println("请输入你要发送的数据:");while(!scan.hasNext()) {break;}String request = scan.nextLine();//构建请求数据包DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length,InetAddress.getByName(serverIP),serverPort);//发送数据包socket.send(requestPacket);//接收数据包DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);socket.receive(responsePacket);//打印响应数据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();}
}

由于我这里是使用一台主机的两个进程来模拟客户端和服务器的,所以IP地址指定为"127.0.0.1",这是每台主机默认的IP地址,端口号这里指定为 9090,最后大家运行两个程序,就可以看到下面的效果了。

效果展示:

在这里插入图片描述

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【MySQL】数据库表连接简明解释

未经许可,不得转载。 文章目录 表连接表连接的类型内连接与外连接结合 WHERE 条件交叉连接(cross join)表连接 在关系型数据库中,建模是数据组织的核心难点。数据库建模需要将数据关系理清,构建出适合存储和查询的结构。 所谓“模型”包括实体(entity) 和关系(relati…

SpringBoot(八)使用AES库对字符串进行加密解密

博客的文章详情页面传递参数是使用AES加密过得,如下图所示: 这个AES加密是通用的加密方式,使用同一套算法,前端和后端都可以对加密之后的字符串进行加密解密操作。 目前线上正在使用的是前端javascript进行加密操作,将…

拦截器实现http请求访问本地图片

本文来记录下拦截器实现http请求访问本地图片 文章目录 概述代码实现本文小结 概述 如下图,本机(服务器)存储的图片想要在浏览器上通过Url地址访问: 浏览器直接访问 代码实现 烂机器实现文件真实地址和物理地址之间的映射 Slf4j Configuration public cl…

gpu-V100显卡相关知识

一、定义 RuntimeError: FlashAttention only supports Ampere GPUs or newer.torch attention注意力接口学习V100 架构是什么? 二、实现 RuntimeError: FlashAttention only supports Ampere GPUs or newer. 报错原因分析: GPU机器配置低,…

C++初阶——list

一、什么是list list是一个可以在序列的任意位置进行插入和删除的容器,并且可以进行双向迭代。list的底层是一个双向链表,双向链表可以将它们包含的每个元素存储在不同且不相关的存储位置。通过将每个元素与前一个元素的链接和后一个元素的链接关联起来&…

《DiffusionDet: Diffusion Model for Object Detection》ICCV2023

摘要 本文提出了一种新的框架DiffusionDet,它将目标检测任务表述为从带噪声的边界框到目标边界框的去噪扩散过程(如图一所示)。在训练阶段,目标边界框逐渐扩散到随机分布,模型学习逆转这一加噪过程。在推理阶段&#…

词嵌入方法(Word Embedding)

词嵌入方法(Word Embedding) Word Embedding是NLP中的一种技术,通过将单词映射到一个空间向量来表示每个单词 ✨️常见的词嵌入方法: 🌟Word2Vec:由谷歌提出的方法,分为CBOW(conti…

Mit6.S081-实验环境搭建

Mit6.S081-实验环境搭建 注:大家每次做一些操作的时候觉得不太保险就先把虚拟机克隆一份 前言 qemu(quick emulator):这是一个模拟硬件环境的软件,利用它可以运行我们编译好的操作系统。 准备一个Linux系统&#xf…

qt QVideoWidget详解

1. 概述 QVideoWidget是Qt框架中用于视频播放的控件。它继承自QWidget,并提供了与QMediaPlayer等多媒体播放类集成的功能。QVideoWidget可以嵌入到Qt应用程序的用户界面中,用于显示视频内容。它支持多种视频格式,并提供了基本的视频播放控制…

10款PDF合并工具的使用体验与推荐!!!

在如今的信息洪流中,我们几乎每个人都被淹没在大量的数字文档之中。无论是学生、教师还是职场人士,我们都需要高效地管理和处理这些文档。而PDF文件,凭借其跨平台的稳定性和通用性,成了最常用的文档格式之一。我们经常需要处理、编…

【AI大模型】ELMo模型介绍:深度理解语言模型的嵌入艺术

学习目标 了解什么是ELMo.掌握ELMo的架构.掌握ELMo的预训练任务.了解ELMo的效果和成绩.了解ELMo的优缺点. 目录 🍔 ELMo简介 🍔 ELMo的架构 2.1 总体架构 2.2 Embedding模块 2.3 两部分的双层LSTM模块 2.4 词向量表征模块 🍔 ELMo的预…

Gurobi学术版+Anaconda安装步骤

注意:在anaconda虚拟环境中安装gurobi库是不需要在本地下载gurobi这个软件的,只需要conda install gurobi即可,或者指定版本的安装conda install -c gurobi gurobi11.0.3。 step0:安装ananconda step1:获得学术许可&a…

【C++】类与对象的基础概念

目录: 一、inline 二、类与对象基础 (一)类的定义 (二)访问限定符 (三)类域 (四)实例化概念 正文 一、inline 在C语言的学习过程中,大家肯定了解过宏这个概…

解决表格出现滚动条样式错乱问题

自定义表格出现滚动条时,会因为宽度不对等导致样式错乱; 解决思路: 监听表格数据的变化,当表格出现滚动条时,再调用更新宽度的方法updateWidth,去改变表格头部的宽度,最终保持表格头部和内容对…

天才的懈怠 : 平衡二叉树

力扣110:平衡二叉树 描述: 二叉树的每一个节点的左右子树高度差不超过1,即为平衡二叉树 递归 树:还是用递归,从最深的节点开始向上判断,保证每个节点的左右子树高度差不大于1,大于1的用 -1 做标…

使用@react-three/fiber,@mkkellogg/gaussian-splats-3d加载.splat,.ply,.ksplat文件

前言 假设您正在现有项目中集成这些包,而该项目的构建工具为 Webpack 或 Vite。同时,您对 Three.js 和 React 有一定的了解。如果您发现有任何错误或有更好的方法,请随时留言。 安装 npm install three types/three react-three/fiber rea…

MySQL:CRUD

MySQL表的增删改查(操作的是表中的记录) CRUD(增删改查) C-Create新增R-Retrieve检查,查询U-Update更新D-Delete删除 新增(Create) 语法: 单行数据全列插入 insert into 表名[字段一,字段…

DeBiFormer实战:使用DeBiFormer实现图像分类任务(二)

文章目录 训练部分导入项目使用的库设置随机因子设置全局参数图像预处理与增强读取数据设置Loss设置模型设置优化器和学习率调整策略设置混合精度,DP多卡,EMA定义训练和验证函数训练函数验证函数调用训练和验证方法 运行以及结果查看测试完整的代码 在上…

小面馆叫号取餐流程 佳易王面馆米线店点餐叫号管理系统操作教程

一、概述 【软件资源文件下载在文章最后】 小面馆叫号取餐流程 佳易王面馆米线店点餐叫号管理系统操作教程 点餐软件以其实用的功能和简便的操作,为小型餐饮店提供了高效的点餐管理解决方案,提高了工作效率和服务质量 ‌点餐管理‌:支持电…

5G时代的关键元件:射频微波MLCCs市场前景广阔

根据QYResearch调研团队最新发布的《全球射频微波多层陶瓷电容器市场报告2023-2029》显示,预计到2029年,全球射频微波多层陶瓷电容器市场规模将攀升至12.4亿美元,其未来几年内的年复合增长率(CAGR)预计为5.1%。 以下图…