Java网络编程-第三节:UDP数据报套接字(DatagramSocket)编程

Posted 快乐江湖

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java网络编程-第三节:UDP数据报套接字(DatagramSocket)编程相关的知识,希望对你有一定的参考价值。

文章目录

一:Java数据报套接字通信模型

Java UDP通信模型:Java中使用UDP协议通信,主要依靠一下两个类

  • DatagramSocket:创建UDP Socket
  • DatagramPacket:是UDP Socket发送和接受的数据报

一次发送和接收UDP数据报流程如下

多个请求流程如下

二:相关API详解

(1)DatagramSocket

DatagramSocket:用于构造UDP Socket,以发送和接收UDP数据报

构造方法如下

方法签名方法说明
DatagramSocket()创建一个UDP数据报 Socket,绑定到本机任意一个随机端口 ,一般用于客户端
DatagramSocket(int port)创建一个UDP数据报 Socket,绑定到本机任意指定端口 ,一般用于服务端

成员方法如下

方法签名方法说明
void receive(DatagramPacket p)从该Socket中接收数据报(如果未收到则会阻塞等待
void send(DatagramPacket p)由该Socket发送数据报(不会阻塞等待,直接发送
void close()关闭此Socket

(2)DatagramPacket

DatagramPacket:是UDP Socket发送和接收的数据报

构造方法如下

方法签名方法说明
DatagramPacket(byet[] buf, int length)用于接收数据报,接收的数据保存在buf数组中,length指定接收长度
DatagramPacket(byet[] buf, int offset, int length, SocketAddress address)用于发送数据报,发送的数据为buf数组,范围为从0到lengthaddress是目的主机的IP和端口号

成员方法如下

方法签名方法说明
InetAddress getAddress()从接收的数据报中获取发送端主机IP地址;或从发送的数据报中获取接收端主机IP地址
int getPort()从接收的数据报中获取发送端主机端口号;或从发送的数据报中获取接收端主机端口号
bye[] getData()获取数据报中的数据

三:UDP通信示例一:客户端发送什么服务端就返回什么

  • 注意:这个功能比较简单,但主要目的是为了演示上面所讲API的用法

(1)代码

服务端IP地址设置为127.0.0.1,也即本地环回,也即自己发自己收,数据会完整走一遍协议

服务端:UDPServer

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class UDPServer 
    // 构造DatagramSocket
    private DatagramSocket socket = null;

    public UDPServer(int port) throws SocketException 
        // 服务端需要绑定一个端口号
        socket = new DatagramSocket(port);
    

    // 服务端启动
    public void start() throws IOException 
        System.out.println("服务器启动!");
        // 服务端随时等待接收客户端请求
        while (true) 
            /*
                 1. 接受请求并解析
                    构造一个DatagramPacket对象requestPacket用于接受客户端发来的数据报,保存在byte[]
                    为了方便查看信息,需要使用 requestPacket的getData()方法将其转换为String
             */
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);
            socket.receive(requestPacket);
            String request = new String(requestPacket.getData(), 0, requestPacket.getLength());
            /*
                2. 拿到客户端的请求request后,将其交给一个方法process进行处理,process方法会返回响应response
             */
            String response = process(request);
            /*
                3. 将响应回复给客户端
                   构造一个DatagramPacket对象responsePacket用于给客户端回复响应response ,response是String
                   所以需要使用用getBytes()方法将其转化为byte[]
                   客户端发来的requestPacket数据报中携带有客户端的Ip地址和端口号,使用getSocketAddress()获得
             */
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length,
                    requestPacket.getSocketAddress());
            socket.send(responsePacket);

            // 打印信息
            System.out.println("【客户端IP: " + requestPacket.getAddress().toString()
                    + "客户端口号:" + requestPacket.getPort() + "】"
                    + ":\\"" + request + "\\"" + ", 服务端回复: " + "\\"" + response + "\\"");
        
    

    // 回显服务器,客户端发什么服务端就回复什么
    public String process(String request) 
        return request;
    

    public static void main(String[] args) throws IOException 
        // 服务端监听9090端口
        UDPServer server = new UDPServer(9090);
        server.start();
    


客户端:UDPClient

import java.io.IOException;
import java.net.*;
import java.util.Scanner;

public class UDPClient 
    DatagramSocket socket = null;
    // 客户端需要指定好服务端的IP和端口号,然后构建一个Socket地址
    private SocketAddress ADDRESS = null;

    public UDPClient(String serverIP, int serverPort) throws SocketException 
        ADDRESS = new InetSocketAddress(serverIP, serverPort);
        socket = new DatagramSocket();
    

    // 客户端启动
    public void start() throws IOException 
        Scanner scanner = new Scanner(System.in);
        while (true) 
            // 1. 读取客户端用户输入
            System.out.print("input: ");
            String request = scanner.next();

            /*
                2. 发送请求给服务端
                   构造一个DatagramPacket对象requestPacket用于给服务端发送数据报,注意需要将String转换为byte[]
                   同时传入服务端的ADDRESS(ip地址和port)
             */
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,
                   ADDRESS);
            socket.send(requestPacket);

            // 3. 从服务端获取响应
            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 
        UDPClient client = new UDPClient("127.0.0.1", 9090);
        client.start();
    


(2)效果展示

