Netty使用Google Protobuf进行编解码

2023-05-16

文章目录

  • 一、概述
    • 1、编解码基础
    • 2、Netty编解码器
    • 3、Protobuf概述
  • 二、Protobuf基本使用
    • 1、引入jar包
    • 2、下载Protobuf
    • 3、编写Student.proto
    • 4、生成StudentPOJO类
    • 5、服务器端
    • 6、客户端
    • 7、验证一下吧
  • 三、Netty使用Protobuf发送多类型对象
    • 1、编写Student.proto
    • 2、生成MyDataInfo.java
    • 3、服务端
    • 4、客户端
    • 5、测试一下吧

一、概述

1、编解码基础

编写网络应用程序时,因为数据在网络中传输的都是二进制字节码数据,在发送数据时就需要编码,接收数据0时就需要解码。

通常来说,codec(编解码器)的组成部分有两个: decoder(解码器)和 encoder(编码器)。encoder 负责把业务数据转换成字节2)码数据,decoder 负责把字节码数据转换成业务数据。

在这里插入图片描述

2、Netty编解码器

Netty自身也携带了一些codec(编解码器):

StringDecoder // 对字符串数据进行解码
StringEncoder // 对字符串数据进行编码
ObjectDecoder // 对Java对象进行解码
ObjectEncoder // 对Java对象进行编码
... 

其中,Netty自带的ObjectDecoder 和ObjectEncoder 可以实现对POJO对象或各种业务对象的编码和解码,但是底层使用的仍是Java序列化技术,而Java序列化技术效率不高并且无法跨语言,所以Google的Protobuf是当今最火热的编解码器。

3、Protobuf概述

Protobuf 是 Google 发布的开源项目,全称 Google Protocol Buffers,是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC[远程过程调用 remote procedurecall] 数据交换格式。

参考文档:https://developers.google.com/protocol-buffers/docs/proto

Protobug是以message的方式来管理数据的,支持跨平台、跨语言,即 [ 客户端和服务器端可以是不同语言编写的 ] (支持目前绝大多数语言,例如C++、C#、Java、Python、Go等),性能高,可靠性 高。

二、Protobuf基本使用

1、引入jar包

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

2、下载Protobuf

https://github.com/protocolbuffers/protobuf/releases

我这里使用的是3.6.1版本,要找到对应的版本:https://github.com/protocolbuffers/protobuf/releases?page=11
在这里插入图片描述

3、编写Student.proto

syntax = "proto3"; //版本
option java_outer_classname = "StudentPOJO";//生成的外部类名,同时也是文件名
//protobuf 使用message 管理数据
message Student { //会在 StudentPOJO 外部类生成一个内部类 Student, 他是真正发送的POJO对象
    int32 id = 1; // Student 类中有 一个属性 名字为 id 类型为int32(protobuf类型) 1表示属性序号,不是值
    string name = 2;
    string msg = 3;
}

4、生成StudentPOJO类

(1)将Student.proto文件拷贝到protoc.exe同级目录并执行命令:

protoc-3.6.1-win32\bin>protoc.exe --java_out=. Student.proto

在这里插入图片描述
会生成一个StudentPOJO.java文件,该文件可以放在项目中使用了,一定不要修改该文件。

5、服务器端


import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
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;

public class NettyServer {
    public static void main(String[] args) throws Exception {


        //创建BossGroup 和 WorkerGroup
        //说明
        //1. 创建两个线程组 bossGroup 和 workerGroup
        //2. bossGroup 只是处理连接请求 , 真正的和客户端业务处理,会交给 workerGroup完成
        //3. 两个都是无限循环
        //4. bossGroup 和 workerGroup 含有的子线程(NioEventLoop)的个数
        //   默认实际 cpu核数 * 2
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup(); //8



        try {
            //创建服务器端的启动对象,配置参数
            ServerBootstrap bootstrap = new ServerBootstrap();

            //使用链式编程来进行设置
            bootstrap.group(bossGroup, workerGroup) //设置两个线程组
                    .channel(NioServerSocketChannel.class) //使用NioSocketChannel 作为服务器的通道实现
                    .option(ChannelOption.SO_BACKLOG, 128) // 设置线程队列得到连接个数
                    .childOption(ChannelOption.SO_KEEPALIVE, true) //设置保持活动连接状态
//                    .handler(null) // 该 handler对应 bossGroup , childHandler 对应 workerGroup
                    .childHandler(new ChannelInitializer<SocketChannel>() {//创建一个通道初始化对象(匿名对象)
                        //给pipeline 设置处理器
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {


                            ChannelPipeline pipeline = ch.pipeline();
                            //在pipeline加入ProtoBufDecoder
                            //指定对哪种对象进行解码
                            pipeline.addLast("decoder", new ProtobufDecoder(StudentPOJO.Student.getDefaultInstance()));
                            pipeline.addLast(new NettyServerHandler());
                        }
                    }); // 给我们的workerGroup 的 EventLoop 对应的管道设置处理器

            System.out.println(".....服务器 is ready...");

            //绑定一个端口并且同步, 生成了一个 ChannelFuture 对象
            //启动服务器(并绑定端口)
            ChannelFuture cf = bootstrap.bind(6668).sync();

            //给cf 注册监听器,监控我们关心的事件

            cf.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    if (cf.isSuccess()) {
                        System.out.println("监听端口 6668 成功");
                    } else {
                        System.out.println("监听端口 6668 失败");
                    }
                }
            });


            //对关闭通道进行监听
            cf.channel().closeFuture().sync();
        }finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }

    }

}


