UDP数据报套接字编程

devtools/2024/9/23 18:07:55/

UDP数据报套接字编程

DatagramSocket API

        DatagramSocket,是UDP Socket,用于发送和收 UDP 数据报。使用这个类,表示一个 socket 对象。一个 socket 对象只能跟一台主机进行通信。在操作系统中,把这个 socket 对象当成一个文件来处理的,相当于是 文件描述符表 上的一项(操作系统操作网卡,是把网卡抽象成了特殊的文件,称为 socket 文件)。

构造方法:

方法签名

方法说明

DatagramSocket()

创建一个UDP数据报套接字的Socket,随机分配一个空闲端口(一般用于客户端)

DatagramSocket(int port)

创建一个UDP数据报套接字的Socket,绑定到本机指定的端口(一般用于服务端)

普通方法:

方法签名

方法说明

void receive(DatagramPacket p)

从此套接字接收数据报(如果没有接收到数据报,该方法会阻塞等待)

void send(DatagramPacket p)

   从此套接字发送数据报包(不会阻塞等待,直接发送)

   void close()

   关闭此数据报套接字

        此处传入的 DatagramPacket p 相当于是一个空的对象。receive 方法内部会对这个空对象进行内容填充,从而构造出结果数据。这个参数是一个“输出型参数”。

DatagramPacket API

        DatagramPacket 是UDP Socket 发送和接收的数据报。表示 UDP 中传输的一个报文,构造这个对象,可以指定一个字节数组,作为持有数据的缓冲区。 

构造方法:

方法签名

方法说明

DatagramPacket(byte[] buf, int length)

构造一个DatagramPacket以用来接收数据报,接收的数据保存在字节数组(第一个参数buf)中,接收指定长度(第二个参数length)

DatagramPacket(byte[] buf, int offset, int length, SocketAddress address)

构造一个DatagramPacket以用来发送数据报,发送的数据为字节数组(第一个参数buf)中,从0到指定长度(第二个参数

length)。address指定目的主机的IP和端口号

普通方法:

方法签名

方法说明

InetAddress getAddress()

从接收的数据报中,获取发送端主机IP地址;或从发送的数据报中,获取接收端主机IP地址

int getPort()

从接收的数据报中,获取发送端主机的端口号;或从发送的数据报中,获取接收端主机端口号

byte[] getData()

获取数据报中的数据

        构造UDP发送的数据报时,需要传入 SocketAddress ,该对象可以使用 InetSocketAddress 来创建。

InetSocketAddress API

InetSocketAddress ( SocketAddress 的子类 )构造方法:

方法签名

方法说明

InetSocketAddress(InetAddress addr, int port)

创建一个Socket地址,包含IP地址和端口号

示例一:回显服务器(请求什么就响应什么)

        一个普通的服务器包括:收到请求,根据请求计算响应(业务逻辑),返回响应。这里就直接省略了业务逻辑。以下为一个客户端一次数据发送,和服务端多次数据接收(一次发送一次接收,可以接收多次),即只有客户端请求,但没有服务端响应的示例:

UDP服务器端 

服务器的工作流程:

1. 读取请求并解析

2. 根据请求计算响应

3. 构造响应并写回给客户端 

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;// UDP 版本的回显服务器
public class UdpEchoServer {// 网络编程, 本质上是要操作网卡.// 但是网卡不方便直接操作. 在操作系统内核中, 使用了一种特殊的叫做 "socket" 这样的文件来抽象表示网卡.// 因此进行网络通信, 势必需要先有一个 socket 对象.private DatagramSocket socket = null;// 对于服务器来说, 创建 socket 对象的同时, 要让他绑定上一个具体的端口号.// 服务器一定要关联上一个具体的端口的!!!// 服务器是网络传输中, 被动的一方. 如果是操作系统随机分配的端口, 此时客户端就不知道这个端口是啥了, 也就无法进行通信了!!!public UdpEchoServer(int port) throws SocketException {socket = new DatagramSocket(port);}public void start() throws IOException {System.out.println("服务器启动!");// 服务器不是只给一个客户端提供服务就完了. 需要服务很多客户端.while (true) {// 只要有客户端过来, 就可以提供服务.// 1. 读取客户端发来的请求是啥.//    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());   //DatagramPacket 这个只认字节,因此必须得response.getBytes() 再获取长度socket.send(responsePacket);   //商家把大病给客户// 4. 打印一下, 当前这次请求响应的处理中间结果.System.out.printf("[%s:%d] request: %s; response: %s\n", requestPacket.getAddress().toString(),requestPacket.getPort(), request, response);}}// 这个方法就表示 "根据请求计算响应"public String process(String request) {return request;}public static void main(String[] args) throws IOException {// 端口号的指定, 大家可以随便指定.// 1024 -> 65535 这个范围里随便挑个数字就行了.UdpEchoServer server = new UdpEchoServer(6666);server.start();}
}

        此处的 while(true) 没有跳出条件,就是死循环的,因为作为服务器要时时刻刻都要提供服务,防止什么时候有请求过来。 

UDP客户端

        服务器的 端口 是要固定指定的:目的是为了方便客户端找到服务器程序。

        客户端的 端口 是由系统自动分配的:如果手动指定,可能会和客户端其他程序的端口有冲突。服务器不怕冲突是因为服务器上面的程序可控,通过命令能看到哪些端口是空闲的;客户端是运行在客户电脑上的,不确定性太大。

import java.io.IOException;
import java.net.*;
import java.util.Scanner;// UDP 版本的 回显客户端
public class UdpEchoClient {private DatagramSocket socket = null;private String serverIp = null;private int serverPort = 0;// 一次通信, 需要有两个 ip, 两个端口.// 客户端的 ip 是 127.0.0.1 已知.// 客户端的 port 是系统自动分配的.// 服务器 ip 和 端口 也需要告诉客户端. 才能顺利把消息发个服务器.public UdpEchoClient(String serverIp, int serverPort) throws SocketException {socket = new DatagramSocket();this.serverIp = serverIp;this.serverPort = serverPort;}public void start() throws IOException {System.out.println("客户端启动!");Scanner scanner = new Scanner(System.in);while (true) {// 1. 从控制台读取要发送的数据System.out.print("> ");String request = scanner.next();if (request.equals("exit")) {System.out.println("goodbye");break;}// 2. 构造成 UDP 请求, 并发送//    构造这个 Packet 的时候, 需要把 serverIp 和 port 都传入过来. 但是此处 IP 地址需要填写的是一个 32位的整数形式.//    上述的 IP 地址是一个字符串. 需要使用 InetAddress.getByName 来进行一个转换.DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,InetAddress.getByName(serverIp), serverPort);socket.send(requestPacket);// 3. 读取服务器的 UDP 响应, 并解析DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);socket.receive(responsePacket);String response = new String(responsePacket.getData(), 0, responsePacket.getLength());// 4. 把解析好的结果显示出来.System.out.println(response);}}public static void main(String[] args) throws IOException {UdpEchoClient client = new UdpEchoClient("127.0.0.1", 6666);client.start();}
}

客户端启动后会发送一个"hello" 的字符串到服务端,在服务端接收后,控制台输出内容如下:                                              这里的 IP 是环回 IP,这里的端口号是随机分配的。

如果想要开启多个客户端可以在这里设置:

示例二:查字典服务器

UDP服务器端

import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;// 对于 DictServer 来说, 和 EchoServer 相比, 大部分的东西都是一样的.
// 主要是 "根据请求计算相应" 这个步骤不太一样.
public class UdpDictServer extends UdpEchoServer {private Map<String, String> dict = new HashMap<>();public UdpDictServer(int port) throws SocketException {super(port);// 给这个 dict 设置内容dict.put("cat", "猫咪");dict.put("dog", "狗子");dict.put("duck", "鸭子");// 当然, 这里可以无限多的设置键值对.....}@Overridepublic String process(String request) {// 查词典的过程.return dict.getOrDefault(request, "当前单词没有查到结果!");}public static void main(String[] args) throws IOException {UdpDictServer server = new UdpDictServer(6666);server.start();}
}

