文章目录
- 🌳前言
- 🌲DatagramSocket API
- 🎄DatagramPacket API
- 🍀InetSocketAddress API
- 🎍回显客户端与服务器
- 📌建立回显服务器
- 📌回显客户端
- 📌成果展示
- 🎋翻译小助手服务器与客户端的简单建立
- 📌服务器的建立
- 📌客户端的建立
- 📌结果展示
- 🌳多个客户端对一个服务器
- ⭕总结
🌳前言
我们用Java实现UDP数据报套接字编程,需要借用以下API来实现
🌲DatagramSocket API
网络编程, 本质上是要操作网卡.
但是网卡不方便直接操作. 在操作系统内核中, 使用了一种特殊的叫做 “socket” 这样的文件来抽象表示网卡.
因此进行网络通信, 势必需要先有一个 socket 对象.
DatagramSocket 是UDP Socket,用于发送和接收UDP数据报
DatagramSocket 构造方法:
方法签名 | 方法说明 |
---|---|
DatagramSocket() | 创建一个UDP数据报套接字的Socket,绑定到本机任意一个随机端口(一般用于客户端) |
DatagramSocket(int port) | 创建一个UDP数据报套接字的Socket,绑定到本机指定的端口(一般用于服务端) |
对于服务器来说, 创建 socket 对象的同时, 要让他绑定上一个具体的端口号.
服务器一定要关联上一个具体的端口的!!!
服务器是网络传输中, 被动的一方. 如果是操作系统随机分配的端口, 此时客户端就不知道这个端口是啥了, 也就无法进行通信了!!!
而做为客户端,可能客户并不知道自己那个端口什么时候空闲,如果强行指定,则可能会出现问题,所以我们选择绑定任意随机端口。
举个例子吧:
比如开饭店,饭店就像一个服务器,它开在哪儿基本上就会在哪儿,如果随便移动,那么客户可能就找不到饭店了,这样一来饭店不久黄了吗?所以饭店应该有个固定的地址。服务器地端口号也应该指定一下
而我们吃饭的人就像客户端,每次去吃饭,我们都会拿一个号,或者坐一张桌子等待上菜,但是由于每天客流量不同,去的时间不同,所以我们每次去的时候都拿的号和坐的桌子都可能不同。
DatagramSocket还提供了以下几种方法供我们使用
DatagramSocket 方法:
方法签名 | 方法说明 |
---|---|
void receive(DatagramPacket p) | 从此套接字接收数据报(如果没有接收到数据报,该方法会阻塞等待) |
void send(DatagramPacketp) | 从此套接字发送数据报包(不会阻塞等待,直接发送) |
void close() | 关闭此数据报套接字 |
🎄DatagramPacket API
DatagramPacket是UDP Socket发送和接收的数据报
DatagramPacket 构造方法:
方法签名 | 方法说明 |
---|---|
DatagramPacket(byte[] buf, int length) | 构造一个DatagramPacket以用来接收数据报,接收的数据保存在字节数组(第一个参数buf)中,接收指定长度(第二个参数length) |
DatagramPacket(byte[] buf, int offset, int length,SocketAddress address) | 构造一个DatagramPacket以用来发送数据报,发送的数据为字节数组(第一个参数buf)中,从0到指定长度(第二个参数length)。address指定目的主机的IP和端口号 |
这个就相当于你去食堂吃饭需要拿个盘子装饭一样,这个就是拿来装数据报的
DatagramSocket也提供了以下几种方法供我们使用
DatagramPacket 方法:
方法签名 | 方法说明 |
---|---|
InetAddress getAddress() | 从接收的数据报中,获取发送端主机IP地址;或从发送的数据报中,获取接收端主机IP地址 |
int getPort() | 从接收的数据报中,获取发送端主机的端口号;或从发送的数据报中,获取接收端主机端口号 |
byte[] getData() | 获取数据报中的数据 |
getSocketAddress() | 获取发送方的IP地址与端口号 |
构造UDP发送的数据报时,需要传入 SocketAddress ,该对象可以使用 InetSocketAddress 来创建。
🍀InetSocketAddress API
InetSocketAddress ( SocketAddress 的子类 )构造方法:
方法签名 | 方法说明 |
---|---|
InetSocketAddress(InetAddress addr, int port) | 创建一个Socket地址,包含IP地址和端口 |
🎍回显客户端与服务器
什么叫回显客户端与与服务器呢?
其实就是:客户端向服务端发送请求,一般来说我们的服务端会对我们发送的请求进行处理,我们这里为了简单,就省略里面的处理过程,只实现将请求重新发回客户端,不做任何处理。
📌建立回显服务器
我们大致可以分为以下几步
-
创建一个类,为UdpEchoServer,来表示我们的UDP 版本的回显服务器
-
创建一个socket对象,来操作网卡
-
在UdpEchoServe类的构造方法里对socket对象进行构造
-
建立start()方法用于启动我们的服务器
-
由于我们的服务器需要长期开着,所以我们设计一个死循环
-
构造好个空白的 DatagramPacket 对象. 交给 receive 来进行填充
-
DatagramPacket 是一个特殊的对象, 并不方便直接进行处理. 可以把这里包含的数据拿出来, 构造成一个字符串.
-
创建一个process()方法,这个方法就表示 “根据请求计算响应”
-
把响应写回到客户端. send 的参数也是 DatagramPacket. 需要把这个 Packet 对象构造好.此处构造的响应对象, 不能是用空的字节数组构造了, 而是要使用响应数据来构造
-
我们加一个打印一下,当前这次请求响应的处理中间结果.
-
最后我们创建main()函数进行启动,创建UdpEchoServer对象传入端口号,端口号为自己指定的,只要在1024 -> 65535 这个范围里随便挑个数字就行了
接下来我们一起来看看代码的实现:
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());
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 {
// 端口号的指定, 大家可以随便指定.
// 1024 -> 65535 这个范围里随便挑个数字就行了.
UdpEchoServer server = new UdpEchoServer(9090);
server.start();
}
}
这时候我们启动,启动结果如下:
📌回显客户端
我们分为以下几步:
-
我们创建一个类UdpEchoClient来表示客户端服务。
-
创建socket对象,来操作网卡
-
定义两个变量,一个表示服务器的IP地址,一个表示端口号
-
在UdpEchoClient的构造方法里进行构造
-
构造成 UDP 请求, 并发送,构造这个 Packet 的时候, 需要把 serverIp 和 port 都传入过来. 但是此处 IP 地址需要填写的是一个 32位的整数形式.上述的 IP 地址是一个字符串. 需要使用 InetAddress.getByName 来进行一个转换.
-
读取服务器的 UDP 响应, 并解析,构造好个空白的 DatagramPacket 对象. 交给 receive 来进行填充
-
把解析好的结果显示出来.
-
创建main函数,创造UdpEchoClient对象,并把服务器的IP和端口给传进去
-
启动服务器
代码实现如下:
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", 9090);
client.start();
}
}
结果展示:
📌成果展示
客户端发送:遇事问春风乄
客户端展示:
服务器展示;
🎋翻译小助手服务器与客户端的简单建立
📌服务器的建立
这里的服务器大致步骤与回显服务器相似,不同的在于相应程序上面,这里我们就直接继承上面的类,然后重写一下process()方法
因为要实现一个小翻译词典,所以我们使用一个Map储存一些数据,方便于我们查询
代码实现如下:
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("fuck", "卧槽");
// 当然, 这里可以无限多的设置键值对.....
}
@Override
public String process(String request) {
// 查词典的过程.
return dict.getOrDefault(request, "当前单词没有查到结果!");
}
public static void main(String[] args) throws IOException {
UdpDictServer server = new UdpDictServer(9090);
server.start();
}
}
📌客户端的建立
客户端与上面一模一样,这里就不写
📌结果展示
客户端
输入:cat
服务器
🌳多个客户端对一个服务器
博主这里只讲IDEA编译器的
首先在我们得客户端页面点击以下
再点击
然后进行勾选
最后我们只需要多次启动客户端,我们就会发现每一次的客户端都是不一样的
结果展示如下:
⭕总结
关于《【JavaEE初阶】 UDP服务器与客户端的搭建》就讲解到这儿,感谢大家的支持,欢迎各位留言交流以及批评指正,如果文章对您有帮助或者觉得作者写的还不错可以点一下关注,点赞,收藏支持一下!