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

Netty 4 實戰:UDP 雙向通訊完整 Demo (伺服器端 + 標準 Java 客戶端)

     編輯:Java 綜合教學

Netty 4 實戰:UDP 雙向通訊完整 Demo

前言

NIO 框架讓高並發、高效能的網路服務開發成為可能。主流框架有 MINA 與 Netty,Netty 經歷 3、4 版演進 (Netty 5 已停止開發),API 風格與 MINA 差異較大。本文提供一個基於 Netty 4 的 UDP 伺服器與標準 Java UDP 客戶端 (無第三方依賴)的雙向通訊完整範例,填補官方範例僅有簡單廣播的不足。

本文亮點

  1. 稀缺資源: Netty 4 完整雙向 UDP 範例網上難覓,官方無提供。
  2. 跨平台客戶端: 客戶端使用標準 Java NIO (DatagramSocket),無第三方依賴,易移植至 Android/iOS。
  3. 可直接執行: 完整原始碼結構清晰,適合新手學習與研究。

Demo 設計

  • 客戶端每 3 秒發送一條含時間戳的訊息給伺服器。
  • 伺服器收到後立即回覆一條含時間戳的訊息。
  • 雙方均實現發送與接收,構成雙向通訊;稍加修改即可擴展為簡易聊天程式。

伺服器端實作 (Netty 4)

1. 依賴準備

從 Netty 官網 下載最新 4.1 版,建議使用 netty-all-4.1.x.Final.jar (約 2MB) 簡化依賴管理。

2. 主類 EchoServer.java

public class EchoServer {
    public static void main(String[] args) throws InterruptedException {
        Bootstrap b = new Bootstrap();
        EventLoopGroup group = new NioEventLoopGroup();
        b.group(group)
         .channel(NioDatagramChannel.class)
         .handler(new EchoServerHandler());

        // 綁定 9999 埠並阻塞等待關閉
        b.bind(9999).sync().channel().closeFuture().await();
    }
}

核心組件:Bootstrap (非阻塞啟動器)、NioEventLoopGroup (事件循環群組)、NioDatagramChannel (UDP 通道)、SimpleChannelInboundHandler 處理器。

3. 處理器 EchoServerHandler.java

public class EchoServerHandler extends SimpleChannelInboundHandler<DatagramPacket> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception {
        // 讀取資料
        ByteBuf buf = (ByteBuf) packet.copy().content();
        byte[] req = new byte[buf.readableBytes()];
        buf.readBytes(req);
        String body = new String(req, CharsetUtil.UTF_8);
        System.out.println("【NOTE】收到客戶端資料:" + body);

        // 回覆客戶端
        ctx.writeAndFlush(new DatagramPacket(
            Unpooled.copiedBuffer("Hello,我是Server,時間戳:" + System.currentTimeMillis(), CharsetUtil.UTF_8),
            packet.sender()
        )).sync();
    }
}

客戶端實作 (標準 Java UDP)

為保持程式碼簡潔,將 Socket 管理與資料接收分離為兩個工具類 (參考開源 MobileIMSDK 簡化)。

1. 主類 EchoClient.java

public class EchoClient {
    public static void main(String[] args) throws Exception {
        // 初始化本地 UDP Socket
        LocalUDPSocketProvider.getInstance().initSocket();
        // 啟動接收執行緒
        LocalUDPDataReciever.getInstance().startup();

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

2. Socket 管理 LocalUDPSocketProvider.java

public class LocalUDPSocketProvider {
    private static LocalUDPSocketProvider instance = null;
    private DatagramSocket localUDPSocket = null;

    public static LocalUDPSocketProvider getInstance() {
        if (instance == null) instance = new LocalUDPSocketProvider();
        return instance;
    }

    public void initSocket() {
        try {
            // 本地監聽埠 (0 表系統自動分配)
            this.localUDPSocket = new DatagramSocket(ConfigEntity.localUDPPort);
            // 連接遠端地址,後續 send 無需重複指定 IP/埠
            // 注意:connect 必須在 receive() 前呼叫,否則 send 可能阻塞
            this.localUDPSocket.connect(
                InetAddress.getByName(ConfigEntity.serverIP), ConfigEntity.serverUDPPort);
            this.localUDPSocket.setReuseAddress(true);
            System.out.println("DatagramSocket 初始化完成");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public DatagramSocket getLocalUDPSocket() { return this.localUDPSocket; }
}

3. 資料接收 LocalUDPDataReciever.java

public class LocalUDPDataReciever {
    private static LocalUDPDataReciever instance = null;
    private Thread thread = null;

    public static LocalUDPDataReciever getInstance() {
        if (instance == null) instance = new LocalUDPDataReciever();
        return instance;
    }

    public void startup() {
        this.thread = new Thread(() -> {
            try {
                System.out.println("本地 UDP 監聽中,埠=" + ConfigEntity.localUDPPort);
                udpListeningImpl();
            } catch (Exception e) {
                System.out.println("UDP 監聽停止:" + e.getMessage());
            }
        });
        this.thread.start();
    }

    private void udpListeningImpl() throws Exception {
        while (true) {
            byte[] data = new byte[1024];
            DatagramPacket packet = new DatagramPacket(data, data.length);
            DatagramSocket socket = LocalUDPSocketProvider.getInstance().getLocalUDPSocket();
            if (socket == null || socket.isClosed()) continue;
            socket.receive(packet); // 阻塞等待
            String msg = new String(packet.getData(), 0, packet.getLength(), "UTF-8");
            System.out.println("【NOTE】收到伺服器訊息:" + msg);
        }
    }
}

執行結果

客戶端輸出: 顯示每 3 秒發送一次,並印出伺服器回覆的時間戳。
伺服器端輸出: 印出收到的客戶端訊息及發送回覆紀錄。

(圖片略,請參考原文截圖)

補充說明與資源

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