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

NIO 框架入門二:MINA 2 UDP 雙向通信 Demo 完整實現(含標準 Java 客戶端)

     編輯:Java 綜合教學

NIO 框架入門二:MINA 2 UDP 雙向通信 Demo 完整實現(含標準 Java 客戶端)

系列定位

本系列旨在提供可直接運行、編碼規範的 NIO 框架入門 Demo,作為 MINA/Netty 學習的拋磚引玉。本文為第 2 篇,對標第 1 篇 Netty 4 實現,便於對比評估。

核心亮點

  • 源碼源自生產級開源項目 MobileIMSDK 簡化而來,具實戰價值
  • 客戶端零第三方依賴,純標準 Java NIO 實現,便於 iOS/Android 跨平台移植
  • 完整可運行源碼,新手友好

Demo 邏輯

客戶端每 3 秒發送消息 → 服務端收到立即回復 → 實現雙向通信。稍加改造即可成簡易聊天程序。

服務端實現

1. 主啟動類 EchoServer

public class EchoServer {
    private static final Logger logger = LoggerFactory.getLogger(EchoServer.class);

    public static void main(String[] args) throws Exception {
        NioDatagramAcceptor acceptor = new NioDatagramAcceptor();
        
        // 關鍵:線程池隔離業務處理,性能提升 10 倍
        acceptor.getFilterChain()
            .addLast("threadPool", new ExecutorFilter(Executors.newCachedThreadPool()));
        
        acceptor.setHandler(new EchoServerHandler());
        acceptor.setSessionRecycler(new ExpiringSessionRecycler(15_000)); // 15s 會話超時
        
        // UDP 參數調優
        DatagramSessionConfig dcfg = acceptor.getSessionConfig();
        dcfg.setReuseAddress(true);
        dcfg.setReceiveBufferSize(1024); // 壓測證明 2048 反而降性能
        dcfg.setSendBufferSize(1024);
        
        acceptor.bind(new InetSocketAddress(9999));
        logger.info("UDP 服務端監聽 9999 端口...");
    }
}

2. 業務處理器 EchoServerHandler

public class EchoServerHandler extends IoHandlerAdapter {
    private static final Logger logger = LoggerFactory.getLogger(EchoServerHandler.class);
    private static final CharsetDecoder DECODER = Charset.forName("UTF-8").newDecoder();

    @Override
    public void exceptionCaught(IoSession session, Throwable cause) {
        logger.error("異常捕獲", cause);
        session.close(true);
    }

    @Override
    public void messageReceived(IoSession session, Object message) {
        // 接收:關鍵解碼方式,避免標準 Java 客戶端數據首部亂碼
        IoBuffer buffer = (IoBuffer) message;
        String body = buffer.getString(DECODER);
        logger.debug("收到客戶端數據: {}", body);

        // 回復:關鍵構包方式,避免客戶端收到多餘字節
        String reply = "Hello,我是Server,時間戳" + System.currentTimeMillis();
        byte[] res = reply.getBytes(StandardCharsets.UTF_8);
        IoBuffer buf = IoBuffer.wrap(res); // 核心:wrap 而非 allocate+putInt+flip

        WriteFuture future = session.write(buf);
        future.awaitUninterruptibly(100);
        if (!future.isWritten()) {
            logger.warn("回復發送失敗");
        }
    }
}

關鍵技術點解析

  1. 接收解碼:buffer.getString(decoder) 替代官方 message.toString(),解決非 MINA 客戶端首部亂碼
  2. 發送構包:IoBuffer.wrap(bytes) 替代官方 allocate+putInt(length)+put+flip+shrink,解決客戶端收到多餘長度字段

客戶端實現(純標準 Java)

1. 主類 EchoClient

public class EchoClient {
    public static void main(String[] args) throws Exception {
        LocalUDPSocketProvider.getInstance().initSocket();
        LocalUDPDataReceiver.getInstance().startup();

        while (true) {
            String msg = "Hi,我是客戶端,時間戳" + System.currentTimeMillis();
            boolean ok = UDPUtils.send(msg.getBytes("UTF-8"));
            Log.d(ok ? "發送成功" : "發送失敗");
            Thread.sleep(3000);
        }
    }
}

2. Socket 管理 LocalUDPSocketProvider

public class LocalUDPSocketProvider {
    private DatagramSocket socket;
    
    public void initSocket() {
        socket = new DatagramSocket(Config.LOCAL_UDP_PORT); // 0=系統分配
        socket.connect(InetAddress.getByName(Config.SERVER_IP), Config.SERVER_UDP_PORT);
        // 關鍵:connect 必須在 receive() 前調用,否則 send 會錯誤阻塞(疑似 JDK bug)
        socket.setReuseAddress(true);
    }
}

3. 數據接收 LocalUDPDataReceiver

public class LocalUDPDataReceiver {
    public void startup() {
        new Thread(() -> {
            byte[] data = new byte[1024];
            DatagramPacket packet = new DatagramPacket(data, data.length);
            while (true) {
                socket.receive(packet); // 阻塞等待
                String msg = new String(packet.getData(), 0, packet.getLength(), "UTF-8");
                Log.w("收到服務端: " + msg);
            }
        }).start();
    }
}

運行效果

客戶端輸出 服務端輸出

服務端控制台每次多出 4 行 MINA 內部 DEBUG 日誌屬正常現象,不可關閉除非關閉整體日誌。

MINA 2 vs Netty 4 觀感

  • API 複雜度:MINA 2 > Netty 4
  • 代碼直觀度:MINA 2 更符合傳統編碼習慣,易理解
  • 非 MINA 客戶端兼容:MINA 2 需特殊處理亂碼/多餘字節,Netty 4 相對友好
  • 本質無異,按團隊技術棧與偏好選型

學習資源鏈接

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved