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,最后大家运行两个程序,就可以看到下面的效果了。
效果展示:


相关文章:
Java 网络编程(一)—— UDP数据报套接字编程
概念 在网络编程中主要的对象有两个:客户端和服务器。客户端是提供请求的,归用户使用,发送的请求会被服务器接收,服务器根据请求做出响应,然后再将响应的数据包返回给客户端。 作为程序员,我们主要关心应…...
ECharts图表图例8
用eclipse软件制作动态单仪表图 用java知识点 代码截图:...
Redis中的线程模型
Redis 的单线程模型详解 Redis 的“单线程”模型主要指的是其 主线程,这个主线程负责从客户端接收请求、解析命令、处理数据和返回响应。为了深入了解 Redis 单线程的具体工作流程,我们可以将其分为以下几个步骤: 接收客户端请求 Redis 的主线…...
[产品管理-77]:技术人需要了解的常见概念:科学、技术、技能、产品、市场、商业模式、运营
目录 一、概念定义 科学 技术 技能 产品 市场 商业模式 运营 二、上述概念在产品创新中的作用 一、概念定义 对于技术人来说,了解并掌握科学、技术、技能、产品、市场、商业模式、运营等常见概念的定义至关重要。以下是这些概念的详细解释: 科…...
鼠标点击(一)与3D视口窗口的交互
(1) (2) (3)...
线程-2-线程概念与控制
main 线程常见寄存器(CR3 EIP IR MMU TLB) CR3是当前进程页表物理内存地址(包不能虚拟地址,不然套娃了) CPU中有寄存器指向task_struct* current EIP:入口虚拟地址 IR:当前命令地址系统总线&a…...
TortoiseSVN提示服务器凭证检核错误:站点名称不符
电脑重装了系统,下载了新版本SVN软件,一切准备就绪,准备大干一场。 打开SVN,一遍一遍的提示【TortoiseSVN提示服务器凭证检核错误:站点名称不符】,一次次的让我接受,终于忍受不了了。 TortoiseSVN提示服务…...
Diffusion Policy——斯坦福机器人UMI所用的扩散策略:从原理到其编码实现(含Diff-Control、ControlNet详解)
前言 本文一开始是属于此文《UMI——斯坦福刷盘机器人:从手持夹持器到动作预测Diffusion Policy(含代码解读)》的第三部分,考虑后Diffusion Policy的重要性很高,加之后续还有一系列基于其的改进工作 故独立成本文,且写的过程中 …...
(动画版)排序算法 -希尔排序
文章目录 1. 希尔排序(Shellsort)1.1 简介1.2 希尔排序的步骤1.3 希尔排序的C实现1.4 时间复杂度1.5 空间复杂度1.6 希尔排序动画 1. 希尔排序(Shellsort) 1.1 简介 希尔排序(Shells Sort),又…...
delphi fmx android 自动更新(二)
自己写了一个升级的类,支持android与windows 1,下载升级包,可以设置进度条 我这里用的fmxui的进度条,你也可以用原生的 http下载我用的nethttpclient, 进度条设置是比较方便的 首先获取下载文件的大小 用nethttpclient.head函数请求文件地址,得到contentlength 接着…...
蓝队知识浅谈(中)
声明:学习视频来自b站up主 泷羽sec,如涉及侵权马上删除文章 感谢泷羽sec 团队的教学 视频地址:蓝队基础之网络七层杀伤链_哔哩哔哩_bilibili 本文主要分享一些蓝队相关的知识。 一、网络杀伤链 网络杀伤链(Cyber Kill Chain&…...
解决vue3+ts打包项目时会生成map文件
在正常未配置的情况下使用npm run build 命令打包,会生成很多的js和map文件,map文件是为了方便我们在生产环境进行更友好的代码调试,但是这样就存一个安全问题;容易被攻击; 解决方法:在package.json文件,重…...
webpack指南
🌈个人主页:前端青山 🔥系列专栏:webpack篇 🔖人终将被年少不可得之物困其一生 依旧青山,本期给大家带来webpack篇专栏内容:webpack-指南 概念 中文: webpack | webpack中文文档 | webpack中文网 英文&…...
关于QUERY_ALL_PACKAGES权限导致Google下架apk
谷歌商店被下架,原因是第三方使用了 QUERY_ALL_PACKAGES 权限; Google在高版本上限制了此权限的使用。当然,并不是 QUERY_ALL_PACKAGES 这个权限没有了,而是被列为敏感权限,必须有充分的理由说明,才允许上架 GP&#…...
优化时钟网络之时钟抖动
Note:文章内容以Xilinx 7系列FPGA进行讲解 1、什么是时钟抖动 时钟抖动就是时钟周期之间出现的偏差。比如一个时钟周期为10ns的时钟,理想情况下,其上升沿会出现在0ns,10ns,20ns时刻,假设某个上升沿出现的时…...
C++《继承》
在之前学习学习C类和对象时我们就初步了解到了C当中有三大特性,分别是封装、继承、多态,通过之前的学习我们已经了解了C的封装特性,那么接下来我们将继续学习另外的两大特性,在此将分为两个章节来分别讲解继承和多态。本篇就先来学…...
微澜:用 OceanBase 搭建基于知识图谱的实时资讯流的应用实践
本文作者: 北京深鉴智源科技有限公司架构师 郑荣凯 本文整理自北京深鉴智源科技有限公司架构师郑荣凯,在《深入浅出 OceanBase 第四期》的分享。 知识图谱是一项综合性的系统工程,需要在在各种应用场景中向用户展示经过分页的一度关系。 微…...
【LeetCode】【算法】538. 把二叉搜索树转换为累加树
LeetCode 538. 把二叉搜索树转换为累加树 题目 给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。 提醒一下…...
YoloV8改进策略:注意力改进|EPSANet,卷积神经网络上的高效金字塔挤压注意力块|即插即用|代码+改进方法
摘要 论文介绍 本文介绍的论文是“EPSANet:卷积神经网络上的高效金字塔挤压注意力块”,该论文提出了一种新颖、轻量且有效的注意力方法,即金字塔挤压注意力(PSA)模块。论文通过替换ResNet瓶颈块中的 3 3 3 \times 3 3...
Nextflow最佳实践:如何在云上高效处理大规模数据集
1. Nextflow 软件架构介绍 Nextflow 是一个用于简化数据驱动计算流程的工具,可以在各种计算环境中轻松部署。它采用了分布式计算和容器技术,实现了高度模块化、可重复性和可扩展性。NextFlow 的软件架构主要包括以下几个部分: 用户界面&…...
【巴洛克AI生成合规白皮书】:基于梵蒂冈档案馆高清藏品训练的192个版权安全Prompt模板
更多请点击: https://codechina.net 第一章:巴洛克AI生成合规白皮书导论 巴洛克AI生成合规白皮书旨在为组织在部署和运营生成式人工智能系统时,提供一套可落地、可审计、可演进的合规治理框架。该白皮书聚焦于中国《生成式人工智能服务管理暂…...
观察使用 Taotoken Token Plan 套餐后的月度成本变化
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 观察使用 Taotoken Token Plan 套餐后的月度成本变化 对于个人开发者或小型团队而言,大模型 API 的调用成本是项目预算…...
ops-rand:AI 训练中的随机数生成
AI 训练离不开随机数。权重初始化要随机、Dropout 要随机、数据打乱要随机、噪声注入要随机。每一次随机操作的背后,都有一组随机数生成器在工作。 CANN 的 ops-rand 仓库提供了 NPU 上的随机数生成算子——Dropout、随机 Shuffle、随机初始化、正态分布采样等。这…...
Servlet 容器与过滤器 超详细讲解
目录 一、Servlet 容器(Servlet Container) 1. 是什么? 2. 核心作用(必须掌握) 3. Servlet 生命周期(容器全权控制) 4. 工作流程(HTTP 请求完整链路) 5. 总结一句话 二、过滤器(Filter) 1. 是什么? 2. 核心特点 3. 过滤器能做什么?(高频场景) 4. 过滤…...
【硬核测评】程序员专属AI面霸养成指南:鹅来面OfferGoose,真的能让你Offer拿到手软?
一、AI面试工具:程序员求职的“智能外挂”,你跟上节奏了吗?在当今内卷激烈的互联网求职环境中,面试环节的难度与复杂度不断攀升。从在线笔试的代码挑战,到远程视频面试的即时问答,再到大厂多轮技术面试的压…...
ElevenLabs湖南话TTS深度评测(2024真实场景压测报告):声调准确率92.6%、连读自然度行业首破88分
更多请点击: https://intelliparadigm.com 第一章:ElevenLabs湖南话语音技术概览 ElevenLabs 作为全球领先的语音合成平台,其多语言支持能力持续扩展,但需明确指出:截至 2024 年底,ElevenLabs 官方模型库*…...
AnimateDiff:3分钟让静态图像动起来的AI动画生成神器
AnimateDiff:3分钟让静态图像动起来的AI动画生成神器 【免费下载链接】animatediff 项目地址: https://ai.gitcode.com/hf_mirrors/ai-gitcode/animatediff 你是否想过,只需几句话就能让静态图片活起来?是否在寻找将创意想法快速转化…...
StarUML Java插件终极指南:高效实现UML与Java代码双向转换
StarUML Java插件终极指南:高效实现UML与Java代码双向转换 【免费下载链接】staruml-java Java extension for StarUML 项目地址: https://gitcode.com/gh_mirrors/st/staruml-java StarUML Java插件为Java开发者提供了强大的UML建模与代码生成能力ÿ…...
OpenClaw 本地部署避坑指南|环境配置 + 故障排查全流程
🦞 OpenClaw 本地部署避坑指南|环境配置 故障排查全流程 开源 AI 自动化工具OpenClaw(小龙虾) 凭借本地私有化部署、无侵入系统交互、全流程自动化执行等核心特性,在开发者社区快速普及。轻量化架构与高扩展性&#…...
终极LuaJIT反编译指南:如何快速恢复丢失的Lua源代码
终极LuaJIT反编译指南:如何快速恢复丢失的Lua源代码 【免费下载链接】luajit-decompiler https://gitlab.com/znixian/luajit-decompiler 项目地址: https://gitcode.com/gh_mirrors/lu/luajit-decompiler 你是否曾面对编译后的LuaJIT字节码文件束手无策&…...
