Netty私有协议栈设计

Netty从入门到精通五——私有协议栈开发 在链路中断INTERVAL时间后,由客户端发起重连操作,若失败则在INTERVAL时间后再次发起重连,因为需要保证服务端在这段时间内充分释放句柄资源,等待下次重连。Ping-Pong是双向机制,可以保证双方无论谁出现故障都可被及时检测,当消息读写发生IO异常后,就代表链路中断,需要关闭链路释放资源,重连或等待重连。,链路建立成功后客户端会主动发送握手消息,服务端收到握手消息后若通过IP校验,会返回应答消息给客户端。:链路空闲时,客户端主动发Ping消息给服务端,服务端接收到消息后回复Pong消息给客户端; 阅读详情

Netty私有协议栈设计

消息定义

  • 消息头

  • 消息主体

图示:

图1.png

Header:


public class Header {

    private int crcCode = 0xadaf0105; // 唯一的通信标志

    private int length; // 总消息的长度 header + body

    private long sessionID; // 会话ID


    private byte type; // 消息的类型

    private byte priority; // 消息的优先级 0~255

    private Map<String, Object> attachment = new HashMap<String, Object>(); // 附件
    // ...
}

NettyMessage

public class NettyMessage {

    private Header header;

    private Object body;

    public final Header getHeader() {
        return header;
    }

    public final void setHeader(Header header) {
        this.header = header;
    }

    public final Object getBody() {
        return body;
    }

    public final void setBody(Object body) {
        this.body = body;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.lang.Object#toString()
     */
    public String toString() {
        return "NettyMessage [header=" + header + "]";
    }


}

编解码设计

选择Marshaller作为Java对象序列化和反序列化的工具

MarshallingCodeCFactory工厂生成具体对象

public class MarshallingCodeCFactory {

    public static Marshaller buildMarshalling() throws IOException {

//首先通过Marshalling工具类的精通方法获取Marshalling实例对象 参数serial标识创建的是java序列化工厂对象。
        final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
        //创建了MarshallingConfiguration对象,配置了版本号为5
        final MarshallingConfiguration configuration = new MarshallingConfiguration();
        configuration.setVersion(5);
        Marshaller marshaller = marshallerFactory.createMarshaller(configuration);
        return marshaller;

    }
    public static Unmarshaller buildUnMarshalling() throws IOException {
        final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
        final MarshallingConfiguration configuration = new MarshallingConfiguration();
        configuration.setVersion(5);
        Unmarshaller unmarshaller = marshallerFactory.createUnmarshaller(configuration);
        return unmarshaller;
    }



}


辅助Marshaller工作的两个类:

public class ChannelBufferByteOutput implements ByteOutput {


    private final ByteBuf buffer;

    /**
     * Create a new instance which use the given {@link ByteBuf}
     */
    public ChannelBufferByteOutput(ByteBuf buffer) {
        this.buffer = buffer;
    }

    @Override
    public void close() throws IOException {
        // Nothing to do
    }

    @Override
    public void flush() throws IOException {
        // nothing to do
    }

    @Override
    public void write(int b) throws IOException {
        buffer.writeByte(b);
    }

    @Override
    public void write(byte[] bytes) throws IOException {
        buffer.writeBytes(bytes);
    }

    @Override
    public void write(byte[] bytes, int srcIndex, int length) throws IOException {
        buffer.writeBytes(bytes, srcIndex, length);
    }

    /**
     * Return the {@link ByteBuf} which contains the written content
     *
     */
    ByteBuf getBuffer() {
        return buffer;
    }
}





public class ChannelBufferByteInput implements ByteInput {


    private final ByteBuf byteBuf;

    public ChannelBufferByteInput(ByteBuf byteBuf) {
        this.byteBuf = byteBuf;
    }

    @Override
    public int read() throws IOException {
        if (byteBuf.isReadable()) {
            return byteBuf.readByte() & 0xff;
        }
        return -1;
    }

    @Override
    public int read(byte[] bytes) throws IOException {
        return read(bytes, 0 , bytes.length);
    }

