netty使用MessageToByteEncoder 自定义协议(四)
开发应用程序与应用程序之间的通信,程序之前通信 需要定义协议,比如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<Message> {
- @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<Object> 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<SocketChannel> {
- 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<SocketChannel> {
- 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();
- }
- }
输出
netty使用MessageToByteEncoder 自定义协议(四)的更多相关文章
- 物联网架构成长之路(35)-利用Netty解析物联网自定义协议
一.前言 前面博客大部分介绍了基于EMQ中间件,通信协议使用的是MQTT,而传输的数据为纯文本数据,采用JSON格式.这种方式,大部分一看就知道是熟悉Web开发.软件开发的人喜欢用的方式.由于我也是做 ...
- 利用Netty构建自定义协议的通信
在复杂的网络世界中,各种应用之间通信需要依赖各种各样的协议,比如:HTTP,Telnet,FTP,SMTP等等. 在开发过程中,有时候我们需要构建一些适应自己业务的应用层协议,Netty作为一个非常优 ...
- 【转】Netty之解决TCP粘包拆包(自定义协议)
1.什么是粘包/拆包 一般所谓的TCP粘包是在一次接收数据不能完全地体现一个完整的消息数据.TCP通讯为何存在粘包呢?主要原因是TCP是以流的方式来处理数据,再加上网络上MTU的往往小于在应用处理的消 ...
- Netty之解决TCP粘包拆包(自定义协议)
1.什么是粘包/拆包 一般所谓的TCP粘包是在一次接收数据不能完全地体现一个完整的消息数据.TCP通讯为何存在粘包呢?主要原因是TCP是以流的方式来处理数据,再加上网络上MTU的往往小于在应用处理的消 ...
- 《精通并发与Netty》学习笔记(14 - 解决TCP粘包拆包(二)Netty自定义协议解决粘包拆包)
一.Netty粘包和拆包解决方案 Netty提供了多个解码器,可以进行分包的操作,分别是: * LineBasedFrameDecoder (换行) LineBasedFrameDecoder是回 ...
- netty 自定义协议
netty 自定义协议 netty 是什么呢? 相信很多人都被人问过这个问题.如果快速准确的回复这个问题呢?网络编程框架,netty可以让你快速和简单的开发出一个高性能的网络应用.netty是一个网络 ...
- Netty自定义协议解析原理与应用
目前,大家都选择Netty做为游戏服务器框架网络通信的框架,而且目前也有很多优秀的产品是基于Netty开发的.它的稳定性,易用性和高效率性已得到广泛的认同.在游戏服务器开发中,选择netty一般就意味 ...
- netty源码解解析(4.0)-20 ChannelHandler: 自己实现一个自定义协议的服务器和客户端
本章不会直接分析Netty源码,而是通过使用Netty的能力实现一个自定义协议的服务器和客户端.通过这样的实践,可以更深刻地理解Netty的相关代码,同时可以了解,在设计实现自定义协议的过程中需要解决 ...
- java-cef系列视频第四集:自定义协议
上一集我们介绍了如何为java-cef添加flashplayer支持. 本视频介绍java-cef中的自定义协议 本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 中国大陆许可协议进行许可.
随机推荐
- Codeforces Round #332 (Div. 2)C. Day at the Beach 树状数组
C. Day at the Beach One day Squidward, Spongebob and Patrick decided to go to the beach. Unfortuna ...
- hihocoder 1680 hiho字符串2 dp求方案数+递归
我们定义第一代hiho字符串是"hiho". 第N代hiho字符串是由第N-1代hiho字符串变化得到,规则是: h -> hio i -> hi o -> ho ...
- 【JLOI 2014】 松鼠的新家
[题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=3631 [算法] 树上差分 [代码] #include<bits/stdc++. ...
- git 设定全局ignore
创建: 2017/08/08 位置: $HOME/.config/git/ignore git/ignore 要自建 内容 https://github.com/github/gitignore ...
- Oracle创建用户教程
计算机-->管理-->应用程序与服务-->(OracleOraDb11g_home1TNSListener 和 OracleServiceORCL 服务)->启动服务 打开Or ...
- POJ 3114 Tarjan+Dijkstra
题意: 间谍在战争期间想要传递一份谍报回国,谍报可以在邮局之间传递,但这种传递是单向的,并且会少耗一些时间.但是如果两个邮局在同一个国家的话,那么谍报在这两个邮局之间传递是不消耗时间的.如果几个邮局发 ...
- C# Area 双重路由如何写
在WebApi项目里面 一般除了接口, 还有管理端...一些乱七八糟的,你想展示的东西, 一种做法是分开写: 比如管理后台一个项目, 然后接口一个, 然后页面一个, 其实这样做也可以,但是这么做, 无 ...
- swift-delegate(代理)或者block传值
1:delegate或者block传值 import UIKit class ViewController: UIViewController,TestDelegatePassValueDelegat ...
- div自动适应浏览器窗口水平和垂直居中
html <body> <div id="centerDiv">自动适应水平和垂直居中</div> </body> css ;;} ...
- Android贝塞尔曲线应用-跳动的水滴
主要通过6个控制点实现. val startPoint = PointF() val endPoint = PointF() val control1 = PointF() val control2 ...