博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
netty使用MessageToByteEncoder 自定义协议(四)
阅读量:4313 次
发布时间:2019-06-06

本文共 11828 字,大约阅读时间需要 39 分钟。

开发应用程序与应用程序之间的通信,程序之前通信 需要定义协议,比如http协议。

首先我们定义一个协议类

package com.liqiang.SimpeEcode;import java.sql.Date;import java.text.SimpleDateFormat;import java.util.Arrays;import com.liqiang.nettyTest2.Md5Utils;/** * 自定义协议 数据包格式 * ----------------------------------- * | 协议开始标志 | 包长度|令牌 (定长50个字节)|令牌生成时间(定长50个字节)| 包内容 |    * ----------------------------------- * 令牌生成规则 *  协议开始标志 +包长度+令牌生成时间+包内容+服务器与客户端约定的秘钥 * @author Administrator * */public class Message {    public Message(MessageHead head,byte[] content) {        this.head=head;        this.content=content;    }    // 协议头    private MessageHead head;    // 内容    private byte[] content;    public MessageHead getHead() {        return head;    }    public void setHead(MessageHead head) {        this.head = head;    }    public byte[] getContent() {        return content;    }    public void setContent(byte[] content) {        this.content = content;    }    @Override    public String toString() {        // TODO Auto-generated method stub        return "[head:"+head.toString()+"]"+"content:"+new String(content);    }        /**     * 生成token   协议开始标志 +包长度+令牌生成时间+包内容+服务器与客户端约定的秘钥     * @return     */    public String buidToken() {        //生成token        SimpleDateFormat format0 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");        String time = format0.format(this.getHead().getCreateDate());// 这个就是把时间戳经过处理得到期望格式的时间        String allData=String.valueOf(this.getHead().getHeadData());        allData+=String.valueOf(this.getHead().getLength());        allData+=time;        allData+=new String(this.getContent());        allData+="11111";//秘钥        return Md5Utils.stringMD5(allData);    }    /**     * 验证是否认证通过     * @param token     * @return     */    public boolean authorization(String token) {        //表示参数被修改        if(!token.equals(this.getHead().getToken())) {            return false;        }        //验证是否失效        Long s = (System.currentTimeMillis() - getHead().getCreateDate().getTime()) / (1000 * 60);        if(s>60) {            return false;        }        return true;    }}

Head类

package com.liqiang.SimpeEcode;import java.text.SimpleDateFormat;import java.util.Date;public class MessageHead {     private int headData=0X76;//协议开始标志        private int length;//包的长度        private String token;        private Date createDate;               public int getHeadData() {            return headData;        }        public void setHeadData(int headData) {            this.headData = headData;        }        public int getLength() {            return length;        }        public void setLength(int length) {            this.length = length;        }                        public String getToken() {            return token;        }        public void setToken(String token) {            this.token = token;        }        public Date getCreateDate() {            return createDate;        }        public void setCreateDate(Date createDate) {            this.createDate = createDate;        }        @Override        public String toString() {            SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");            // TODO Auto-generated method stub            return "headData:"+headData+",length:"+length+",token:"+token+",createDate:"+    simpleDateFormat.format(createDate);        }}

自定义的编码器

package com.liqiang.SimpeEcode;import java.text.SimpleDateFormat;import java.util.Arrays;import java.util.Calendar;import io.netty.buffer.ByteBuf;import io.netty.channel.ChannelHandlerContext;import io.netty.handler.codec.MessageToByteEncoder;public class MessageEncoder extends MessageToByteEncoder
{ @Override protected void encode(ChannelHandlerContext ctx, Message msg, ByteBuf out) throws Exception { // TODO Auto-generated method stub // 写入开头的标志 out.writeInt(msg.getHead().getHeadData()); // 写入包的的长度 out.writeInt(msg.getContent().length); byte[] tokenByte = new byte[50]; /** * token定长50个字节 * 第一个参数 原数组 * 第二个参数 原数组位置 * 第三个参数 目标数组 * 第四个参数 目标数组位置 * 第五个参数 copy多少个长度 */ byte[] indexByte=msg.getHead().getToken().getBytes(); try { System.arraycopy(indexByte, 0, tokenByte, 0,indexByte.length>tokenByte.length?tokenByte.length:indexByte.length); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } //写入令牌 out.writeBytes(tokenByte); byte[] createTimeByte = new byte[50]; SimpleDateFormat format0 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String time = format0.format(msg.getHead().getCreateDate()); indexByte=time.getBytes(); System.arraycopy(indexByte, 0, createTimeByte, 0,indexByte.length>createTimeByte.length?createTimeByte.length:indexByte.length); //写入令牌生成时间 out.writeBytes(createTimeByte); // 写入消息主体 out.writeBytes(msg.getContent()); }}

按照message注释的协议顺序 写入。token和token生成时间定长50 不足空补

 

解码器

 

package com.liqiang.SimpeEcode;import java.text.SimpleDateFormat;import java.util.List;import com.liqiang.nettyTest2.nettyMain;import io.netty.buffer.ByteBuf;import io.netty.channel.ChannelHandlerContext;import io.netty.handler.codec.ByteToMessageDecoder;import io.netty.handler.codec.MessageToByteEncoder;import io.netty.handler.codec.MessageToMessageDecoder;public class MessageDecode extends ByteToMessageDecoder{    private final int BASE_LENGTH=4+4+50+50;//协议头 类型 int+length 4个字节+令牌和 令牌生成时间50个字节    private int headData=0X76;//协议开始标志        @Override    protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List out) throws Exception {        // 刻度长度必须大于基本长度        if(buffer.readableBytes()>=BASE_LENGTH) {            /**             * 粘包 发送频繁 可能多次发送黏在一起 需要考虑  不过一个客户端发送太频繁也可以推断是否是攻击             */            //防止soket流攻击。客户端传过来的数据太大不合理            if(buffer.readableBytes()>2048) {                //buffer.skipBytes(buffer.readableBytes());                            }        }        int beginIndex;//记录包开始位置        while(true) {              // 获取包头开始的index              beginIndex = buffer.readerIndex();              //如果读到开始标记位置 结束读取避免拆包和粘包            if(buffer.readInt()==headData) {                break;            }                         //初始化读的index为0            buffer.resetReaderIndex();              // 当略过,一个字节之后,              //如果当前buffer数据小于基础数据 返回等待下一次读取            if (buffer.readableBytes() < BASE_LENGTH) {                  return;              }          }           // 消息的长度          int length = buffer.readInt();          // 判断请求数据包数据是否到齐          if ((buffer.readableBytes()-100) < length) {              //没有到期 返回读的指针 等待下一次数据到期再读            buffer.readerIndex(beginIndex);              return;          }          //读取令牌        byte[] tokenByte=new byte[50];        buffer.readBytes(tokenByte);               //读取令牌生成时间        byte[]createDateByte=new byte[50];        buffer.readBytes(createDateByte);        //读取content        byte[] data = new byte[length];          buffer.readBytes(data);         MessageHead head=new MessageHead();        head.setHeadData(headData);        head.setToken(new String(tokenByte).trim());        SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");        head.setCreateDate(  simpleDateFormat.parse(new String(createDateByte).trim()));        head.setLength(length);        Message message=new Message(head, data);        //认证不通过        if(!message.authorization(message.buidToken())) {            ctx.close();                        return;        }        out.add(message);        buffer.discardReadBytes();//回收已读字节    }    }

解码器 在解码的同时需要做拆包和粘包处理

    1.循环读到包分割符起始位置

    2.判断可读的包长度是否大于基本数据长度 如果不大于表示 拆包了 head部分没有发完。等待下一次处理

    3.如果head部分发过来了  通过length 判断剩余可读部分 是否大于等于content内容长度 如果小于 表示 内容部分没有发完等待下一次处理

    4.如果都满足 则解析head部分 再根据length解析包内容 封装到message

    5.message.authorization   

             1.首先按照我们token生成规则 生成字符串 +加密秘钥 生成token

             2.2个token对比是否相等。如果不相等表示参数被窜改 或者加密秘钥有问题。是非法请求

             3.如果token相等 判断时间是否超过1分种。避免别人抓到我们的包内容根据我们的包内容循环发送请求

   服务端和客户端应用上编码器

Server

package com.liqiang.nettyTest2;import com.liqiang.SimpeEcode.MessageDecode;import com.liqiang.SimpeEcode.MessageEncoder;import io.netty.buffer.Unpooled;import io.netty.channel.ChannelInitializer;import io.netty.channel.socket.SocketChannel;import io.netty.handler.codec.LineBasedFrameDecoder;import io.netty.handler.codec.AsciiHeadersEncoder.NewlineType;import io.netty.handler.codec.DelimiterBasedFrameDecoder;import io.netty.handler.codec.string.StringDecoder;import io.netty.handler.codec.string.StringEncoder;public class ServerChannelInitializer extends ChannelInitializer
{ private Server server; public ServerChannelInitializer(Server server) { this.server=server; } @Override protected void initChannel(SocketChannel channel) throws Exception { // TODO Auto-generated method stub channel.pipeline() .addLast("decoder",new MessageDecode()) .addLast("encoder",new MessageEncoder()) .addLast(new ServerHandle(server)); }}

Client

package com.liqiang.nettyTest2;import com.liqiang.SimpeEcode.MessageDecode;import com.liqiang.SimpeEcode.MessageEncoder;import io.netty.channel.ChannelInitializer;import io.netty.channel.socket.SocketChannel;import io.netty.handler.codec.string.StringDecoder;import io.netty.handler.codec.string.StringEncoder;public class ClientChannelInitializer extends ChannelInitializer
{ private Client client; public ClientChannelInitializer(Client client) { // TODO Auto-generated constructor stub this.client=client; } @Override protected void initChannel(SocketChannel socketChannel) throws Exception { // TODO Auto-generated method stub socketChannel.pipeline() .addLast("encoder",new MessageEncoder()) .addLast("decode",new MessageDecode()) .addLast(new ClientHandle(client));//注册处理器 }}

 

测试运行

package com.liqiang.nettyTest2;import java.text.DateFormat;import java.text.SimpleDateFormat;import java.util.Date;import javax.management.StringValueExp;import javax.swing.text.StringContent;import com.liqiang.SimpeEcode.Message;import com.liqiang.SimpeEcode.MessageHead;public class nettyMain {    public static void main(String[] args) {        new Thread(new Runnable() {            @Override            public void run() {                // TODO Auto-generated method stub                Server server = new Server(8081);                server.start();            }        }).start();        new Thread(new Runnable() {            @Override            public void run() {                // TODO Auto-generated method stub                Client client1 = new Client("127.0.0.1", 8081);                client1.connection();                String content = "哈哈哈哈!";                byte[] bts = content.getBytes();                MessageHead head = new MessageHead();                // 令牌生成时间                head.setCreateDate(new Date());                head.setLength(bts.length);                Message message = new Message(head, bts);                message.getHead().setToken(message.buidToken());                message.getHead().setToken(message.buidToken());                client1.sendMsg(message);                try {                    Thread.sleep(2000);                } catch (InterruptedException e) {                    // TODO Auto-generated catch block                    e.printStackTrace();                }                //token错误 则认为是非法客户端会关闭连接                message.getHead().setToken("fff");                client1.sendMsg(message);                //再次发送 服务端则收不到                message.getHead().setToken(message.buidToken());                client1.sendMsg(message);            }        }).start();    }}

输出

 

 

转载于:https://www.cnblogs.com/LQBlog/p/9159134.html

你可能感兴趣的文章
PHP 实现笛卡尔积
查看>>
Laravel中的$loop
查看>>
CentOS7 重置root密码
查看>>
Centos安装Python3
查看>>
PHP批量插入
查看>>
laravel连接sql server 2008
查看>>
Laravel框架学习笔记之任务调度(定时任务)
查看>>
laravel 定时任务秒级执行
查看>>
浅析 Laravel 官方文档推荐的 Nginx 配置
查看>>
Swagger在Laravel项目中的使用
查看>>
Laravel 的生命周期
查看>>
CentOS Docker 安装
查看>>
Nginx
查看>>
Navicat远程连接云主机数据库
查看>>
Nginx配置文件nginx.conf中文详解(总结)
查看>>
MySQL innert join、left join、right join等理解
查看>>
sdc时序约束
查看>>
NoC片上网络
查看>>
开源SoC整理
查看>>
【2020-3-21】Mac安装Homebrew慢,解决办法
查看>>