Netty4 實現數據傳輸中間層處理

Netty4 實現數據報文的接收/拆包/重組/轉發


完整代碼:netty4-datatrans


個人博客:DoubleFJ の Blog


前言

由於項目中有對建築的 GPS 定位模塊,而 GPS 儀器作爲客戶端連接,傳輸的是標準的 GPGGA 語句,也就是多個客戶端對一個服務端發送數據,節約端口資源故配置的是同一個端口,此時服務端接收到的 GPGGA 數據卻並不能分辨出到底是哪一個客戶端發送的,由此決定寫一個數據中間層處理,給報文重組根據規則加上唯一標識符。

正題

根據實際需求我這寫了服務端和客戶端,即該腳本部署的機器同時作爲 server 和 client。

可以進行對接收數據的拆包/邏輯重組/添加數據標識符等等 DIY 操作,再進行定向轉發。

客戶端處理

  • 添加了 Listener 啓動時可監聽判斷 client 是否正常啓動,即對應 server 端口是否啓用監聽
    • 若通道連通,正常連接進行數據傳輸
    • 若通道未連通,則調用 schedule 進行定時重連操作

GPSTransClientConnectionListener.java

if (!future.isSuccess()) {
    final EventLoop loop = future.channel().eventLoop();
    loop.schedule(new Runnable() {
        @Override
        public void run() {
            System.err.println("client reconnecting ...");
            try {
                client.connect(GPSTransConsts.REMOTE_IP, Integer.parseInt(GPSTransConsts.REMOTE_PORT));
            } catch (NumberFormatException | InterruptedException e) {
                System.out.println("restart err...");
                e.printStackTrace();
            }
        }
    }, 5L, TimeUnit.SECONDS);
} else {
    System.out.println("client connected ...");
}
  • 同樣若是啓動成功但是運行一段時間後 server 端口關閉監聽了,那也要進行重連處理,可以根據實際需求更改

GPSTransClientHandler.java

@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
    System.err.println("server disconnect ...");
    success = false;
    // 使用過程中斷線重連
    final EventLoop eventLoop = ctx.channel().eventLoop();
    eventLoop.schedule(new Runnable() {
        @Override
        public void run() {
            try {
                client.connect(GPSTransConsts.REMOTE_IP, Integer.parseInt(GPSTransConsts.REMOTE_PORT));
            } catch (Exception e) {
                System.out.println("restart err...");
                e.printStackTrace();
            }
        }
    }, 5L, TimeUnit.SECONDS);
    super.channelInactive(ctx);
}

由於是不停的進行轉發操作,所以需要循環處理。

定義了 private static volatile boolean success; 作爲數據發送線程的循環標誌符。

當連接成功時,success 置爲 true,當連接斷開時,success 置爲 false。

volatile 修飾故保證了其可見性。

@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
    System.out.println("channelActive ...");
    success = true;
    System.out.println("send data to server ...");
    // 必須另開線程處理,否則會在這個方法中出不去
    new Thread() {
        @Override
        public void run() {
            while (success) {
                if (!GPSTransConsts.NAME_MESS.isEmpty()) {
                    StringBuilder sb = new StringBuilder();
                    GPSTransConsts.NAME_MESS.values().forEach(value -> {
                        sb.append(value);
                    });
                    ByteBuf resp = Unpooled.copiedBuffer(sb.toString(), CharsetUtil.UTF_8);
                    ctx.writeAndFlush(resp);
                    try {
                        Thread.sleep(1000L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
            System.out.println("client thread exit ...");
        };
    }.start();
    super.channelActive(ctx);
}

服務端處理

接收多個客戶端數據,根據其 IP 來定位設備,再進行報文拆包重組 DIY,存儲到內存中便於 client 模塊進行轉發。

GPSTransServerHandler.java

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
  
    InetSocketAddress ipsocket = (InetSocketAddress) ctx.channel().remoteAddress();
    // 獲取客戶端 IP
    String clientIP = ipsocket.getAddress().getHostAddress();
    int index = clientIP.lastIndexOf(".");
    String ipNum = clientIP.substring(index + 1);
    ByteBuf in = (ByteBuf) msg;
    String message = in.toString(CharsetUtil.UTF_8);
    if (message.startsWith("$")) {
        message = message.replace("$", "#");
        if (!GPSTransConsts.IP_NAME.containsKey(ipNum)) {
            System.err.println(ipNum + "未配置!");
            return;
        }
        String name = GPSTransConsts.IP_NAME.get(ipNum);
        message = "#" + GPSTransConsts.IP_NAME.get(ipNum) + message + "\r";
        GPSTransConsts.NAME_MESS.put(name, message);
    }
    // 釋放
    super.channelRead(ctx, msg);
}

因爲我們沒有進行 write 和 flush 操作,所以需要進行釋放。

配置文件

爲了方便配置的修改,可以把項目打成 jar 包,然後在同目錄下新建一個 config 文件夾,把 gps.properties 丟進去,完事。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章