import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.util.CharsetUtil;

/*
说明
1. 我们自定义一个Handler 需要继续netty 规定好的某个HandlerAdapter(规范)
2. 这时我们自定义一个Handler , 才能称为一个handler
 */
//public class NettyServerHandler extends ChannelInboundHandlerAdapter {
public class NettyServerHandler extends SimpleChannelInboundHandler<StudentPOJO.Student> {


    //读取数据实际(这里我们可以读取客户端发送的消息)
    /*
    1. ChannelHandlerContext ctx:上下文对象, 含有 管道pipeline , 通道channel, 地址
    2. Object msg: 就是客户端发送的数据 默认Object
     */
    @Override
    public void channelRead0(ChannelHandlerContext ctx, StudentPOJO.Student msg) throws Exception {

        //读取从客户端发送的StudentPojo.Student


        System.out.println("客户端发送的数据 id=" + msg.getId() + " 名字=" + msg.getName() + "msg" + msg.getMsg());
    }



//    //读取数据实际(这里我们可以读取客户端发送的消息)
//    /*
//    1. ChannelHandlerContext ctx:上下文对象, 含有 管道pipeline , 通道channel, 地址
//    2. Object msg: 就是客户端发送的数据 默认Object
//     */
//    @Override
//    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//
//        //读取从客户端发送的StudentPojo.Student
//
//        StudentPOJO.Student student = (StudentPOJO.Student) msg;
//
//        System.out.println("客户端发送的数据 id=" + student.getId() + " 名字=" + student.getName());
//    }

    //数据读取完毕
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {

        //writeAndFlush 是 write + flush
        //将数据写入到缓存,并刷新
        //一般讲,我们对这个发送的数据进行编码
        ctx.writeAndFlush(Unpooled.copiedBuffer("hello, 客户端", CharsetUtil.UTF_8));
    }

    //处理异常, 一般是需要关闭通道

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

6、客户端


import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
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.ProtobufEncoder;

public class NettyClient {
    public static void main(String[] args) throws Exception {

        //客户端需要一个事件循环组
        EventLoopGroup group = new NioEventLoopGroup();


        try {
            //创建客户端启动对象
            //注意客户端使用的不是 ServerBootstrap 而是 Bootstrap
            Bootstrap bootstrap = new Bootstrap();

            //设置相关参数
            bootstrap.group(group) //设置线程组
                    .channel(NioSocketChannel.class) // 设置客户端通道的实现类(反射)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            //在pipeline中加入 ProtoBufEncoder
                            pipeline.addLast("encoder", new ProtobufEncoder());
                            pipeline.addLast(new NettyClientHandler()); //加入自己的处理器
                        }
                    });

            System.out.println("客户端 ok..");

            //启动客户端去连接服务器端
            //关于 ChannelFuture 要分析,涉及到netty的异步模型
            ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 6668).sync();
            //给关闭通道进行监听
            channelFuture.channel().closeFuture().sync();
        }finally {

            group.shutdownGracefully();

        }
    }
}

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;

public class NettyClientHandler extends ChannelInboundHandlerAdapter {

    //当通道就绪就会触发该方法
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {

        //发生一个Student 对象到服务器

        StudentPOJO.Student student = StudentPOJO.Student.newBuilder()
                .setId(4)
                .setName("张三")
                .setMsg("我会武功,是个饭桶")
                .build();
        //Teacher , Member ,Message
        ctx.writeAndFlush(student);

    }

    //当通道有读取事件时,会触发
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

        ByteBuf buf = (ByteBuf) msg;
        System.out.println("服务器回复的消息:" + buf.toString(CharsetUtil.UTF_8));
        System.out.println("服务器的地址: "+ ctx.channel().remoteAddress());
    }

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

7、验证一下吧

.....服务器 is ready...
监听端口 6668 成功
客户端发送的数据 id=4 名字=张三msg我会武功,是个饭桶
客户端 ok..
服务器回复的消息:hello, 客户端
服务器的地址: /127.0.0.1:6668

三、Netty使用Protobuf发送多类型对象

1、编写Student.proto

syntax = "proto3";
option optimize_for = SPEED; // 加快解析
// option java_package="com.demo.netty.codec";   //指定生成到哪个包下
option java_outer_classname="MyDataInfo"; // 外部类名, 文件名

//protobuf 可以使用message 管理其他的message
message MyMessage {

    //定义一个枚举类型
    enum DataType {
        StudentType = 0; //在proto3 要求enum的编号从0开始
        WorkerType = 1;
    }

    //用data_type 来标识传的是哪一个枚举类型
    DataType data_type = 1; // 编号

    //表示每次枚举类型最多只能出现其中的一个, 节省空间 ,dataBody可以自己命名
    oneof dataBody {
        Student student = 2; // 编号
        Worker worker = 3; // 编号
    }

}


message Student {
    int32 id = 1;//Student类的属性
    string name = 2; //
}
message Worker {
    string name=1;
    int32 age=2;
}

2、生成MyDataInfo.java

(1)将Student.proto文件拷贝到protoc.exe同级目录并执行命令:

protoc-3.6.1-win32\bin>protoc.exe --java_out=. Student.proto

在这里插入图片描述
生成的MyDataInfo.java可以拷贝到项目中使用了。

3、服务端


import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
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;

public class NettyServer {
    public static void main(String[] args) throws Exception {


        //创建BossGroup 和 WorkerGroup
        //说明
        //1. 创建两个线程组 bossGroup 和 workerGroup
        //2. bossGroup 只是处理连接请求 , 真正的和客户端业务处理,会交给 workerGroup完成
        //3. 两个都是无限循环
        //4. bossGroup 和 workerGroup 含有的子线程(NioEventLoop)的个数
        //   默认实际 cpu核数 * 2
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup(); //8



        try {
            //创建服务器端的启动对象,配置参数
            ServerBootstrap bootstrap = new ServerBootstrap();

            //使用链式编程来进行设置
            bootstrap.group(bossGroup, workerGroup) //设置两个线程组
                    .channel(NioServerSocketChannel.class) //使用NioSocketChannel 作为服务器的通道实现
                    .option(ChannelOption.SO_BACKLOG, 128) // 设置线程队列得到连接个数
                    .childOption(ChannelOption.SO_KEEPALIVE, true) //设置保持活动连接状态
//                    .handler(null) // 该 handler对应 bossGroup , childHandler 对应 workerGroup
                    .childHandler(new ChannelInitializer<SocketChannel>() {//创建一个通道初始化对象(匿名对象)
                        //给pipeline 设置处理器
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {


                            ChannelPipeline pipeline = ch.pipeline();
                            //在pipeline加入ProtoBufDecoder
                            //指定对哪种对象进行解码
                            pipeline.addLast("decoder", new ProtobufDecoder(MyDataInfo.MyMessage.getDefaultInstance()));
                            pipeline.addLast(new NettyServerHandler());
                        }
                    }); // 给我们的workerGroup 的 EventLoop 对应的管道设置处理器

            System.out.println(".....服务器 is ready...");

            //绑定一个端口并且同步, 生成了一个 ChannelFuture 对象
            //启动服务器(并绑定端口)
            ChannelFuture cf = bootstrap.bind(6668).sync();

            //给cf 注册监听器,监控我们关心的事件

            cf.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    if (cf.isSuccess()) {
                        System.out.println("监听端口 6668 成功");
                    } else {
                        System.out.println("监听端口 6668 失败");
                    }
                }
            });


            //对关闭通道进行监听
            cf.channel().closeFuture().sync();
        }finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }

    }

}


import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;

/*
说明
1. 我们自定义一个Handler 需要继续netty 规定好的某个HandlerAdapter(规范)
2. 这时我们自定义一个Handler , 才能称为一个handler
 */
//public class NettyServerHandler extends ChannelInboundHandlerAdapter {
public class NettyServerHandler extends SimpleChannelInboundHandler<MyDataInfo.MyMessage> {


    //读取数据实际(这里我们可以读取客户端发送的消息)
    /*
    1. ChannelHandlerContext ctx:上下文对象, 含有 管道pipeline , 通道channel, 地址
    2. Object msg: 就是客户端发送的数据 默认Object
     */
    @Override
    public void channelRead0(ChannelHandlerContext ctx, MyDataInfo.MyMessage msg) throws Exception {

        //根据dataType 来显示不同的信息
        MyDataInfo.MyMessage.DataType dataType = msg.getDataType();
        if(dataType == MyDataInfo.MyMessage.DataType.StudentType) {

            MyDataInfo.Student student = msg.getStudent();
            System.out.println("学生id=" + student.getId() + " 学生名字=" + student.getName());

        } else if(dataType == MyDataInfo.MyMessage.DataType.WorkerType) {
            MyDataInfo.Worker worker = msg.getWorker();
            System.out.println("工人的名字=" + worker.getName() + " 年龄=" + worker.getAge());
        } else {
            System.out.println("传输的类型不正确");
        }


    }



//    //读取数据实际(这里我们可以读取客户端发送的消息)
//    /*
//    1. ChannelHandlerContext ctx:上下文对象, 含有 管道pipeline , 通道channel, 地址
//    2. Object msg: 就是客户端发送的数据 默认Object
//     */
//    @Override
//    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//
//        //读取从客户端发送的StudentPojo.Student
//
//        StudentPOJO.Student student = (StudentPOJO.Student) msg;
//
//        System.out.println("客户端发送的数据 id=" + student.getId() + " 名字=" + student.getName());
//    }

    //数据读取完毕
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {

        //writeAndFlush 是 write + flush
        //将数据写入到缓存,并刷新
        //一般讲,我们对这个发送的数据进行编码
        ctx.writeAndFlush(Unpooled.copiedBuffer("hello, 客户端~", CharsetUtil.UTF_8));
    }

    //处理异常, 一般是需要关闭通道

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

4、客户端


import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
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.ProtobufEncoder;

public class NettyClient {
    public static void main(String[] args) throws Exception {

        //客户端需要一个事件循环组
        EventLoopGroup group = new NioEventLoopGroup();


        try {
            //创建客户端启动对象
            //注意客户端使用的不是 ServerBootstrap 而是 Bootstrap
            Bootstrap bootstrap = new Bootstrap();

            //设置相关参数
            bootstrap.group(group) //设置线程组
                    .channel(NioSocketChannel.class) // 设置客户端通道的实现类(反射)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            //在pipeline中加入 ProtoBufEncoder
                            pipeline.addLast("encoder", new ProtobufEncoder());
                            pipeline.addLast(new NettyClientHandler()); //加入自己的处理器
                        }
                    });

            System.out.println("客户端 ok..");

            //启动客户端去连接服务器端
            //关于 ChannelFuture 要分析,涉及到netty的异步模型
            ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 6668).sync();
            //给关闭通道进行监听
            channelFuture.channel().closeFuture().sync();
        }finally {

            group.shutdownGracefully();

        }
    }
}


import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;

import java.util.Random;

public class NettyClientHandler extends ChannelInboundHandlerAdapter {

    //当通道就绪就会触发该方法
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {

        //随机的发送Student 或者 Workder 对象
        int random = new Random().nextInt(3);
        MyDataInfo.MyMessage myMessage = null;

        if(0 == random) { //发送Student 对象

            myMessage = MyDataInfo.MyMessage.newBuilder().setDataType(MyDataInfo.MyMessage.DataType.StudentType).setStudent(MyDataInfo.Student.newBuilder().setId(5).setName("张三").build()).build();
        } else { // 发送一个Worker 对象
            myMessage = MyDataInfo.MyMessage.newBuilder().setDataType(MyDataInfo.MyMessage.DataType.WorkerType).setWorker(MyDataInfo.Worker.newBuilder().setAge(20).setName("李四").build()).build();
        }

        ctx.writeAndFlush(myMessage);
    }

    //当通道有读取事件时,会触发
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

        ByteBuf buf = (ByteBuf) msg;
        System.out.println("服务器回复的消息:" + buf.toString(CharsetUtil.UTF_8));
        System.out.println("服务器的地址: "+ ctx.channel().remoteAddress());
    }

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

5、测试一下吧

.....服务器 is ready...
监听端口 6668 成功
工人的名字=李四 年龄=20
学生id=5 学生名字=张三
工人的名字=李四 年龄=20

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Netty使用Google Protobuf进行编解码 的相关文章

  • Micronaut ReadTimeoutException 异常

    我有一个提供 REST API 的 Grails 4 应用程序 端点之一有时会失败 但会出现以下异常 io micronaut http client exceptions ReadTimeoutException Read Timeout
  • AsyncHttpClient 是否知道为所有 HTTP 请求分配多少个线程

    我正在评估异步Http客户端 https github com AsyncHttpClient async http client对于大负载 1M HTTP 请求 对于每个请求 我想使用 AsyncCompletionHandler 调用回
  • 如何管理标识您的协议的前缀字节序列

    这与我有关统一设置的问题有关 具有持久通道的端口统一 https stackoverflow com questions 18445861 port unification with persistent channel 我正在尝试发送一个
  • jboss的netty中下游事件是如何工作的?

    刚刚开始使用 netty 来实现我自己的服务器 我花了一段时间才掌握它的窍门 但现在我能够通过编写自己的 MessageHandler 来接受客户端 并且在 messageReceived 内部我能够从缓冲区中读取数据并执行一些与接收到的数
  • 使用 Netty 的多线程 UDP 服务器

    我正在尝试使用 Netty 实现 UDP 服务器 这个想法是只绑定一次 因此只创建一个Channel This Channel仅使用一个处理程序进行初始化 该处理程序通过一个线程在多个线程之间分派传入数据报的处理ExecutorServic
  • Netty异步写入响应和大小未知的大数据

    我开发了一个netty http服务器 但是当我在方法ChannelInboundHandlerAdapter channelRead0中写入响应时 我的响应结果来自另一台服务器 并且结果的大小未知 因此它的http响应标头可能具有内容长度
  • 使用多个线程处理单个连接

    我有 3 个异步服务器和客户端 它们像一条链一样相互连接 一个请求会经过 3 个系统 例如 gt System 1 gt System 2 gt System 3 gt 和回应 gt System 3 gt System 2 gt Syst
  • 如何在Java中解码http POST数据?

    我正在使用 Netty 并且必须接受并解析 http POST 请求 据我所知 Netty 没有内置对 POST 的支持 只有 GET 的支持 这是一个相当低级的库 处理原始网络操作 使用 servlet 容器 开箱即用地完成所有这些工作
  • Spring Webflux 不明时间损失

    我们最近切换到 ExpediaGroups GraphQLlibrary https github com ExpediaGroup graphql kotlin它基于 Spring Webflux Since switching our
  • Netty的ChannelLocal的使用

    Netty 的 JavaDocs 解释 ChannelLocal 与 ThreadLocal 类似 但是我对它的用法有一些疑问 ThreadLocal 是一个静态类 具有访问特定于实例的对象的静态方法 ChannelLocal 不是静态的
  • JBoss Netty 与 JSON

    我希望我的 Ajax 代码能够通过 Netty 连接服务器 为此 我需要在服务器端 Netty 处理程序中使用 JSON 解码器和编码器 是否有任何开箱即用的实现 或者我应该编写自己的实现 Thanks Gil 据我所知 没有内置的 JSO
  • ChannelOption.SO_BACKLOG 的作用是什么?

    option ChannelOption SO BACKLOG 100 Netty 4 升级文档中显示 你能解释一下它的作用吗 Thanks 它是一个传递的套接字选项 用于确定排队的连接数 http docs oracle com java
  • 如何知道Netty ByteBuf中是否没有数据可读取?

    我是 Netty 新手 文件传输的问题让我困惑了好几天 我想发送image文件从客户端到服务器 下面的代码是可执行的 但只有我shutdown服务器强制我可以正常打开收到的图像文件 否则 显示 您似乎没有查看此文件的权限 检查权限并重试 所
  • 内蒂不写

    当尝试使用 netty 写入时 写入的数据永远不会在远程端结束 这已通过 Wireshark 确认 我努力了 Directly using writeAndFlush channel writeAndFlush new Packet Man
  • netty 4.x.x 中的 UDP 广播

    我们需要使用 Netty 4 0 0 二进制文件通过 UDP 通道广播对象 Pojo 在 Netty 4 0 0 中 它允许我们仅使用 DatagramPacket 类来发送 UDP 数据包 此类仅接受 ByteBuf 作为参数 还有其他方
  • Netty:奇怪的 IndexOutOfBoundsException:readerIndex + 长度超过 writerIndex

    我目前正在通过 netty 发送不同的数据包 并且经常遇到这样的异常收到它们时 java lang IndexOutOfBoundsException readerIndex 39 length 32 exceeds writerIndex
  • Netty:正确关闭 WebSocket

    如何从服务器端正确关闭 WebSocket 通道 连接 如果我使用一个ctx getChannel close the onerror在浏览器 Firefox 9 中抛出 页面加载时与 ws localhost 8080 websocket
  • 为什么我的 Camel Netty 路由会在 JMS 消息的开头添加换行符?

    我有一个 Camel Netty 路由 它将 XML 发送到服务器端口并将其放入 JMS 消息中 在第一条消息之后 所有其他消息的顶部都有一个换行符 导致当 GUI 收到它时 我的 XML 无法解组 我的路线是这样的
  • 使用 Play WS 并获取 java.net.ConnectException:Amazon Cloudfront 上的握手超时

    在我的 Play 应用程序中 我需要从 Amazon Cloudfront 下载大量文件 使用 SSL 我在链接上随机收到以下错误 play api http HttpErrorHandlerExceptions anon 1 Execut
  • 使用 Netty 将 websocket 与在 tomcat 中运行的 Spring Web 应用程序集成

    我有一个使用 Netty 的 Web 套接字服务器实现 例如监听端口 8081 和一个在 Tomcat 中运行的单独的 Spring Web 应用程序 在端口 80 中运行 我想以某种方式将所有来自 localhost 80 Websock