一个服务端一个客户端


一个服务端多个客户端

注意如果需要运行多个UDPClient实例,需要在Run/Debug Configurations中选中Allow multiple instances

这里启动三个客户端

(3)分析

对于服务端(UDPServer类)

  • 构造方法( public UDPServer(int port)

    • 需要建立一个DatagramSocket并绑定一个指定的端口号port,也即 DatagramSocket socket =new DatagramSocket(port)。服务端绑定端口号的原因是因为程序运行在服务器上是确定的、可控的
  • 服务端处理逻辑(public void start()

    • ①:接受请求并解析

      • 需要构造一个DatagramPacket用于接受客户端发来的数据报,会保存在byte[]中,也即DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096)
      • 接受行为由socket完成,也即socket.receive(requestPacket)
      • 接受到的requestPacket 其类型为byte[],所以需要把它转为String以便于查看,也即String request = new String(requestPacket.getData(), 0, requestPacket.getLength())
    • ②:处理请求

      • 拿到解析后的request后,需要对该request进行处理(交给方法process),不同的业务逻辑会有不同的处理方法。这里我们只是简单的“回显”一下即可,也即客户端发什么服务端就回复什么
    • ③:将处理结果(响应)回复给客户端

      • 需要构造一个DatagramPacket用于给客户端回复响应response,在构造时要将类型为Stringrespnse转化为byte[],同时使用requestPacket.getSocketAddress()获取到客户端的IPport(因为服务端总要知道客户端的具体地址才能发送),也即DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length, requestPacket.getSocketAddress())
      • 发送行为由socket完成,也即socket.send(responsePacket);
    • ④:打印相关信息

  • main方法

    • 构造UDPServer对象,并绑定指定端口号,如9090,也即UDPServer server = new UDPServer(9090)
    • 启动服务端,也即server.start()

对于客户端(UDPClient类):

  • 构造方法(public UDPClient(String serverIP, int serverPort)

    • 需要建立一个DatagramSocket,但不指定端口号,也即DatagramSocket socket =new DatagramSocket()。客户端无需绑定端口号是因为客户端上运行状况复杂,端口号占用情况各不相同,无法保证所指定的端口号在数据报来临时一定是空闲的,所以这里让系统自动指定空闲端口号即可

    • 需要建立一个InetSocketAddress,传入服务端serverIPserverPort,也即SocketAddress ADDRESS = new InetSocketAddress(serverIP, serverPort),这里的serverPort就是服务端中所指定的那个port。之所以这样做是因为客户端发送时必须要知道服务端的IP地址和port

      • InetSocketAddressSocketAddress的子类
  • 客户端处理逻辑(public void start()

    • ①:读取客户端中用户的输入

      • 使用Scanner接受即可,也即String request = scanner.next()
    • ②:发送请求给服务端

      • 需要构造一个DatagramPacket给服务端发送数据报,注意需要将类型Stringrequest转化为byte[],同时传入服务端的ADDRESS。也即DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length, ADDRESS);
      • 发送行为由socket完成,也即socket.send(requestPacket)
    • ③:从服务端获取响应

      • 需要构造一个DatagramPacket用于接受服务端端发来的数据报,会保存在byte[]中,也即DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096)
      • 接受行为由socket完成,也即socket.receive(responsePacket)
      • 接受到的responsePacket 其类型为byte[],所以需要把它转为String以便于查看,也即String response = new String(responsePacket.getData(), 0, resoibsePacket.getLength())
    • ④:打印相关信息

  • main方法

    • 构造UDPClient对象,并给定服务端IPport,也即 UDPClient client = new UDPClient("127.0.0.1", 9090)
    • 启动客户端,也即client.start()

四:UDP通信示例二:实现简单的翻译功能

一个服务器通信的基本流程就是上面所展示的那样,最核心的区别在于“业务处理逻辑”不同,这里我们对客户端进行改造,让其实现一个简单的“英翻汉”功能

  • 词典词汇量虽然屈指可数,但整体逻辑就像下面演示的那样,当然你也可以调用一些翻译API接口(例如百度)

(1)代码

客户端:UDPTranslateClient 让此类继承上面的UDPClient然后重写 process方法即可

import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;

public class UDPTranslateServer extends UDPServer
    // 查询字典
    private Map<String, String> dict = new HashMap<>();
    public UDPTranslateServer(int port) throws SocketException 
        super(port);
        dict.put("cat", "猫");
        dict.put("dog", "狗");
        dict.put("pig", "猪");
        dict.put("wolf", "狼");

    

    // 重写process方法实现查询词典功能
    @Override
    public String process(String request) 
        return dict.getOrDefault(request, "没有这个词");
    

    public static void main(String[] args) throws IOException 
        UDPTranslateServer udpTranslateServer = new UDPTranslateServer(9090);
        udpTranslateServer.start();
    


(2)效果展示

以上是关于Java网络编程-第三节:UDP数据报套接字(DatagramSocket)编程的主要内容,如果未能解决你的问题,请参考以下文章

传输层-第三节:TCP和UDP对比

Java网络编程之UDP网络编程

传输层-第三节:TCP和UDP对比

Java-UDP Socket编程

网络层-第三节1:IP数据报格式及分片

Python之路(第三十二篇) 网络编程:udp套接字简单文件传输