JAVA异步的UDP 通讯-客户端

1. 使用DatagramSocket的非阻塞模式

Java的DatagramSocket默认是阻塞模式,但可以通过设置Socket选项来启用非阻塞模式。这样可以在发送和接收数据时避免线程阻塞

java 复制代码
import java.net.DatagramSocket;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.StandardSocketOptions;

public class AsyncUDPClient {
    public static void main(String[] args) throws Exception {
        DatagramSocket socket = DatagramSocket.create();
        socket.setOption(StandardSocketOptions.SO_REUSEADDR, true); // 允许端口复用
        socket.connect(InetAddress.getByName("localhost"), 12345); // 连接到服务器
        socket.setSoTimeout(1000); // 设置超时时间

        // 发送数据
        String message = "Hello, UDP Server!";
        byte[] data = message.getBytes();
        DatagramPacket packet = new DatagramPacket(data, data.length);
        socket.send(packet);

        // 接收响应
        byte[] receiveBuffer = new byte[1024];
        DatagramPacket receivePacket = new DatagramPacket(receiveBuffer, receiveBuffer.length);
        socket.receive(receivePacket);
        String response = new String(receivePacket.getData(), 0, receivePacket.getLength());
        System.out.println("Received from server: " + response);

        socket.close();
    }
}

2. 使用线程池实现异步处理

通过线程池来处理UDP数据的发送和接收,可以避免阻塞主线程,提高程序的响应性。

java 复制代码
import java.net.DatagramSocket;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class AsyncUDPClientWithThreadPool {
    private static final ExecutorService executor = Executors.newFixedThreadPool(4);

    public static void main(String[] args) throws Exception {
        DatagramSocket socket = new DatagramSocket();
        socket.connect(InetAddress.getByName("localhost"), 12345);

        // 异步发送数据
        executor.submit(() -> {
            try {
                String message = "Hello, UDP Server!";
                byte[] data = message.getBytes();
                DatagramPacket packet = new DatagramPacket(data, data.length);
                socket.send(packet);
                System.out.println("Message sent to server.");
            } catch (Exception e) {
                e.printStackTrace();
            }
        });

        // 异步接收数据
        executor.submit(() -> {
            try {
                byte[] receiveBuffer = new byte[1024];
                DatagramPacket receivePacket = new DatagramPacket(receiveBuffer, receiveBuffer.length);
                socket.receive(receivePacket);
                String response = new String(receivePacket.getData(), 0, receivePacket.getLength());
                System.out.println("Received from server: " + response);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });

        executor.shutdown();
        socket.close();
    }
}

3. 使用Selector实现多路复用

Selector可以用于同时监控多个DatagramSocket,从而实现更高效的异步通信。

java 复制代码
import java.net.DatagramSocket;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.ByteBuffer;

public class AsyncUDPClientWithSelector {
    public static void main(String[] args) throws Exception {
        DatagramChannel channel = DatagramChannel.open();
        channel.configureBlocking(false);
        channel.connect(InetAddress.getByName("localhost"), 12345);

        Selector selector = Selector.open();
        channel.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ);

        while (selector.select() > 0) {
            for (SelectionKey key : selector.selectedKeys()) {
                if (key.isWritable()) {
                    String message = "Hello, UDP Server!";
                    byte[] data = message.getBytes();
                    DatagramPacket packet = new DatagramPacket(data, data.length);
                    channel.send(ByteBuffer.wrap(data), channel.socket().getRemoteSocketAddress());
                    System.out.println("Message sent to server.");
                }

                if (key.isReadable()) {
                    byte[] receiveBuffer = new byte[1024];
                    DatagramPacket receivePacket = new DatagramPacket(receiveBuffer, receiveBuffer.length);
                    channel.receive(receivePacket);
                    String response = new String(receivePacket.getData(), 0, receivePacket.getLength());
                    System.out.println("Received from server: " + response);
                }

                selector.selectedKeys().remove(key);
            }
        }

        channel.close();
        selector.close();
    }
}

4. 设置合理的超时和缓冲区大小

通过设置setSoTimeoutsetReceiveBufferSize等参数,可以优化UDP客户端的性能。

java 复制代码
socket.setSoTimeout(1000); // 设置接收超时时间
socket.setReceiveBufferSize(8192); // 设置接收缓冲区大小
socket.setSendBufferSize(8192); // 设置发送缓冲区大小
相关推荐
咩咩啃树皮17 小时前
第40篇:Vue3组件化开发精讲——组件拆分、复用、父子通信、工程化架构
java·前端·架构
灯澜忆梦18 小时前
GO_并发编程---定时器
开发语言·后端·golang
鱟鲥鳚18 小时前
Spring Boot 集成 LangChain4j:从模型调用到 Tool Calling(Demo版)
java·spring boot
-银雾鸢尾-18 小时前
C#中的StringBuilder相关方法
开发语言·c#
-银雾鸢尾-18 小时前
C#中结构体与类的区别;抽象类与接口的区别
开发语言·c#
大模型码小白19 小时前
【Python零基础教程】继承、多态与魔法函数:面向对象编程三大核心特性详解
java·大数据·开发语言·人工智能·python·ai编程
腾渊信息科技公司20 小时前
Spring Boot对接MES实战:视觉检测数据自动同步方案
java·人工智能·spring boot·后端·计算机视觉·ai·软件需求
爱笑的源码基地21 小时前
高并发 Redis 缓存门诊HIS系统源码,含财务统计药房进销存
java·程序·门诊系统·诊所系统·云诊所源码
wuqingshun31415921 小时前
TCP超时重传机制是为了解决什么问题?
java
段一凡-华北理工大学21 小时前
向量数据库实战:选型、调优与落地~系列文章12:文本分块策略实战:chunk_size 怎么选?重叠多少?
开发语言·数据库·后端·oracle·rust·工业智能体·高炉智能化