随机推荐

  • macOS中iOS模拟卡顿 ios simulator 高CPU占用解决办法

    问题描述 已经忘记从什么时间起 xff0c iOS的模拟器只要一开启 xff0c 我电脑的风扇就开始嚎叫了 我印象中以前开模拟器 xff0c 风扇还是比较安静的 开发iOS模拟器后 xff0c 会有一个Spotlight进程占用了大量的CP
  • 03[Tampermonkey开发]赖人必备自动点赞插件开发

    span class token comment 61 61 UserScript 61 61 span span class token comment 64 name bilibili auto like span span class
  • 04[Tampermonkey开发]无限三连之术(幻术)

    span class token comment 61 61 UserScript 61 61 span span class token comment 64 name bilibili max like span span class
  • macOS 安装istats zsh: command not found: istats

    现象 已经成功安装了istats xff0c 但是仍然提示zsh command not found istats span class token function sudo span gem span class token funct
  • “System Events”遇到一个错误:“osascript”不允许发送按键。 (1002)

    我想使用applescript模拟按键请求 xff0c tell application span class token string 34 System Events 34 span key code span class token
  • IDEA自动补全tab键向下选择s-tab向上选择

    友情提醒 目前不完美 xff0c 这样设置完后 tab键制表符功能会受到影响 目前没找到完美的解决办法 目前是使用其他的按键来代替tab的功能 34 代替tab inoremap span class token operator lt s
  • 20_[nvim0.5+从0单排]_lsp状态栏(lualine)标签页(bufferline)美化

    视频与目录 项目值教程目录https blog csdn net lxyoucan article details 120641546B站视频暂无 20 nvim0 5 43 从0单排 lsp状态栏标签页美化lualine bufferli
  • macOS平铺窗口yabai配置分享

    简介 yabai 是一个窗口管理实用程序 xff0c 旨在作为 macOS 内置窗口管理器的扩展工作 yabai 允许您使用直观的命令行界面自由控制窗口 空间和显示 xff0c 并可选择使用设置用户定义的键盘快捷键 skhd和其他第三方软件
  • Netty入门案例——Netty实现websocket

    文章目录 一 服务端二 网页 一 服务端 span class token keyword import span span class token namespace io span class token punctuation spa
  • React Navigation中使用typescript简洁演示代码

    最近在尝试转到typescript xff0c 之前代码中含有的大量 navigaiton any近期打算把这样的代码优化一下 参考以下文档 xff1a https reactnavigation org docs typescript 尽
  • Linux新磁盘挂载到/home目录

    经常会遇到服务用着用着发现空间不够啦 xff01 怎么办呢 xff1f 备份数据 61 使用更大的磁盘重新安装系统 61 转移数据 这样太麻烦了 xff0c 如果是生产环境 xff0c 还要停机 增加新的磁盘 这里我选择方法 2 空间不足时
  • ReactNative AsyncLocalStorageUtil is defined multiple times

    ios运行正常 xff0c 在android下运行报错如下 xff1a AS 编译报错 Type com reactnativecommunity asyncstorage AsyncLocalStorageUtil is defined
  • React Native项目gradle手动编译

    最近在折腾 xff0c 远程开发React Native 项目 xff0c 我想实现在ssh命令行中 在服务器上自动编译RN 项目 xff08 android xff09 xff0c 这样就可以使用高速的服务器来编译项目 正解 cd and
  • Docker使用笔记

    软件安装 https docs docker com engine install ubuntu 下载镜像 span class token function docker span pull ubuntu 创建一个CONTAINER 示例
  • ubuntu编译安装最新的tmux

    通过apt get安装的tmux版本比较旧 xff0c 我喜欢使用最新的版本 那就自己编译安装一下吧 很简单 xff0c 耗时1分钟左右 环境 操作系统 xff1a Ubuntu 20 04 3 LTS 安装的tmux版本 xff1a tm
  • Ubuntu Linux 更改主机名(hostname)

    操作 编辑 etc hostname文件 span class token function vim span etc hostname 文件内容修改成自己想要的名称 修改完后 xff0c 重启机器就生效了 span class token
  • docker文件目录迁移

    docker默认存放路径是 var lib docker xff0c 按理来说没有什么问题 但是在我安装操作系统时 xff0c 分区空间分的太少了 xff08 50G xff09 但是 home目录就非常大了 所以我想把docker的默认路
  • ubuntu开启ssh服务

    环境 我的测试环境是 xff1a docker中的Ubuntu 20 04 3 LTS 安装openssh server span class token function sudo span span class token functi
  • neovim无法中文显示的问题

    场景 中文的语言环境 xff0c 其他支持中文的程序 正常能显示中文 xff0c 比如 date xff0c vim都可以支持中文 就neovim显示的是英文 我下载的neovim是全功能的版本 xff0c 支持中文的 所以排除软件的原因
  • Netty使用Google Protobuf进行编解码

    文章目录 一 概述1 编解码基础2 Netty编解码器3 Protobuf概述 二 Protobuf基本使用1 引入jar包2 下载Protobuf3 编写Student proto4 生成StudentPOJO类5 服务器端6 客户端7