    @Override
    public int read(byte[] dst, int dstIndex, int length) throws IOException {
        int available = available();
        if (available == 0) {
            return -1;
        }

        length = Math.min(available, length);
        byteBuf.readBytes(dst, dstIndex, length);
        return length;
    }
    @Override
    public int available() throws IOException {
        return byteBuf.readableBytes();
    }

    @Override
    public long skip(long bytes) throws IOException {
        int readable = byteBuf.readableBytes();
        if (readable < bytes) {
            bytes = readable;
        }
        byteBuf.readerIndex((int) (byteBuf.readerIndex() + bytes));
        return bytes;
    }

    @Override
    public void close() throws IOException {

    }
}


编码器

处理流程:

图2.png

MarshallingEncoder:

public class MarshallingEncoder {

    //空白占位: 用于预留设置 body的数据包长度
    private static final byte[] LENGTH_PLACEHOLDER = new byte[4];

    private Marshaller marshaller;

    public MarshallingEncoder() throws IOException {
        this.marshaller = MarshallingCodeCFactory.buildMarshalling();
    }

    public void encode(Object body, ByteBuf out) throws IOException {
        try {
            //必须要知道当前的数据位置是哪: 起始数据位置
            //长度属性的位置索引
            int lengthPos = out.writerIndex();
            //占位写操作:先写一个4个字节的空的内容,记录在起始数据位置,用于设置内容长度
            out.writeBytes(LENGTH_PLACEHOLDER);
            ChannelBufferByteOutput output = new ChannelBufferByteOutput(out);
            marshaller.start(output);
            marshaller.writeObject(body);
            marshaller.finish();
            //总长度(结束位置) - 初始化长度(起始位置) - 预留的长度  = body数据长度
            int endPos = out.writerIndex();
            out.setInt(lengthPos, endPos - lengthPos - 4);

        } finally {
            marshaller.close();
        }


    }


}


NettyMessageEncoder:

public class NettyMessageEncoder extends MessageToByteEncoder<NettyMessage> {

    private MarshallingEncoder marshallingEncoder;

    public NettyMessageEncoder() throws IOException {
        this.marshallingEncoder = new MarshallingEncoder();
    }


    @Override
    protected void encode(ChannelHandlerContext ctx, NettyMessage message, ByteBuf sendBuf) throws Exception {
        if(message == null || message.getHeader() == null){
            throw new Exception("编码失败,没有数据信息!");
        }

        //Head:
        Header header = message.getHeader();
        sendBuf.writeInt(header.getCrcCode());//校验码
        sendBuf.writeInt(header.getLength());//总长度
        sendBuf.writeLong(header.getSessionID());//会话id
        sendBuf.writeByte(header.getType());//消息类型
        sendBuf.writeByte(header.getPriority());//优先级

        //对附件信息进行编码
        //编码规则为:如果attachment的长度为0,表示没有可选附件,则将长度	编码设置为0
        //如果attachment长度大于0,则需要编码,规则:
        //首先对附件的个数进行编码
        sendBuf.writeInt((header.getAttachment().size())); //附件大小
        String key = null;
        byte[] keyArray = null;
        Object value = null;
        //然后对key进行编码,先编码长度,然后再将它转化为byte数组之后编码内容
        for (Map.Entry<String, Object> param : header.getAttachment()
                .entrySet()) {
            key = param.getKey();
            keyArray = key.getBytes("UTF-8");
            sendBuf.writeInt(keyArray.length);//key的字符编码长度
            sendBuf.writeBytes(keyArray);
            value = param.getValue();
            marshallingEncoder.encode(value, sendBuf);
        }
        key = null;
        keyArray = null;
        value = null;

        //Body:
        Object body = message.getBody();
        //如果不为空 说明: 有数据
        if(body != null){
            //使用MarshallingEncoder
            this.marshallingEncoder.encode(body, sendBuf);
        } else {
            //如果没有数据 则进行补位 为了方便后续的 decoder操作
            sendBuf.writeInt(0);
        }

        //最后我们要获取整个数据包的总长度 也就是 header +  body 进行对 header length的设置

        // TODO:  解释: 在这里必须要-8个字节 ,是因为要把CRC和长度本身占的减掉了
        //(官方中给出的是:LengthFieldBasedFrameDecoder中的lengthFieldOffset+lengthFieldLength)
        //总长度是在header协议的第二个标记字段中
        //第一个参数是长度属性的索引位置
        sendBuf.setInt(4, sendBuf.readableBytes() - 8);
        
    }
}

解码器

图3.png

MarshallingDecoder


public class MarshallingDecoder {


    private Unmarshaller unmarshaller;

    public MarshallingDecoder() throws IOException {
        this.unmarshaller = MarshallingCodeCFactory.buildUnMarshalling();
    }

    public Object decode(ByteBuf in) throws Exception {
        try {
            //1 首先读取4个长度(实际body内容长度)
            int bodySize = in.readInt();
            //2 获取实际body的缓冲内容
            int readIndex = in.readerIndex();
            ByteBuf buf = in.slice(readIndex, bodySize);
            //3 转换
            ChannelBufferByteInput input = new ChannelBufferByteInput(buf);
            //4 读取操作:
            this.unmarshaller.start(input);
            Object ret = this.unmarshaller.readObject();
            this.unmarshaller.finish();
            //5 读取完毕以后, 更新当前读取起始位置:
            //因为使用slice方法,原buf的位置还在readIndex上,故需要将位置重新设置一下
            in.readerIndex(in.readerIndex() + bodySize);

            return ret;

        } finally {
            this.unmarshaller.close();
        }
    }


}


NettyMessageDecoder

public class NettyMessageDecoder extends LengthFieldBasedFrameDecoder {

    private MarshallingDecoder marshallingDecoder;

    /**
     * 那减8应该是因为要把CRC和长度本身占的减掉了。
     * @param maxFrameLength 第一个参数代表最大的序列化长度   1024*1024*5
     * @param lengthFieldOffset 代表长度属性的偏移量 简单来说就是message中 总长度的起始位置(Header中的length属性的起始位置)   本例中为4
     * @param lengthFieldLength 代表长度属性的长度 整个属性占多长(length属性为int,占4个字节)  4
     * @throws IOException
     */
    public NettyMessageDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength) throws IOException {
        super(maxFrameLength, lengthFieldOffset, lengthFieldLength);
        this.marshallingDecoder = new MarshallingDecoder();
    }



    @Override
    protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
        //1 调用父类(LengthFieldBasedFrameDecoder)方法:
        ByteBuf frame  = (ByteBuf)super.decode(ctx, in);

        if(frame == null){
            return null;
        }

        NettyMessage message = new NettyMessage();
        Header header = new Header();
        header.setCrcCode(frame.readInt());		//crcCode ----> 添加通信标记认证逻辑
        header.setLength(frame.readInt());		//length
        header.setSessionID(frame.readLong());	//sessionID
        header.setType(frame.readByte());		//type
        header.setPriority(frame.readByte());	//priority



        int size = frame.readInt();
        //附件个数大于0,则需要解码操作
        if (size > 0) {
            Map<String, Object> attch = new HashMap<String, Object>(size);
            int keySize = 0;
            byte[] keyArray = null;
            String key = null;
            for (int i = 0; i < size; i++) {
                keySize = frame.readInt();
                keyArray = new byte[keySize];
                frame.readBytes(keyArray);
                key = new String(keyArray, "UTF-8");
                attch.put(key, marshallingDecoder.decode(frame));
            }
            keyArray = null;
            key = null;
            //解码完成放入attachment
            header.setAttachment(attch);
        }

        message.setHeader(header);
        //对于ByteBuf来说,读一个数据,就会少一个数据,所以读完header,剩下的应该就是body了
        if(frame.readableBytes() > 4) { //大于4个字节,肯定就有数据了(4个字节是内容长度的占位)
            message.setBody(marshallingDecoder.decode(frame));
        }
        return message;
    }
}


握手消息请求的发送以及处理

图示:

图4.png

握手请求:

LoginAuthReqHandler


public class LoginAuthReqHandler extends ChannelInboundHandlerAdapter {


    private static final Logger LOGGER = LoggerFactory.getLogger(LoginAuthReqHandler.class);


    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        LOGGER.info("通道激活,握手请求认证..................");

        ctx.writeAndFlush(buildLoginReq());
    }


    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        NettyMessage message = (NettyMessage) msg;

        if (message.getHeader() != null && message.getHeader().getType() == MessageType.LOGIN_RESP.value()) {

            byte loginResult = (byte) message.getBody();

            if (loginResult != ResultType.SUCCESS.value()) {
                ctx.close();
            } else {
                System.out.println("Login is OK : " + message);
                ctx.fireChannelRead(msg);
            }
        } else {
            ctx.fireChannelRead(msg);
        }
    }

    private NettyMessage buildLoginReq() {
        NettyMessage message = new NettyMessage();
        Header header = new Header();
        header.setType(MessageType.LOGIN_REQ.value());
        message.setHeader(header);
        return message;
    }

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

服务端处理:

LoginAuthRespHandler


public class LoginAuthRespHandler extends ChannelInboundHandlerAdapter {

    private static final Logger LOGGER = LoggerFactory.getLogger(LoginAuthRespHandler.class);

    /**
     * 考虑到安全,链路的建立需要通过基于IP地址或者号段的黑白名单安全认证机制,本例中,多个IP通过逗号隔开
     */
    private Map<String, Boolean> nodeCheck = new ConcurrentHashMap<String, Boolean>();
    private String[] whitekList = { "127.0.0.1", "192.168.56.1" };

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

        NettyMessage message = (NettyMessage) msg;

        // 判断消息是否为握手请求消息
        if (message.getHeader() != null && message.getHeader().getType()
        == MessageType.LOGIN_REQ.value()) {
            String nodeIndex = ctx.channel().remoteAddress().toString();
            NettyMessage loginResp = null;
            if (nodeCheck.containsKey(nodeIndex)) {
                LOGGER.error("重复登录,拒绝请求!");
                loginResp = buildResponse(ResultType.FAIL);
            } else {
                InetSocketAddress address = (InetSocketAddress) ctx.channel().remoteAddress();
                String ip = address.getAddress().getHostAddress();
                boolean isOK = false;
                for (String WIP : whitekList) {
                    if (WIP.equals(ip)) {
                        isOK = true;
                        break;
                    }
                }
                loginResp = isOK ? buildResponse(ResultType.SUCCESS) : buildResponse(ResultType.FAIL);
                if (isOK)
                    nodeCheck.put(nodeIndex, true);
            }
            LOGGER.info("The login response is : {} body [{}]",loginResp,loginResp.getBody());
            ctx.writeAndFlush(loginResp);
        } else {
            ctx.fireChannelRead(msg);

        }


    }

    /**
     * 服务端接到客户端的握手请求消息后,如果IP校验通过,返回握手成功应答消息给客户端,应用层成功建立链路,否则返回验证失败信息。消息格式如下:
     * 1.消息头的type为4
     * 2.可选附件个数为0
     * 3.消息体为byte类型的结果,0表示认证成功,1表示认证失败
     */
    private NettyMessage buildResponse(ResultType result) {
        NettyMessage message = new NettyMessage();
        Header header = new Header();
        header.setType(MessageType.LOGIN_RESP.value());
        message.setHeader(header);
        message.setBody(result.value());
        return message;
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        nodeCheck.remove(ctx.channel().remoteAddress().toString());// 删除缓存
        ctx.close();
        ctx.fireExceptionCaught(cause);    }
}

心跳检测

图示:

图5.png

HeartBeatReqHandler

客户端发送:

public class HeartBeatReqHandler extends ChannelInboundHandlerAdapter {

    private static final Logger LOGGER = LoggerFactory.getLogger(HeartBeatReqHandler.class);

    private volatile ScheduledFuture<?> heartBeat;

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