        这里的操作主要是直接复用了之前的 EchoServer 的代码,直接重写 process 即可。

端口冲突

        由于一个端口只能对应一个进程,所以当一个端口有多个进程想使用的时候就会报错。因此开启上述两个服务器就会出现以下报错:


http://www.ppmy.cn/devtools/22811.html

相关文章

SpringMVC基础篇(四)

文章目录 1.视图1.基本介绍1.视图介绍2.为什么需要自定义视图 2.自定义视图实例1.思路分析2.代码实例1.view.jsp2.接口3.配置自定义视图解析器springDispatcherServlet-servlet.xml4.自定义视图MyView.java5.view_result.jsp6.结果展示 3.自定义视图执行流程4.自定义视图执行流…

CSS基础:4类组合选择器以及5个注意事项

你好&#xff0c;我是云桃桃。 一个希望帮助更多朋友快速入门 WEB 前端的程序媛。 云桃桃-大专生&#xff0c;一枚程序媛&#xff0c;感谢关注。回复 “前端基础题”&#xff0c;可免费获得前端基础 100 题汇总&#xff0c;回复 “前端工具”&#xff0c;可获取 Web 开发工具合…

Matlab实现CNN-BiLSTM模型,对一维时序信号进行分类

1、利用Matlab2021b训练CNN-BiLSTM模型&#xff0c;对采集的一维时序信号进行分类二分类或多分类 2、CNN-BiLSTM时序信号多分类执行结果截图 训练进度&#xff1a; 网络分析&#xff1a; 指标变化趋势&#xff1a; 代码下载方式&#xff08;代码含数据集与模型构建&#xff0…

MATLAB初学者入门(23)—— 旅行商问题(TSP)优化

旅行商问题&#xff08;TSP, Traveling Salesman Problem&#xff09;是一个经典的优化问题&#xff0c;要求找到一个最短的路线&#xff0c;使得旅行商从一个城市出发&#xff0c;经过所有城市一次后&#xff0c;回到原出发点。这是一个NP难问题&#xff0c;在数学优化和计算机…

2024 JAVA Tinypng压缩图片,超级简单!!!

一、打开官网&#xff0c;注册账号&#xff0c;获取秘钥&#xff08;每个月500张免费&#xff09; 1.打开官网&#xff0c;注册账号 TinyPNG – Compress WebP, PNG and JPEG images intelligently 2.登录后&#xff0c;点击账号名字&#xff0c;找到如图所示 3.找到API&…

idea的插件,反编译整个jar包

idea的插件&#xff0c;反编译整个jar包 1.安装插件1.1找到插件1.2 搜索插件 2.反编译整个jar包2.1 复制jar包到工件目录下&#xff1a;2.2 选中jar包&#xff0c;点出右键 3.不用插件&#xff0c;手动查看某一个java类3.1 选中jar包&#xff0c;点出右键 1.安装插件 1.1找到插…

【计算机网络】成功解决 ARP项添加失败:请求的操作需要提升

最近在用Wireshark做实验时候&#xff0c;需要清空本机ARP表和DNS缓存&#xff0c;所以在cmd窗口输入以下命令&#xff0c; 结果发生了错误&#xff1a;ARP项添加失败&#xff1a;请求的操作需要提升 一开始我还以为是操作的命令升级了&#xff0c;但是后面发现其实只是给的权…

NLP transformers - 文本分类

Text classification 文章目录 Text classification加载 IMDb 数据集Preprocess 预处理EvaluateTrainInference 本文翻译自&#xff1a;Text classification https://huggingface.co/docs/transformers/tasks/sequence_classification notebook : https://colab.research.googl…