第3講 編解碼

本章要點:

  • java序列化缺點
  • 業(yè)界流行的集中編解碼框架介紹

3.1 java序列化缺點

  • 無法跨越語言扳缕,是java序列化最致命的缺點饰潜;
  • 序列化后的碼流太大
  • 序列化性能太低

3.2 主流編輯碼框架

3.2.1 Google Protobuf編解碼

Protobuf在業(yè)界非常流行,Protobuf具有以下有點:

  • 產品成熟
  • 跨語言箕别、支持多種語言
  • 編碼后消息更小铜幽,便與存儲和傳輸
  • 編解碼性能高、
  • 支持定義可選和必選字段
    Protobuf是一個靈活串稀、高效除抛、結構化的數(shù)據(jù)序列化框架,相比于XML等傳統(tǒng)的序列化工具母截,它更小到忽、更快、更簡單清寇。
    https://github.com/google/protobuf/releases
    下載protof工具喘漏,protoc-3.5.1-win32.zip,并解壓华烟,會看到protoc.exe工具翩迈。

User.proto的文件內容如下:

package com.bj58.wuxian.protobuf;

option java_outer_classname = "UserProto"; 
message User{
    required int32 id=1;
    required string username=2;
    required string password=3;
    enum Sex{
     nan=1;
     nv=2;
    }
    required Sex sex=4;
}

執(zhí)行如下命令:

D:/develop/protoc-3.5.1-win32/bin/protoc.exe -I=./proto --java_out=D:/develop/protoc-3.5.1-win32/bin/proto/ ./proto/User.proto

生成了一個UserProto.java文件。
protoc.exe -I=proto的輸入目錄 --java_out=java類輸出目錄 proto的輸入目錄包括包括proto文件
此時將UserProto.java文件拷貝到IDE中會報錯垦江,那是因為缺少包依賴:

        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
            <version>3.5.1</version>
        </dependency>

protobuf序列化傳輸代碼實例:
UserServer代碼:

package com.bj58.wuxian.netty.codec.protobuf;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;

import com.bj58.wuxian.protobuf.UserProto;

public class UserServer {
    
    public static void main(String[] args) throws InterruptedException {
        EventLoopGroup bossGroup=new NioEventLoopGroup(); 
        EventLoopGroup workGroup=new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap=new ServerBootstrap();
            bootstrap.group(bossGroup, workGroup)
            .channel(NioServerSocketChannel.class)
            .option(ChannelOption.SO_BACKLOG, 1024)
            .handler(new LoggingHandler(LogLevel.INFO))
            .childHandler(new ChannelInitializer<SocketChannel>() {

                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline().addLast(new ProtobufVarint32FrameDecoder());//用于半包處理
                    ch.pipeline().addLast(new ProtobufDecoder(UserProto.User.getDefaultInstance()));//ProtobufDecoder參數(shù)是所要解碼的目標類
                    ch.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender());
                    ch.pipeline().addLast(new ProtobufEncoder());
                    ch.pipeline().addLast(new UserServerHandler());
                }
            });
            
            ChannelFuture f=bootstrap.bind(8888).sync();
            f.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }
}

UserServerHandler 代碼:

package com.bj58.wuxian.netty.codec.protobuf;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

import com.bj58.wuxian.protobuf.UserProto;
import com.bj58.wuxian.protobuf.UserProto.User.Sex;

public class UserServerHandler  extends ChannelInboundHandlerAdapter {

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("**********server channelActive*************");
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg)
            throws Exception {
        
        UserProto.User user =(UserProto.User)msg;
        System.out.println("request:"+msg);
        UserProto.User.Builder builder = UserProto.User.newBuilder();
        builder.setId(123);
        builder.setSex(Sex.nv);
        builder.setPassword("45678");
        if("zhaoshichao".equalsIgnoreCase(user.getUsername())){
            builder.setUsername("shangjing");
        }else{
            builder.setUsername("guanggunhan");
        }
        UserProto.User wife = builder.build();
        ctx.writeAndFlush(wife);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
            throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

UserClient 代碼:

package com.bj58.wuxian.netty.codec.protobuf;

import com.bj58.wuxian.protobuf.UserProto;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;

public class UserClient {
    public static void main(String[] args) throws InterruptedException {
        EventLoopGroup group=new NioEventLoopGroup();
        
        try {
            Bootstrap bootstrap=new Bootstrap();
            bootstrap.group(group)
            .channel(NioSocketChannel.class)
            .option(ChannelOption.TCP_NODELAY, true)
            .handler(new ChannelInitializer<SocketChannel>() {

                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline().addLast(new ProtobufVarint32FrameDecoder());
                    ch.pipeline().addLast(new ProtobufDecoder(UserProto.User.getDefaultInstance()));
                    ch.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender());
                    ch.pipeline().addLast(new ProtobufEncoder());
                    ch.pipeline().addLast(new UserClientHandler());
                }
            });
            
            ChannelFuture f=bootstrap.connect("127.0.0.1",8888).sync();
            f.channel().closeFuture().sync();
            
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            group.shutdownGracefully();
        }
    }
}