        NettyMessage message = (NettyMessage) msg;

// 握手成功,主动发送心跳消息
        if (message.getHeader() != null && message.getHeader().getType() == MessageType.LOGIN_RESP.value()) {
            heartBeat = ctx.executor().scheduleAtFixedRate(new HeartBeatReqHandler.HeartBeatTask(ctx), 0, 5000,
                    TimeUnit.MILLISECONDS);
        } else if (message.getHeader() != null && message.getHeader().getType() == MessageType.HEARTBEAT_RESP.value()) {
            LOGGER.info("Client receive server heart beat message : ---> {}", message);
        } else
            ctx.fireChannelRead(msg);

    }


    private class HeartBeatTask implements Runnable {

        private final ChannelHandlerContext ctx;

        public HeartBeatTask(final ChannelHandlerContext ctx) {
            this.ctx = ctx;
        }

        @Override
        public void run() {
            NettyMessage heatBeat = buildHeatBeat();
            LOGGER.info("Client send heart beat messsage to server : ---> {}", heatBeat);
            ctx.writeAndFlush(heatBeat);
        }

        private NettyMessage buildHeatBeat() {
            NettyMessage message = new NettyMessage();
            Header header = new Header();
            header.setType(MessageType.HEARTBEAT_REQ.value());
            message.setHeader(header);
            return message;
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        //断连期间,心跳定时器停止工作,不再发送心跳请求信息
        if (heartBeat != null) {
            heartBeat.cancel(true);
            heartBeat = null;
        }
        ctx.fireExceptionCaught(cause);    }
}

服务端处理:


public class HeartBeatRespHandler extends ChannelInboundHandlerAdapter {

    private static final Logger LOGGER = LoggerFactory.getLogger(HeartBeatRespHandler.class);

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

        NettyMessage message = (NettyMessage) msg;

        // 判断是否 是心跳检测消息
        if (message.getHeader() != null && message.getHeader().getType() ==
                MessageType.HEARTBEAT_REQ.value()) {

            LOGGER.info("Receive client heart beat message : ---> {} " ,message);
            NettyMessage heartBeat = buildHeatBeat();
            LOGGER.info("Send heart beat response message to client : ---> {}" ,heartBeat);
            ctx.writeAndFlush(heartBeat);
        } else {
            ctx.fireChannelRead(msg);
        }
    }

    // 生成心跳检测消息
    private NettyMessage buildHeatBeat() {
        NettyMessage message = new NettyMessage();
        Header header = new Header();
        header.setType(MessageType.HEARTBEAT_RESP.value());
        message.setHeader(header);
        return message;
    }

}


Netty之私有协议栈开发 前言:《Netty权威指南》这本书让我收益巨大,接下来将结合自己的理解以及书中私有协议栈开发的例子,谈谈私有协议开发的思路,可能会遇到的问题,以及解决方法。同时会谈谈针对网络攻击(例如syn攻击)的解决方案。具体代码链接:https://github.com/wacxt/netty-private-protocol,可以结合进行看,这次不会上一堆代码,重点谈谈一些设计理念上的东西。(一)通信模型: 阅读详情

相关推荐

《Netty权威指南》笔记 —— 第十二、十三、十四章

《Netty权威指南》笔记——Netty多协议开发和应用第12章 私有协议栈开发私有协议介绍Netty协议栈功能设计第13章 服务端创建第14章 客户端创建 第12章 私有协议栈开发 私有协议介绍 私有协议具有封闭性, 垄断性,排他性的特点. Netty提供的异步TCP协议栈开发一个私有协议栈, 该协议栈被命名为Netty协议栈. Netty协议栈功能设计 Netty协议栈用于内部各模块之间的通信, 基于TCP/IP协议栈, 一个类HTTP协议的应用层协议栈, 相比于传统的标准协议栈, 它更加轻巧,灵活和实

weixin_41225956的博客 696

构建高效RPC框架:私有协议设计及消息封装解码实践

