Java UDP通信案例如何实现

wen java案例 23

Java UDP通信案例实现

UDP(User Datagram Protocol)是一种无连接的传输层协议,适合实时性要求高但可以容忍少量丢包的应用场景。

Java UDP通信案例如何实现

基础UDP通信实现

UDP发送端(客户端)

import java.net.*;
public class UDPSender {
    public static void main(String[] args) {
        DatagramSocket socket = null;
        try {
            // 创建UDP套接字
            socket = new DatagramSocket();
            // 要发送的数据
            String message = "Hello, UDP Server!";
            byte[] sendData = message.getBytes();
            // 指定接收方地址和端口
            InetAddress address = InetAddress.getByName("localhost");
            int port = 8888;
            // 创建数据报包
            DatagramPacket packet = new DatagramPacket(
                sendData, 
                sendData.length, 
                address, 
                port
            );
            // 发送数据
            socket.send(packet);
            System.out.println("数据已发送: " + message);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (socket != null && !socket.isClosed()) {
                socket.close();
            }
        }
    }
}

UDP接收端(服务器)

import java.net.*;
public class UDPReceiver {
    public static void main(String[] args) {
        DatagramSocket socket = null;
        try {
            // 创建UDP套接字并绑定到指定端口
            socket = new DatagramSocket(8888);
            System.out.println("UDP服务器已启动,等待接收数据...");
            while (true) {
                // 创建接收缓冲区
                byte[] receiveData = new byte[1024];
                DatagramPacket packet = new DatagramPacket(receiveData, receiveData.length);
                // 接收数据(阻塞方法)
                socket.receive(packet);
                // 提取数据
                String message = new String(
                    packet.getData(), 
                    0, 
                    packet.getLength()
                );
                // 获取发送方信息
                InetAddress senderAddress = packet.getAddress();
                int senderPort = packet.getPort();
                System.out.println("收到来自 " + senderAddress.getHostAddress() + 
                                 ":" + senderPort + " 的数据: " + message);
                // 可选:回复确认消息
                String response = "消息已收到: " + message;
                byte[] responseData = response.getBytes();
                DatagramPacket responsePacket = new DatagramPacket(
                    responseData, 
                    responseData.length, 
                    senderAddress, 
                    senderPort
                );
                socket.send(responsePacket);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (socket != null && !socket.isClosed()) {
                socket.close();
            }
        }
    }
}

双向通信示例

双向通信客户端

import java.net.*;
import java.util.Scanner;
public class UDPClient {
    public static void main(String[] args) {
        DatagramSocket socket = null;
        Scanner scanner = new Scanner(System.in);
        try {
            socket = new DatagramSocket();
            InetAddress serverAddress = InetAddress.getByName("localhost");
            int serverPort = 9999;
            // 启动接收线程
            Thread receiveThread = new Thread(() -> {
                try {
                    while (true) {
                        byte[] receiveData = new byte[1024];
                        DatagramPacket packet = new DatagramPacket(
                            receiveData, 
                            receiveData.length
                        );
                        socket.receive(packet);
                        String message = new String(
                            packet.getData(), 
                            0, 
                            packet.getLength()
                        );
                        System.out.println("\n服务器回复: " + message);
                        System.out.print("请输入消息: ");
                    }
                } catch (Exception e) {
                    // 套接字关闭时退出
                }
            });
            receiveThread.start();
            // 发送数据
            while (true) {
                System.out.print("请输入消息 (输入'quit'退出): ");
                String message = scanner.nextLine();
                if ("quit".equalsIgnoreCase(message)) {
                    break;
                }
                byte[] sendData = message.getBytes();
                DatagramPacket packet = new DatagramPacket(
                    sendData, 
                    sendData.length, 
                    serverAddress, 
                    serverPort
                );
                socket.send(packet);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (socket != null && !socket.isClosed()) {
                socket.close();
            }
            scanner.close();
        }
    }
}

双向通信服务器

import java.net.*;
import java.util.concurrent.ConcurrentHashMap;
public class UDPServer {
    private static ConcurrentHashMap<String, ClientInfo> clients = new ConcurrentHashMap<>();
    public static void main(String[] args) {
        DatagramSocket socket = null;
        try {
            socket = new DatagramSocket(9999);
            System.out.println("UDP服务器已启动,监听端口: 9999");
            while (true) {
                byte[] receiveData = new byte[1024];
                DatagramPacket packet = new DatagramPacket(receiveData, receiveData.length);
                socket.receive(packet);
                // 处理接收到的数据
                String message = new String(
                    packet.getData(), 
                    0, 
                    packet.getLength()
                );
                InetAddress clientAddress = packet.getAddress();
                int clientPort = packet.getPort();
                String clientKey = clientAddress.getHostAddress() + ":" + clientPort;
                // 保存客户端信息
                clients.put(clientKey, new ClientInfo(clientAddress, clientPort));
                System.out.println("收到客户端 [" + clientKey + "] 消息: " + message);
                // 处理广播或单播
                if (message.startsWith("@")) {
                    // 私聊功能
                    String[] parts = message.split(" ", 3);
                    if (parts.length >= 3) {
                        String targetKey = parts[0].substring(1);
                        String privateMessage = parts[2];
                        sendToClient(socket, targetKey, privateMessage);
                    }
                } else {
                    // 广播消息
                    broadcast(socket, clientKey, message);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (socket != null && !socket.isClosed()) {
                socket.close();
            }
        }
    }
    // 发送消息给指定客户端
    private static void sendToClient(DatagramSocket socket, String clientKey, String message) {
        ClientInfo client = clients.get(clientKey);
        if (client != null) {
            try {
                byte[] data = message.getBytes();
                DatagramPacket packet = new DatagramPacket(
                    data, 
                    data.length, 
                    client.address, 
                    client.port
                );
                socket.send(packet);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    // 广播消息给所有客户端
    private static void broadcast(DatagramSocket socket, String sender, String message) {
        String broadcastMessage = sender + ": " + message;
        byte[] data = broadcastMessage.getBytes();
        clients.forEach((key, client) -> {
            try {
                DatagramPacket packet = new DatagramPacket(
                    data, 
                    data.length, 
                    client.address, 
                    client.port
                );
                socket.send(packet);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }
    // 客户端信息类
    static class ClientInfo {
        InetAddress address;
        int port;
        ClientInfo(InetAddress address, int port) {
            this.address = address;
            this.port = port;
        }
    }
}

使用多线程处理多个客户端

import java.net.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class UDPThreadServer {
    private static final int PORT = 7777;
    private static ExecutorService threadPool = Executors.newFixedThreadPool(10);
    public static void main(String[] args) {
        DatagramSocket socket = null;
        try {
            socket = new DatagramSocket(PORT);
            System.out.println("多线程UDP服务器已启动");
            while (true) {
                byte[] receiveData = new byte[1024];
                DatagramPacket packet = new DatagramPacket(receiveData, receiveData.length);
                socket.receive(packet);
                // 将处理任务交给线程池
                threadPool.execute(new ClientHandler(socket, packet));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (socket != null && !socket.isClosed()) {
                socket.close();
            }
            threadPool.shutdown();
        }
    }
    // 客户端处理器
    static class ClientHandler implements Runnable {
        private DatagramSocket socket;
        private DatagramPacket packet;
        public ClientHandler(DatagramSocket socket, DatagramPacket packet) {
            this.socket = socket;
            this.packet = packet;
        }
        @Override
        public void run() {
            try {
                // 解析接收到的数据
                String message = new String(
                    packet.getData(), 
                    0, 
                    packet.getLength()
                );
                System.out.println("处理来自 " + 
                    packet.getAddress().getHostAddress() + " 的消息: " + message);
                // 处理业务逻辑...
                String response = "处理完成: " + message.toUpperCase();
                // 发送响应
                byte[] responseData = response.getBytes();
                DatagramPacket responsePacket = new DatagramPacket(
                    responseData, 
                    responseData.length, 
                    packet.getAddress(), 
                    packet.getPort()
                );
                socket.send(responsePacket);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

UDP通信的注意事项

数据大小限制

// UDP数据包最大为65507字节(64KB减去UDP和IP头)
// 实际应用中建议控制在1472字节以内(避免IP分片)
int MAX_UDP_PACKET_SIZE = 1472;
byte[] data = new byte[MAX_UDP_PACKET_SIZE];

超时设置

// 设置接收超时
socket.setSoTimeout(3000); // 3秒超时
try {
    socket.receive(packet);
} catch (SocketTimeoutException e) {
    System.out.println("接收超时");
}

端口复用

// 允许端口复用
socket.setReuseAddress(true);

广播地址

// 发送广播消息
socket.setBroadcast(true);
InetAddress broadcastAddress = InetAddress.getByName("255.255.255.255");
// 或者获取子网广播地址
// InetAddress broadcastAddress = InetAddress.getByName("192.168.1.255");

UDP vs TCP 选择建议

/*
UDP适用场景:
1. 实时性要求高(视频通话、游戏)
2. 容忍少量丢包
3. 简单查询-响应模式
4. 多播或广播通信
TCP适用场景:
1. 数据完整性要求高
2. 大量数据传输
3. 需要可靠连接
4. 文件传输、网页浏览
*/

运行示例

  1. 先运行UDPReceiver或UDPServer
  2. 再运行UDPSender或UDPClient
  3. 观察控制台输出

这个案例提供了UDP通信的完整实现,包括单向通信、双向通信、多客户端处理和线程池应用,你可以根据实际需求选择和修改相应的代码。

抱歉,评论功能暂时关闭!