UserClientHandler 代碼:

package com.bj58.wuxian.netty.codec.protobuf;

import com.bj58.wuxian.protobuf.UserProto;
import com.bj58.wuxian.protobuf.UserProto.User.Sex;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class UserClientHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        for(int i=0;i<=5;i++){
            UserProto.User.Builder builder = UserProto.User.newBuilder();
            builder.setId(123);
            builder.setSex(Sex.nan);
            builder.setPassword("45678");
            if(i%2==0){
                builder.setUsername("zhaoshichao");
            }else{
                builder.setUsername("zhaoshichao"+i);
            }
            
            UserProto.User wife = builder.build();
            ctx.writeAndFlush(wife);
        }
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg)
            throws Exception {
        System.out.println("response:"+(UserProto.User)msg);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
            throws Exception {
        cause.printStackTrace();
        ctx.close();
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }
}

3.2.1 Message Pack編解碼

MessagePack特點如下:

  • 編解碼高效帽馋,性能高
  • 序列化之后的碼流小
  • 支持跨語言

添加pom依賴:

        <dependency>
            <groupId>org.msgpack</groupId>
            <artifactId>msgpack</artifactId>
            <version>0.6.12</version>
        </dependency>

自定義編碼器:

package com.bj58.wuxian.netty.codec.msgpack;

import org.msgpack.MessagePack;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;

public class MsgPackEncoder extends MessageToByteEncoder<Object> {

    @Override
    protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out)
            throws Exception {
        MessagePack msfPack=new MessagePack();
        byte[] bytes=msfPack.write(msg);
        out.writeBytes(bytes);
    }
}

自定義解碼器:

package com.bj58.wuxian.netty.codec.msgpack;

import java.util.List;
import org.msgpack.MessagePack;
import com.bj58.wuxian.msgpack.model.User;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;

public class MsgPackDecoder extends MessageToMessageDecoder<ByteBuf> {

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf msg,
            List<Object> out) throws Exception {
        byte[] bytes=new byte[msg.readableBytes()];
        msg.readBytes(bytes);
        MessagePack messagePack=new MessagePack();
        out.add(messagePack.read(bytes,User.class));
    }
}

UserServer代碼:

package com.bj58.wuxian.netty.codec.msgpack;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;

public class UserServer {
    public static void main(String[] argsStrings) throws Exception {
        // 配置服務端NIO線程組(boss線程、worker線程)
        EventLoopGroup bGroup = new NioEventLoopGroup();
        EventLoopGroup wGroup = new NioEventLoopGroup();
        // 創(chuàng)建啟動輔助類
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(bGroup, wGroup).channel(NioServerSocketChannel.class).option(ChannelOption.TCP_NODELAY, true)
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel channel) throws Exception {
                        // 添加對象系列化編解碼器,同時提供粘包拆包支持
                        channel.pipeline().addLast(new LengthFieldBasedFrameDecoder(65535, 0, 2, 0, 2));
                        channel.pipeline().addLast("解碼器", new MsgPackDecoder());
                        channel.pipeline().addLast(new LengthFieldPrepender(2));
                        channel.pipeline().addLast("編碼器", new MsgPackEncoder());
                        channel.pipeline().addLast(new UserServerHandler());
                    }

                });

        try {
            // 監(jiān)聽本地端口,同步等待監(jiān)聽結果
            ChannelFuture future = bootstrap.bind(8888).sync();
            // 等待服務端監(jiān)聽端口關閉,優(yōu)雅退出
            future.channel().closeFuture().sync();
        } finally {
            bGroup.shutdownGracefully();
            wGroup.shutdownGracefully();
        }

    }
}

ServerHanler代碼:

package com.bj58.wuxian.netty.codec.msgpack;

import com.bj58.wuxian.msgpack.model.User;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class UserServerHandler extends ChannelInboundHandlerAdapter  {

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("**********server channelActive**********");
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        User user=(User)msg;
        User wife=new User();
        if("zhaoshichao".equals(user.getUsername())){
            wife.setPassword("1234");
            wife.setUsername("shangjing");;
        }else{
            wife.setPassword("1234");
            wife.setUsername("others");
        }
        
        ctx.writeAndFlush(wife);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }

}

User客戶端:

package com.bj58.wuxian.netty.codec.msgpack;


import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;

