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.mzph.cn/diannao/59634.shtml

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

相关文章

使用C++来编写VTK项目时,就是要写自己的算法

其实,使用VTK可以使用很多种语言,比如java,python,和C。那么为什么非要使用C 呢?一个原因是觉得C语言处理数据比较快,另一个原因是需要自己写算法。通过继承polyDataAlgorithm来写自己的算法,很…

[ 内网渗透实战篇-2 ] 父域子域架构的搭建与安装域环境判断域控定位组策略域森林架构配置信任关系

🍬 博主介绍 👨‍🎓 博主介绍:大家好,我是 _PowerShell ,很高兴认识大家~ ✨主攻领域:【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 🎉点赞➕评论➕收藏 养成习…

FileLink跨网数据摆渡系统:打破网络隔阂,轻松实现跨网络数据传输

在数字化时代,跨网络、跨区域的数据传输成为了企业和个人信息流通的重大挑战。而如今,FileLink跨网数据摆渡系统的问世,彻底解决了这一难题,帮助用户实现快速、安全、无缝的跨网络数据传输。 1. 跨网络数据传输的痛点 随着企业信…

MySQL_聚合函数分组查询

上篇复习: 设计数据库时的三大范式1.第一范式,一行数据中每一列不可再分 关系型数据库必须要满足第一范式,设计表的时候,如果每一列都可以用SQL规定的数据类型描述,就天然满足第一范式. 2.第二范式,在第一…

【Ai教程】Ollma安装 | 0代码本地运行Qwen大模型,保姆级教程来了!

我们平时使用的ChatGPT、kimi、豆包等Ai对话工具,其服务器都是部署在各家公司的机房里,如果我们有一些隐私数据发到对话中,很难保证信息是否安全等问题,如何在保证数据安全的情况下,又可以使用大预言模型,O…

FastAPI全方位分析:优劣尽显

近年来,随着技术的飞速发展,快速构建高性能API的需求越来越强烈。Python作为一个广泛使用的编程语言,也在这一领域下涌现出了许多优秀的框架。FastAPI便是其中一颗璀璨的新星。 FastAPI以其卓越的性能和独特的功能吸引了众多开发者。本文将深入剖析FastAPI的各个方面,详细…

LongVU :Meta AI 的解锁长视频理解模型,利用自适应时空压缩技术彻底改变视频理解方式

Meta AI在视频理解方面取得了令人瞩目的里程碑式成就,推出了LongVU,这是一种开创性的模型,能够理解以前对人工智能系统来说具有挑战性的长视频。 研究论文 "LongVU:用于长视频语言理解的时空自适应压缩 "提出了一种革命…

什么是兼容性测试

兼容性测试,提供具有兼容性特性的云端设备(覆盖主流品牌、SDK、分辨率),通过模拟用户行为进行真机测试。及时有效的发现应用中存在的兼容性问题。解除测试人员的双手,提高测试效率,保证产品在海量真机上的高…

IDEA:ctrl+/ 快捷键生成的注释,设置“//”开始位置

问题场景: IDEA中使用 ctrl/ 快捷键,//显示在最左边(顶格),不美观,中间隔了好长的空格,如图: 解决方法: 操作步骤 File–>Sttings–>Editor–>Code Style–>Java–>…

IDEA2024下安装kubernetes插件并配置进行使用

【1】安装插件 其实2024.2.3下默认已经安装了kubernetes插件,如果你发现自己IDEA中没有,在市场里面检索并下载即可。 【2】kubernetes配置 ① 前置工作 首先你要准备一个config文件和一个kubectl.exe 。 config文件类似如下: apiVersi…

H7-TOOL的CAN/CANFD助手增加帧发送成功标识支持, 继续加强完善功能细节

2.27版本固件正式携带此功能,包括之前做的负载率检测和错误信息展示也将集成到这个版本固件中。 对于接收,我们可以直接看到效果,而发送不行,所以打算在发送的地方展示下发送成功标识。CAN发送不像串口,需要等待应答后…

CSP/信奥赛C++刷题训练:经典广搜例题(4):洛谷P1746 :离开中山路

CSP/信奥赛C刷题训练:经典广搜例题(4):洛谷P1746 :离开中山路 题目背景 《爱与愁的故事第三弹shopping》最终章。 题目描述 爱与愁大神买完东西后,打算坐车离开中山路。现在爱与愁大神在 x 1 , y 1 x_1…

CST汽车天线仿真(双向混合求解)

CST从2018版本开始具有双向混合求解,到2019版已经通用微波工作室的各个求解器之间的双向混合。具体的混合对象如下图: 对天线的安装和耦合仿真,意味着对复杂结构(天线)和电大尺寸环境(安装平台,…

【鸿蒙】HarmonyOS NEXT应用开发快速入门教程之布局篇(下)

系列文章目录 【鸿蒙】HarmonyOS NEXT开发快速入门教程之ArkTS语法装饰器(上) 【鸿蒙】HarmonyOS NEXT开发快速入门教程之ArkTS语法装饰器(下) 【鸿蒙】HarmonyOS NEXT应用开发快速入门教程之布局篇(上) 【…

【HCIP园区网综合拓扑实验】配置步骤与详解(未施工完,持续更新中)

一、实验要求 实验拓扑图如上图所示 1、按照图示的VLAN及IP地址需求,完成相关配置 2、要求SW1为VLAN 2/3的主根及主网关 SW2为vlan 20/30的主根及主网关 SW1和SW2互为备份 3、可以使用super vlan 4、上层通过静态路由协议完成数据通信过程 5、…

C++设计模式结构型模式———外观模式

文章目录 一、引言二、外观模式三、总结 一、引言 外观模式是一种结构型设计模式, 能为程序库、 框架或其他复杂类提供一个简单的接口。也就是说,该模式的目的用于隔离接口,换句话说,就是扮演中间层的角色,把本来结合…

软件设计师:排序算法总结

一、直接插入 排序方式:从第一个数开始,拿两个数比较,把后面一位跟前面的数比较,把较小的数放在前面一位 二、希尔 排序方式:按“增量序列(步长)”分组比较,组内元素比较交换 假设…

vue输入中文,获取英文首字母缩写

背景:要求输入中文的时候,系统给出对应的首字母大写,作为拼音。 例如:输入“博客”,输出‘BK’ 等等…… 经查:使用 js-pinyin 这个第三方插件即可实现 1. 下载依赖 npm install js-pinyin 或者 yarn ad…

数据结构与算法--回溯法

回溯法 1 括号生成分析: 2 解数独分析代码 回溯法本质是的暴力枚举/遍历法,一般用递归实现。 当我们可以把问题分解为若干个步骤,每个步骤都有若干个选择的时候,若需要列出所有解答形式,则采用枚举法。 1 括号生成 数…

外卖小程序的研究与开发ssm+论文源码调试讲解

2系统关键技术 2.1微信小程序 微信小程序,简称小程序,英文名Mini Program,是一种全新的连接用户与服务的方式,可以快速访问、快速传播,并具有良好的使用体验。 小程序的主要开发语言是JavaScript,它与普通…