文档章节

Netty就是这么回事(六)

ACRushVoid
 ACRushVoid
发布于 2017/01/14 19:01
字数 982
阅读 192
收藏 2

趁着今天有时间,多发一点。这一章主要介绍Protobuf编解码器,首先先说protobuf是个什么东西。它其实是Google开发的一个数据交换的格式,它独立于语言,独立于平台。用过它的都会觉得它太好用了,记得当初在iesLab开发电力系统高级应用软件的服务端用到的通信协议就是它,除了代码风格不是很好之外,其他的真是太方便了,膜拜Google的技术啊。

在详细介绍之前,我先来问大家一个问题,如果让你开发一款通信协议,你希望如何设计协议?我相信,大家既然都已经习惯了面向对象编程,都是选择将对象转换为协议发送,当然接受的时候是将协议转换为对象。先抛开半包问题不说,先谈数据帧的传输,我们是不是希望:1. 通信的时候最好能用最少的字节来传递更多的消息,这样就可以加快消息的传输,产生很小的延迟。2. 我们的消息可以跨平台传输,不管对方用的什么语言?C还是C++还是java,对同样的通信协议可以通用,不会因为不同的平台因为字节大小而痛苦。那么java给我带来的序列化可以用么?当然可以但是有个问题,首先java的序列化的对象生成的字节数目太过庞大,几乎是正常二进制编码的5倍左右,当然是protobuf的更度倍。第二,你用java平台序列化的对象,在C++的平台是没法解析的。所以,基于这个protobuf给我们解决了,他的编码字节足够小,不同平台直接可以通用。

在Netty中天生给我们集成了protobuf的编解码器,不需要我们自己去实现,主要有解码器:ProtobufVarint32FrameDecoder(负责半包解码),ProtobufDecoder(负责protobuf的解码)

编码器:ProtobufVarint32LengthFieldPrepender(负责半包编码),ProtobufEncoder(负责协议编码)

这个其实很好理解,一个是为了解决半包问题,另一个解决协议解析问题。

我们看一下服务端的代码:

SubscribeProto.Subscribe.getDefaultInstance()这个是protobuf协议工具给我们生成的,具体的百度一下就知道了,它是什么意思呢?你想啊,Netty的解码器又不是万能的,他怎么知道你的帧格式是什么样子的?所以这个就是告诉他你给我照着这个样式解码,是不是很方便啊!

package com.dlb.note.server;

import com.dlb.note.doj.SubscribeProto;
import com.google.protobuf.ProtocolStringList;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
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;

/**
 * 功能:protobuf时间服务器
 * 版本:1.0
 * 日期:2016/12/9 18:33
 * 作者:馟苏
 */
public class ProtobufTimeServer {
    /**
     * main函数
     * @param args
     */
    public static void main(String []args) {
        // 构造nio线程组
        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)
                    .childHandler(new ChannelInitializer() {
                        protected void initChannel(Channel channel) throws Exception {
                            /**
                             * 以下是支持protobuf的解码器
                             */
                            channel.pipeline()
                                    .addLast(new ProtobufVarint32FrameDecoder()) // 半包解码
                                    .addLast(new ProtobufDecoder(SubscribeProto.Subscribe.getDefaultInstance())) // protobuf解码
                                    .addLast(new ProtobufVarint32LengthFieldPrepender()) // 半包编码
                                    .addLast(new ProtobufEncoder()) // protobuf编码
                                    .addLast(new MyProtoBufHandler());
                        }
                    });
            // 绑定端口,同步等待成功
            ChannelFuture future = bootstrap.bind(8888).sync();
            System.out.println("----服务端在8888端口监听----");

            // 等待服务端监听端口关闭
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 优雅的退出,释放线程池资源
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }
}

class MyProtoBufHandler extends ChannelHandlerAdapter {
    // 客户端链接异常
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("client exception,ip=" + ctx.channel().remoteAddress());
        ctx.close();
        super.exceptionCaught(ctx, cause);
    }

    // 客户端链接到来
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("client come,ip=" + ctx.channel().remoteAddress());
        super.channelActive(ctx);
    }

    // 客户端链接关闭
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("client close,ip=" + ctx.channel().remoteAddress());
        ctx.close();
        super.channelInactive(ctx);
    }

    // 可读
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        // 读数据
        SubscribeProto.Subscribe subscribe = (SubscribeProto.Subscribe) msg;
        ProtocolStringList addressList = subscribe.getAddressList();

        System.out.println(subscribe.getName());
        for (String str : addressList) {
            System.out.println(str);
        }
        System.out.println("---------------------------------------------------");

        super.channelRead(ctx, msg);
    }
}

 