public class UserClient {
    public static void  main(String [] argsStrings) throws Exception {  
        //配置客戶端端NIO線程組  
        EventLoopGroup bGroup = new NioEventLoopGroup();  
        //創(chuàng)建客戶端啟動輔助類  
        Bootstrap bootstrap = new Bootstrap();  
        bootstrap.group(bGroup).  
                  channel(NioSocketChannel.class).  
                  option(ChannelOption.TCP_NODELAY, true).  
                  option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000).  
                  handler(new ChannelInitializer<SocketChannel>() {  
                        @Override  
                        protected void initChannel(SocketChannel channel) throws Exception {  
                            //添加對象系列化編解碼器,同時提供粘包拆包支持  
                            channel.pipeline().addLast(new LengthFieldBasedFrameDecoder(65535, 0, 2, 0, 2));
                            channel.pipeline().addLast("解碼器", new MsgPackDecoder());
                            channel.pipeline().addLast(new LengthFieldPrepender(2));
                            channel.pipeline().addLast("編碼器", new MsgPackEncoder());
                            channel.pipeline().addLast(new UserClientHandler());
                        }  
                           
                    });  
          
        //發(fā)起異步連接  
        ChannelFuture future = bootstrap.connect("127.0.0.1", 8888).sync();  
        try {  
            //等待客戶端鏈路關閉  
            future.channel().closeFuture().sync();  
            } finally {  
            //優(yōu)雅退出,釋放資源  
            bGroup.shutdownGracefully();  
        }  
    }  
}

ClientHandler代碼:

package com.bj58.wuxian.netty.codec.msgpack;

import com.bj58.wuxian.msgpack.model.User;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class UserClientHandler extends ChannelInboundHandlerAdapter{
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        for(int i=0;i<=5;i++){
            User user=new User();
            user.setId(123);
            user.setPassword("45678");
            if(i%2==0){
                user.setUsername("zhaoshichao");
            }else{
                user.setUsername("zhaoshichao"+i);
            }
            
            ctx.writeAndFlush(user);
        }
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg)
            throws Exception {
        System.out.println("response:"+(User)msg);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
            throws Exception {
        cause.printStackTrace();
        ctx.close();
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }

}

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末比吭,一起剝皮案震驚了整個濱河市绽族,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌衩藤,老刑警劉巖吧慢,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異赏表,居然都是意外死亡检诗,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進店門瓢剿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來逢慌,“玉大人,你說我怎么就攤上這事间狂」テ茫” “怎么了?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長忙菠。 經(jīng)常有香客問我何鸡,道長,這世上最難降的妖魔是什么牛欢? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任骡男,我火速辦了婚禮,結果婚禮上傍睹,老公的妹妹穿的比我還像新娘隔盛。我一直安慰自己,他們只是感情好焰望,可當我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布骚亿。 她就那樣靜靜地躺著,像睡著了一般熊赖。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上虑椎,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天震鹉,我揣著相機與錄音,去河邊找鬼捆姜。 笑死传趾,一個胖子當著我的面吹牛,可吹牛的內容都是我干的泥技。 我是一名探鬼主播浆兰,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼珊豹!你這毒婦竟也來了簸呈?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤店茶,失蹤者是張志新(化名)和其女友劉穎蜕便,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贩幻,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡轿腺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了丛楚。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片族壳。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖趣些,靈堂內的尸體忽然破棺而出仿荆,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布赖歌,位于F島的核電站枉圃,受9級特大地震影響,放射性物質發(fā)生泄漏庐冯。R本人自食惡果不足惜孽亲,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望展父。 院中可真熱鬧返劲,春花似錦、人聲如沸栖茉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吕漂。三九已至亲配,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間惶凝,已是汗流浹背吼虎。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留苍鲜,地道東北人思灰。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像混滔,于是被迫代替她去往敵國和親洒疚。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,486評論 2 348

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,749評論 25 707
  • 很少曬娃坯屿,今天有感而發(fā)曬一記油湖。 姆二長大了,上了幼兒園愿伴。作為父母肺魁,自然都希望孩子天資聰穎,這跟取名“逸生”無關隔节。然...
    realMeathill閱讀 460評論 1 0
  • 最近小熊中招流感蹦误,請假在家休息劫拢。堅持到第三天不再發(fā)燒,我就有點急著送他去幼兒園强胰。熊爸挺不可思議的看著我:他還在咳嗽...
    熊媽侯蕾閱讀 725評論 1 3
  • 張藝興1991年10月7日出生在湖南省長沙市(標準的90后舱沧,一口湖南話說的相當標準),歌手偶洋、演員熟吏、EXO-M中國籍...
    小梁超閱讀 926評論 0 0
  • 我若為林,便欣欣以向榮玄窝;我若為草牵寺,便萋萋而搖綠。 儒家說:勿我勿必恩脂;道家言:無為忘機帽氓;佛家云:驅除心魔,天神合一俩块。...
    心夢飛翔h閱讀 217評論 0 1