在现代软件开发中,随着微服务架构的普及,远程过程调用(RPC)框架成为了连接服务之间通信的桥梁。我有决定开发了一款高性能的RPC框架,它不仅实现了服务之间的高效调用,还集成了关键的服务治理功能,如负载均衡、熔断机制和限流策略,以确保系统的稳定性和可靠性。核心技术本项目采用Netty作为其强大的底层通信组件,确保了网络通信的高效与稳定。同时,通过与ZooKeeper的结合,实现了服务的注册与发现,为服务治理提供了坚实的基础。

鱼干的博客 1367

【技术笔记】Cadence实现Orcad与Allegro软件交互式布局设置

为了达到原理图和PCB两两交互的功能,需要在ORCAD中将允许交互进行勾选,这样才可以,执行菜单命令Options-Preference后,我们打开参数界面,进行选择Miscellaneous菜单,将交互模式进行勾选。在做硬件pcb设计的时候,原理图选中一个元器件,希望可以再PCB中可以直接选中。对于网表必须是导入第一方网表,第三方是无效的。在allegro中使能/关闭交互式布局功能。

weixin_50499137的博客 2075

实战Netty!基于私有协议,怎样快速开发网络通信服务?

在工作中,我经常使用netty开发一些服务,掌握netty的工作原理,开发一些服务端以及客户端是非常简单,本篇文章,我们就以具体的协议来进行一个简单的服务的开发。

初念初恋的博客 1368

Vertx之Grpc消息发送

介绍 Vert.x gRPC 模块,它将 Google gRPC 的编程风格与 Vert.x 风格保持一致. gRPC 是一个高性能、开源、通用的RPC框架,是一种进程间通讯技术,由Google推出,基于HTTP2协议标准设计开发,默认采用Protocol Buffers数据序列化协议,支持多种开发语言。gRPC提供了一种简单的方法来精确的定义服务,并且为客户端和服务端自动生成可靠的功能库。 在gRPC客户端可以直接调用不同服务器上的远程程序,使用姿势看起来就像调用本地程序一样,轻松的实现远端服务的连接、

Lance 1188

最新打赏系统打赏源码麒麟最新打赏源码喜盈门打赏知识付费课程教程直播讲解

打赏源码打赏系统

qq_41095398的博客 2026

Netty之私有协议栈开发(四)

2021SC@SDUSC

May121812345的博客 1493

【知识点讲解】大模型参数量与计算量(FLOPs)估算方法

指标参数量 (Params)计算量 (FLOPs)含义模型的大小,静态模型的计算复杂度,动态决定因素模型结构(宽度、深度)模型结构 +输入数据(B, S)影响内存占用、磁盘空间运行速度、能耗单位M (百万), B (十亿)工具thopptflopstorchinfothopptflopsfvcore重要提示FLOPs ≠ 速度:FLOPs 是理论计算量。实际速度还受内存带宽 (Memory Bandwidth)并行度硬件架构等因素严重影响。

AI浩 2095

Netty协议设计与解析

简单记录一下netty协议的编写和使用过程,方便后面查看。

404 Not Found 1638

JavaScript实现斗地主(附完整源码)

JavaScript实现斗地主(附完整源码)

希望我的博客,能帮上你解决学习中工作中所遇到的问题 1503

Ceph入门到精通-更换osd、扩容osd

【代码】Ceph入门到精通-更换osd、扩容osd。

隔壁老瓦的专栏 1547

java私有协议开发_priProto 基于netty5基于私有协议栈设计

priProto 基于netty5基于私有协议栈设计通讯模型 具体步骤如下。(1)Netty协议栈客户端发送握手请求消息,携带节点ID等有效身份认证信息;(2)Netty协议栈服务端对握手请求消息进行合法性校验,包括节点ID有效性校验、节点重复登录校验和IP地址合法性校验,校验通过后,返回登录成功的握手应答消息;(3)链路建立成功之后,客户端发送业务消息;(4)链路成功之后,服务端发送心跳消息;(...

weixin_33581873的博客 260

微信公众号 - 解决报错 “err_msg“:“system:access_denied“,微信环境浏览器真机使用微信支付、微信授权登录、分享朋友圈转发好友报错system:access_denied

微信公众号,微信网页,手机h5网页,微信报错,system:access_denied,微信支付,提示报错,微信登录授权报错,微信朋友圈分享,微信转发,WeixinJSBridge,微信环境下,微信内置浏览器,微信公众号项目报错system:access_denied,如何解决,invoke(),API调用后出现报错,微信内部H5 WeixinJSBridge.invoke system:access_denied,使用微信jssdk时报access_denied错误,还是报错,真机运行,真机调试api返回

🏆 前端领域优质创作者 1万+

Netty学习笔记系列之七:开发私有协议栈

通信协议从广义上区分,可以分为公有协议和私有协议。由于私有协议的灵活性,它往往会在某个公司或者组织内部使用,按需定制,也因为如此,升级起来会非常方便,灵活性好。绝大多数的私有协议传输层都基于TCP/IP,所以利用Netty的NIO TCP协议栈可以非常方便地进行私有协议的定制和开发。Netty协议栈功能设计使用Netty提供的异步TCP协议栈开发一个私有协议栈,该协议栈被命名为Netty协议栈。N...

it_monkey_ali的博客 355

ECU诊断功能测试:从理论到实践

ECU诊断功能是指通过特定的诊断协议与ECU进行通信,以获取ECU的状态信息、故障码、数据流等,并执行特定的诊断操作。诊断功能的主要目的是帮助开发人员和维修人员快速定位和解决ECU中的问题,确保车辆的安全性和可靠性。UDS协议是ISO 14229标准的一部分,定义了一系列的诊断服务,如读取故障码、清除故障码、读取数据流等。UDS协议基于CAN(Controller Area Network)总线,具有较高的灵活性和扩展性。读取故障码是诊断功能中最基本的操作之一。通过读取故障码,可以快速定位ECU中的问题。

闲人编程的博客 1804

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

Netty之私有协议栈开发(五)

广义上区分,通信协议可以分为公有协议和私有协议。由于私有协议的灵活性,它往往会在某个公司或者组织内部使用,按需定制,也因为如此,升级起来会非常方便,灵活性好。绝大多数的私有协议传输层都基于TCP/IP,所以利用Netty的NIO/TCP协议栈可以非常方便地进行私有协议的定制和开发。私有协议本质上是厂商内部发展和采用的标准,除非授权,其他厂商一般无权使用该协议。私有协议也称非标准协议,就是未经国际或...

青鸟飞鱼 3232

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Netty权威指南:Netty总结-私有协议栈开发

本质是厂商内部发展和采用的标准,除非授权不然其他厂商无权使用。具有封闭性、垄断性、排他性等特点。包含两部分,消息头和消息体NettyMessage数据结构可参考之前定义,以及添加get,set方法。

Oceáno的博客 1432

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

【Netty源码解读和权威指南】第10篇:Netty私有协议栈开发——从零设计一套企业级通信协议

摘要 本文详细介绍了基于Netty的企业级私有通信协议设计全过程。主要内容包括: 协议设计:采用16字节固定消息头+变长消息体结构,包含魔数、版本号、消息类型、请求ID等关键字段,支持握手认证、心跳检测等企业级特性。 消息类型:定义了6种核心消息类型(握手请求/响应、心跳请求/响应、业务请求/响应),每种类型有明确的功能定义。 技术实现:提供了MessageHeader和Message的完整Java实现代码,展示了如何构建协议消息对象。 该协议设计具有轻量高效(最小仅16字节)、可扩展(预留字段和版本号)、

xyghehehehe的专栏 180

Netty学习要点

Netty学习要点 1、UNIX的5种I/O模型 2、epoll与select的对比 3、私有协议栈可靠性设计 4、ByteBuf和ByteBuffer对比 6、Netty的线程模型 7、Netty的高性能,可靠性,可定制性,可扩展性的分析 8、多线程编程在Netty中的应用 9、Netty的高性能分析 10、Netty高可靠性设计 UNIX的5种I/O模型:   Linux...

qq_38019655的博客 438

私有协议栈开发

通信协议从广义上区分,可以分为公有协议和私有协议。由于私有协议的灵活性,它往往会在某个公司或者组织内部使用,按需定制,也因为如此,升级起来会非常方便,灵活性好。绝大多数的私有协议传输层都基于TCP/IP,所以利用Netty的NIO TCP协议栈可以非常方便地进行私有协议的定制和开发。 私有协议介绍 私有协议本质上是厂商内部发展和采用的标准,除非授权,其他厂商一般无权使用该协议。私有协议也称非标...

weixin_30493401的博客 160

Netty学习(六)- 基于Netty的私有协议栈开发

大多公司都会有自己的一套私有协议,可能只在自己公司内部使用的协议。 使用Netty实现的私有协议可以用于内部各模块之间的通信,基于TCP/IP协议栈,异步NIO框架,提供高性能、异步化的通信能力。

noaman_wgs的博客 1401

netty权威指南~第12章——私有协议栈开发

广义上区分,通信协议可以分为公有协议和私有协议。由于私有协议的灵活性,它往往会在某个公司或者组织内部使用,按需定制,也因为如此,升级起来会非常方便,灵活性好。绝大多数的私有协议传输层都基于TCP/IP,所以利用Netty的NIO/TCP协议栈可以非常方便地进行私有协议的定制和开发。 私有协议本质上是厂商内部发展和采用的标准,除非授权,其他厂商一般无权使用该协议。私有协议也称非标准协议,就是未经国际...

m0_37941483的博客 319

Netty实现TCP私有协议栈的开发

为什么使用TCP通信? 因为在进行对接机器C语言开发的直播主机)的时候,TCP是一种更安全更高效率的连接,它是一种会确认通信对方,保持通信状态,并且能检查报文完整性的连接,而HTTP则是在TCP服务层上面的应用层,当然TCP通信稍微要复杂一点。 为什么要使用Netty? 先说一下BIO,NIO,AIO的关系: BIO:是一种同步阻塞的io,当io建立连接等待应答的时候,当前的线程就被挂起不能做...

♾️ 1696

Netty 私有协议栈 代码实现

私有协议介绍   通信协议从广义上区分,可以分为共有协议和私有协议。由于私有协议的灵活性,它往往会在某个公司或者组织内部使用,按需定制,也因为如此,升级起来会非常方便,灵活性好。   Netty私有协议栈功能设计   Netty协议栈用于内部各模块之间的通信,它基于TCP/IP协议栈,是一个类似HTTP协议的应用层协议栈,相比与传统的标准协议栈,它更加轻巧、灵活和实用   1.功能描述 ...

weixin_30872671的博客 190

Netty权威指南之私有协议栈开发

相关知识点: 通信协议从广义区分,可以分为公有制协议和私有协议。由于私有协议的灵活性,它往往会在某个公司或组织内部使用,按需定制,也因为如此,升级起来会非常方便,灵活性好。 绝大多数的私有协议传输层都基于TCP/IP,所以利用Netty的NIO TCP协议栈可以非常方便的进行私有协议的定制和开发。 本章学习目标: 1、私有协议介绍 2、基于Netty的私有栈设计 3、私有协

zhouzhiwengang的专栏 2395

Netty自定义协议开发

目录(?)[-]私有协议介绍Netty协议栈功能设计网络拓扑图协议栈功能描述通信模型 消息定义Netty协议支持的字段类型Netty协议的编解码规范链路的建立链路的关闭可靠性设计安全性设计可扩展性设计Netty协议栈开发数据结构定义 消息编解码握手和安全认证 心跳检测机制基础类客户端服务端测试结果通信协议从广义上区分,可以分为公有协议和私有协议。由于私有协议的灵活性,它往往会在某个公司或者组织内部...

liyuling52011的博客 1948
上一篇: 剖析LengthFieldBasedFrameDecoder
下一篇: Netty多人与群组聊天
Coder_py
博客等级 码龄10年 106粉丝 258原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值