001.Netty分隔符解碼

1.目標(biāo)

解碼服務(wù)器消息中的[消息有效內(nèi)容],有效內(nèi)容首尾是固定的分隔符$OUTSTA#,內(nèi)容格式如下:

$OUTSTA[消息有效內(nèi)容]#[結(jié)束內(nèi)容,可能為有效內(nèi)容]

2.pom.xml

添加netty依賴

        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.14.Final</version>
        </dependency>

3.TcpClient.java

package com.airkisser.netty;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;

/**
 * TCP/IP客戶端
 */
public class TcpClient {

    /**
     * 連接TCP服務(wù)端
     * @param host 服務(wù)端IP
     * @param port 服務(wù)端監(jiān)聽(tīng)端口號(hào)
     */
    public void connect(String host, int port) {
        // 配置NIO線程組
        EventLoopGroup group = new NioEventLoopGroup();
        Bootstrap b = new Bootstrap();
        b.group(group).channel(NioSocketChannel.class)
                .option(ChannelOption.TCP_NODELAY, true)
                .handler(new LoggingHandler(LogLevel.DEBUG))
                .handler(new ChannelInitializer<SocketChannel>() {
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        ByteBuf startDelimiter = Unpooled.copiedBuffer("$OUTSTA".getBytes());
                        ByteBuf endDelimiter = Unpooled.copiedBuffer("#".getBytes());
                        socketChannel.pipeline()
                                /*  分隔符解碼器
                                    如果接收內(nèi)容超過(guò)2048的長(zhǎng)度還沒(méi)有查找到分隔符,拋出TooLongFrameException異常,防止內(nèi)存溢出
                                    可以定義一個(gè)或多個(gè)分隔符,解碼器將只取分隔符之間的內(nèi)容
                                    例如:
                                    服務(wù)端一次返回消息內(nèi)容為:
                                    $OUTSTA65535,10,1256,170823_162540,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0#ss

                                    則定義兩個(gè)分隔符"$OUTSTA"和"#ss"
                                    那么第一次接收服務(wù)器返回消息時(shí)解碼后內(nèi)容為一條:
                                    65535,10,1256,170823_162540,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0
                                    還剩下'#ss'后面的內(nèi)容沒(méi)有被解碼,等待接收下一條服務(wù)器消息
                                    第二次接收服務(wù)器返回消息時(shí)解碼后的內(nèi)容為兩條:
                                    第一條:空字符串""
                                    第二條:
                                        65535,10,1256,170823_162540,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0
                                    需要注意的是:"#ss"和"$OUTSTA"作為分隔的首尾,中間的內(nèi)容被解碼為空字符串
                                    需要對(duì)解碼后的內(nèi)容進(jìn)行空字符串過(guò)濾

                                    如果定義兩個(gè)分隔符為"$OUTSTA"和"#"
                                    那么第一次接收服務(wù)器返回消息時(shí)解碼后內(nèi)容為一條:
                                    65535,10,1256,170823_162540,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0
                                    還剩下'#'后面的內(nèi)容沒(méi)有被解碼,等待接收下一條服務(wù)器消息
                                    第二次接收服務(wù)器返回消息時(shí)解碼后的內(nèi)容為兩條:
                                    第一條:ss
                                    第二條:
                                        65535,10,1256,170823_162540,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0
                                    其中第一條'ss'是第一次接收到的服務(wù)器消息'#'后面的內(nèi)容以及第二次接收到的服務(wù)器消息'$OUTSTA'之前的內(nèi)容
                                    是我們不需要的內(nèi)容,可以通過(guò)限定長(zhǎng)度過(guò)濾掉,不對(duì)該條消息做處理
                                    同理,還剩下'#'后面的內(nèi)容沒(méi)有被解碼,等待接收下一條服務(wù)器消息
                                 */
                                .addLast(new DelimiterBasedFrameDecoder(2048, startDelimiter, endDelimiter))
                                /*
                                 * 將分隔符解碼后的內(nèi)容轉(zhuǎn)為字符串
                                 */
                                .addLast(new StringDecoder())
                                .addLast(new ChannelInboundHandlerAdapter() {
                                    @Override
                                    public void channelActive(ChannelHandlerContext ctx) throws Exception {
                                        //@TODO 進(jìn)行連接成功后的操作
                                        System.out.println("連接成功... ");
                                    }

                                    @Override
                                    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                                        // StringDecoder解碼后的信息,
                                        String message = (String) msg;
                                        // 前面已經(jīng)說(shuō)了,通過(guò)分隔符解碼后的消息中有一部分并不是我們需要的消息
                                        // 此時(shí)我們可以通過(guò)一些限制條件過(guò)濾掉這些我們不需要的內(nèi)容,比如空字符串,此處通過(guò)限定長(zhǎng)度來(lái)過(guò)濾:長(zhǎng)度大于10,
                                        // 可以根據(jù)實(shí)際情況進(jìn)行過(guò)濾
                                        // 只對(duì)過(guò)濾后的內(nèi)容進(jìn)行解析
                                        if (message != null && message.length() > 10) {
                                            System.out.println("接收內(nèi)容:\n" + message);
                                            // @TODO 對(duì)消息進(jìn)行解析等操作,一般放在線程或線程池中處理
                                        }
                                    }

                                    @Override
                                    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
                                        // @TODO 捕捉到異常后進(jìn)行重連或者其他異常處理操作
                                        System.out.println("發(fā)生異常...");
                                        cause.printStackTrace();
                                        ctx.channel().close();
                                    }

                                });

                    }
                });
        try {
            ChannelFuture f = b.connect(host, port).sync();
        } catch (InterruptedException e) {
            // @TODO 處理連接失敗后的異常
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        TcpClient client = new TcpClient();
        client.connect("127.0.0.1", 12800);
    }


}

4.測(cè)試

步驟1:?jiǎn)?dòng)TCP模擬服務(wù)端
監(jiān)聽(tīng)地址:127.0.0.1
監(jiān)聽(tīng)端口:12800
模擬消息:

$OUTSTA65535,10,1256,170823_162540,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0,0,0,0,0#ss

客戶端需要的有效內(nèi)容:

65535,10,1256,170823_162540,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0,0,0,0,0
image.png

步驟2:運(yùn)行TcpClient中的main方法作為客戶端
步驟3:服務(wù)端發(fā)送消息給客戶端,客戶端控制臺(tái)打印內(nèi)容如下:

image.png

解碼完成。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,500評(píng)論 19 139
  • 1、Netty基礎(chǔ)入門(mén) Netty是由JBOSS提供的一個(gè)java開(kāi)源框架。Netty提供異步的、事件驅(qū)動(dòng)的網(wǎng)絡(luò)應(yīng)...
    我是嘻哈大哥閱讀 4,771評(píng)論 0 31
  • 前奏 https://tech.meituan.com/2016/11/04/nio.html 綜述 netty通...
    jiangmo閱讀 6,199評(píng)論 0 13
  • 空間里是扭曲的,音樂(lè)終于擰轉(zhuǎn)起原本扭曲的生活 意識(shí)可以扭曲現(xiàn)實(shí),表現(xiàn)好的就是“神”,表現(xiàn)差的就是“白癡”,沒(méi)有中間...
    hawkingmx閱讀 225評(píng)論 0 0
  • 首先,祝自己生日快樂(lè)。 三十而立。 然,不才,30年來(lái)無(wú)所建樹(shù),只是一日日地爬走在生活的貧瘠田字格里,而內(nèi)心時(shí)時(shí)羨...
    毛毛蟲(chóng)媽咪閱讀 518評(píng)論 0 3

友情鏈接更多精彩內(nèi)容