© 著作权归作者所有

ACRushVoid
粉丝 58
博文 18
码字总数 19761
作品 0
威海
私信 提问
源码之下无秘密 ── 做最好的 Netty 源码分析教程

背景 在工作中, 虽然我经常使用到 Netty 库, 但是很多时候对 Netty 的一些概念还是处于知其然, 不知其所以然的状态, 因此就萌生了学习 Netty 源码的想法. 刚开始看源码的时候, 自然是比较痛苦...

永顺
2017/11/29
0
0
Netty 系列六(编解码器).

一、概念 网络传输的单位是字节,如何将应用程序的数据转换为字节,以及将字节转换为应用程序的数据,就要说到到我们该篇介绍的编码器和解码器。 将应用程序的数据转换为网络格式,以及将网络...

JMCui
2018/08/14
0
0
网络编程入门(六):什么是公网IP和内网IP?NAT转换又是什么鬼?

本文引用了“帅地”发表于公众号苦逼的码农的技术分享。 1、引言 搞网络通信应用开发的程序员,可能会经常听到外网IP(即互联网IP地址)和内网IP(即局域网IP地址),但他们的区别是什么?又...

首席大胸器
2018/11/20
135
0
脑残式网络编程入门(六):什么是公网IP和内网IP?NAT转换又是什么鬼?

本文引用了“帅地”发表于公众号苦逼的码农的技术分享。 1、引言 搞网络通信应用开发的程序员,可能会经常听到外网IP(即互联网IP地址)和内网IP(即局域网IP地址),但他们的区别是什么?又...

JackJiang2011
2018/11/20
0
0
Dubbo 服务调用 源码学习(下)(七)

笔记简述 本学习笔记接上篇Dubbo 服务调用 源码(上)学习(六),上一篇已经完成了invoker的生成,接下来就是具体的方法调用了,包含了mock测试、负载均衡(不涉及细节)、重试、netty调用、...

jwfy
2018/05/16
0
0

没有更多内容

加载失败,请刷新页面

加载更多

还为PDF转Word抓狂?以下神器让你在职场倍受欢迎!

身在职场的你,是否一直在琢磨:如何能让自己在公司更受欢迎?如何才能在办公室里混个好人缘?如何在同事圈里留个好印象?其实,想要让自己成为受欢迎的人,只要让自己成为大家需要的人不就行...

foxit2world
31分钟前
7
0
AndServer+Service打造Android服务器实现so文件调用

so 文件调用 随着 Android 移动安全的高速发展,不管是为了执行效率还是程序的安全性等,关键代码下沉 native 层已成为基本操作。 native 层的开发就是通指的 JNI/NDK 开发,通过 JNI 可以实...

夜幕NightTeam
33分钟前
5
0
Docker下kafka学习三部曲之二:本地环境搭建

在上一章《 Docker下kafka学习,三部曲之一:极速体验kafka》中我们快速体验了kafka的消息分发和订阅功能,但是对环境搭建的印象仅仅是执行了几个命令和脚本,本章我们通过实战来学习如何编写...

程序员欣宸
33分钟前
4
0
萌新推荐!不再为Excel转换PDF发愁,Aspose.Cells for .NET一步到位!

Aspose.Cells for .NET(点击下载)是Excel电子表格编程API,可加快电子表格管理和处理任务,支持构建具有生成,修改,转换,呈现和打印电子表格功能的跨平台应用程序。 将Excel工作簿转换为...

mnrssj
34分钟前
6
0
对于绘画小白怎么画制服?该注意什么?

怎样制作学生服装?想必绘画初学者们常常会想的问题吧,不知道怎样才能画好人物的衣服,别着急,今日就在这儿讲一些关于如何绘画学生衣服校服的教程给我们!期望能够帮到你们! 轻便西装是不...

热爱画画的我
40分钟前
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部