首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java NIO UDP 編程實戰:基於 Selector 的無連接 Echo 服務端與客戶端

     編輯:Java 相關知識

UDP 與 TCP 的 NIO 差異

  • 無連接:無 OP_ACCEPT,單 DatagramChannel 收發所有數據
  • 基於數據報:每次讀寫一個完整包,附帶遠端 SocketAddress
  • 狀態管理:需自行維護客戶端地址與緩衝區映射

協議接口復用

同樣的 EchoProtocol 接口,UDP 實現邏輯完全不同:

public class UDPEchoSelectorProtocol implements EchoProtocol {
    private static final int ECHOMAX = 255;
    // 客戶端上下文:地址 + 緩衝區
    static class ClientRecord {
        SocketAddress clientAddress;
        ByteBuffer buffer = ByteBuffer.allocate(ECHOMAX);
    }

    @Override public void handleAccept(SelectionKey key) throws IOException {} // UDP 無連接

    @Override
    public void handleRead(SelectionKey key) throws IOException {
        DatagramChannel channel = (DatagramChannel) key.channel();
        ClientRecord record = (ClientRecord) key.attachment();
        record.buffer.clear(); // 準備讀取
        record.clientAddress = channel.receive(record.buffer); // 接收數據報
        if (record.clientAddress != null) {
            key.interestOps(SelectionKey.OP_WRITE); // 註冊寫就緒
        }
    }

    @Override
    public void handleWrite(SelectionKey key) throws IOException {
        DatagramChannel channel = (DatagramChannel) key.channel();
        ClientRecord record = (ClientRecord) key.attachment();
        record.buffer.flip(); // 切換寫模式
        int sent = channel.send(record.buffer, record.clientAddress);
        if (sent != 0) { // 發送完成
            key.interestOps(SelectionKey.OP_READ); // 切回讀就緒
        }
    }
}

非阻塞 UDP 客戶端:模擬可靠傳輸

public class UDPEchoClientNonblocking {
    private static final int TIMEOUT = 3000, MAXTRIES = 255;
    public static void main(String[] args) throws Exception {
        DatagramChannel channel = DatagramChannel.open();
        channel.configureBlocking(false);
        channel.socket().setSoTimeout(TIMEOUT); // 設置超時

        ByteBuffer writeBuf = ByteBuffer.wrap("0123456789abcdefghijklmnopqrstuvwxyz".getBytes());
        ByteBuffer readBuf = ByteBuffer.allocate(MAXTRIES);
        channel.connect(new InetSocketAddress("127.0.0.1", 5500)); // 連接後可直接 read/write

        int total = 0;
        while (total < writeBuf.capacity()) {
            if (writeBuf.hasRemaining()) channel.write(writeBuf);
            int r = channel.read(readBuf);
            if (r == -1) throw new SocketException("連接關閉");
            total += r;
            System.out.print(".");
        }
        System.out.println("收到: " + new String(readBuf.array(), 0, total));
        channel.close();
    }
}

UDP Selector 服務端

public class UDPEchoServerSelector {
    private static final int TIMEOUT = 3000;
    public static void main(String[] args) throws IOException {
        Selector selector = Selector.open();
        DatagramChannel channel = DatagramChannel.open();
        channel.configureBlocking(false);
        channel.socket().bind(new InetSocketAddress(5500));
        // 註冊讀事件,附帶 ClientRecord 上下文
        channel.register(selector, SelectionKey.OP_READ, new UDPEchoSelectorProtocol.ClientRecord());

        EchoProtocol protocol = new UDPEchoSelectorProtocol();
        while (true) {
            if (selector.select(TIMEOUT) == 0) { System.out.print("."); continue; }
            Iterator it = selector.selectedKeys().iterator();
            while (it.hasNext()) {
                SelectionKey key = it.next(); it.remove();
                if (key.isReadable()) protocol.handleRead(key);
                if (key.isValid() && key.isWritable()) protocol.handleWrite(key);
            }
        }
    }
}

關鍵設計差異總結

特性NIO TCPNIO UDP
Channel 類型SocketChannel / ServerSocketChannelDatagramChannel
連接建立OP_ACCEPT + accept()無,直接收發
數據邊界流式,需自行分幀數據報,天然保邊界
客戶端狀態Channel 本身標識連接需附件記錄 SocketAddress
緩衝區管理單緩衝區雙向每客戶端獨立緩衝區
可靠性內建重傳、排序應用層自行實現(如示例超時重發)

生產環境避坑指南

  1. MTU 限制:UDP 包建議 ≤ 1472 字節(1500 - 20 IP - 8 UDP),避免分片丟包
  2. 緩衝區溢出:receive() 時緩衝區不足會靜默丟包,生產環境需監控 ReceiveBufferSize
  3. NAT 穿透:客戶端 connect() 後才能穿透大多數 NAT,服務端無需 connect
  4. 擴展性:單 Selector 處理海量 UDP 客戶端時,附件查找成為瓶頸,可分片或引入 Netty
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved