【JavaEE初阶】 UDP服务器与客户端的搭建

简介: 【JavaEE初阶】 UDP服务器与客户端的搭建


🌳前言

我们用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地址和端口

🎍回显客户端与服务器

什么叫回显客户端与与服务器呢?

其实就是:客户端向服务端发送请求,一般来说我们的服务端会对我们发送的请求进行处理,我们这里为了简单,就省略里面的处理过程,只实现将请求重新发回客户端,不做任何处理。

📌建立回显服务器

我们大致可以分为以下几步

  1. 创建一个类,为UdpEchoServer,来表示我们的UDP 版本的回显服务器
  2. 创建一个socket对象,来操作网卡
  3. 在UdpEchoServe类的构造方法里对socket对象进行构造
  4. 建立start()方法用于启动我们的服务器
  5. 由于我们的服务器需要长期开着,所以我们设计一个死循环
  6. 构造好个空白的 DatagramPacket 对象. 交给 receive 来进行填充
  7. DatagramPacket 是一个特殊的对象, 并不方便直接进行处理. 可以把这里包含的数据拿出来, 构造成一个字符串.
  8. 创建一个process()方法,这个方法就表示 “根据请求计算响应”
  9. 把响应写回到客户端. send 的参数也是 DatagramPacket. 需要把这个 Packet 对象构造好.此处构造的响应对象, 不能是用空的字节数组构造了, 而是要使用响应数据来构造
  10. 我们加一个打印一下,当前这次请求响应的处理中间结果.
  11. 最后我们创建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();
    }
}

这时候我们启动,启动结果如下:

📌回显客户端

我们分为以下几步:

  1. 我们创建一个类UdpEchoClient来表示客户端服务。
  2. 创建socket对象,来操作网卡
  3. 定义两个变量,一个表示服务器的IP地址,一个表示端口号
  4. 在UdpEchoClient的构造方法里进行构造
  5. 构造成 UDP 请求, 并发送,构造这个 Packet 的时候, 需要把 serverIp 和 port 都传入过来. 但是此处 IP 地址需要填写的是一个 32位的整数形式.上述的 IP 地址是一个字符串. 需要使用 InetAddress.getByName 来进行一个转换.
  6. 读取服务器的 UDP 响应, 并解析,构造好个空白的 DatagramPacket 对象. 交给 receive 来进行填充
  7. 把解析好的结果显示出来.
  8. 创建main函数,创造UdpEchoClient对象,并把服务器的IP和端口给传进去
  9. 启动服务器

代码实现如下:

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服务器与客户端的搭建》就讲解到这儿,感谢大家的支持,欢迎各位留言交流以及批评指正,如果文章对您有帮助或者觉得作者写的还不错可以点一下关注,点赞,收藏支持一下!

相关文章
|
2月前
|
消息中间件 运维 网络协议
客户端和服务器之间的通信
客户端和服务器之间的通信
34 0
|
20天前
|
网络协议 Python
pythonTCP客户端编程连接服务器
【4月更文挑战第6天】本教程介绍了TCP客户端如何连接服务器,包括指定服务器IP和端口、发送连接请求、处理异常、进行数据传输及关闭连接。在Python中,使用`socket`模块创建Socket对象,然后通过`connect()`方法尝试连接服务器 `(server_ip, server_port)`。成功连接后,利用`send()`和`recv()`进行数据交互,记得在通信完成后调用`close()`关闭连接,确保资源释放和程序稳定性。
|
19天前
|
Python
Python网络编程基础(Socket编程)UDP服务器编程
【4月更文挑战第8天】Python UDP服务器编程使用socket库创建UDP套接字,绑定到特定地址(如localhost:8000),通过`recvfrom`接收客户端数据报,显示数据长度、地址和内容。无连接的UDP协议使得服务器无法主动发送数据,通常需应用层实现请求-响应机制。当完成时,用`close`关闭套接字。
|
2月前
|
网络协议 Java API
【JavaEE初阶】 TCP服务器与客户端的搭建
【JavaEE初阶】 TCP服务器与客户端的搭建
|
1月前
|
Ubuntu JavaScript 关系型数据库
在阿里云Ubuntu 20.04服务器中搭建一个 Ghost 博客
在阿里云Ubuntu 20.04服务器上部署Ghost博客的步骤包括创建新用户、安装Nginx、MySQL和Node.js 18.x。首先,通过`adduser`命令创建非root用户,然后安装Nginx和MySQL。接着,设置Node.js环境,下载Nodesource GPG密钥并安装Node.js 18.x。之后,使用`npm`安装Ghost-CLI,创建Ghost安装目录并进行安装。配置过程中需提供博客URL、数据库连接信息等。最后,测试访问前台首页和后台管理页面。确保DNS设置正确,并根据提示完成Ghost博客的配置。
在阿里云Ubuntu 20.04服务器中搭建一个 Ghost 博客
|
1月前
|
存储 弹性计算 数据可视化
要将ECS中的文件直接传输到阿里云网盘与相册(
【2月更文挑战第31天】要将ECS中的文件直接传输到阿里云网盘与相册(
420 4
|
1月前
|
SQL 弹性计算 安全
购买阿里云活动内云服务器之后设置密码、安全组、增加带宽、挂载云盘教程
当我们通过阿里云的活动购买完云服务器之后,并不是立马就能使用了,还需要我们设置云服务器密码,配置安全组等基本操作之后才能使用,有的用户还需要购买并挂载数据盘到云服务器上,很多新手用户由于是初次使用阿里云服务器,因此并不知道这些设置的操作流程,下面给大家介绍下这些设置的具体操作流程。
购买阿里云活动内云服务器之后设置密码、安全组、增加带宽、挂载云盘教程
|
22天前
|
弹性计算
阿里云ECS使用体验
在申请高校学生免费体验阿里云ECS云服务器后的一些使用体验和感受。
|
1月前
|
弹性计算
阿里云3M带宽云服务器并发多大?阿里云3M带宽云服务器测评参考
在探讨云服务器3M带宽能支持多大并发这一问题时,我们首先要明白一个关键点:并发量并非仅由带宽决定,还与网站本身的大小密切相关。一般来说,一个优化良好的普通网站页面大小可能只有几K,为便于计算,我们可以暂且假定每个页面大小为50K。
822 1
|
2天前
|
弹性计算 运维 安全
阿里云ecs使用体验
整了台服务器部署项目上线