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

首先我们定义一个协议类

  1. package com.liqiang.SimpeEcode;
  2.  
  3. import java.sql.Date;
  4. import java.text.SimpleDateFormat;
  5. import java.util.Arrays;
  6.  
  7. import com.liqiang.nettyTest2.Md5Utils;
  8.  
  9. /**
  10. * 自定义协议 数据包格式
  11. * -----------------------------------
  12. * | 协议开始标志 | 包长度|令牌 (定长50个字节)|令牌生成时间(定长50个字节)| 包内容 |
  13. * -----------------------------------
  14. * 令牌生成规则
  15. * 协议开始标志 +包长度+令牌生成时间+包内容+服务器与客户端约定的秘钥
  16. * @author Administrator
  17. *
  18. */
  19. public class Message {
  20. public Message(MessageHead head,byte[] content) {
  21. this.head=head;
  22. this.content=content;
  23. }
  24. // 协议头
  25. private MessageHead head;
  26.  
  27. // 内容
  28. private byte[] content;
  29.  
  30. public MessageHead getHead() {
  31. return head;
  32. }
  33.  
  34. public void setHead(MessageHead head) {
  35. this.head = head;
  36. }
  37.  
  38. public byte[] getContent() {
  39. return content;
  40. }
  41.  
  42. public void setContent(byte[] content) {
  43. this.content = content;
  44. }
  45. @Override
  46. public String toString() {
  47. // TODO Auto-generated method stub
  48. return "[head:"+head.toString()+"]"+"content:"+new String(content);
  49. }
  50.  
  51. /**
  52. * 生成token 协议开始标志 +包长度+令牌生成时间+包内容+服务器与客户端约定的秘钥
  53. * @return
  54. */
  55. public String buidToken() {
  56. //生成token
  57. SimpleDateFormat format0 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  58. String time = format0.format(this.getHead().getCreateDate());// 这个就是把时间戳经过处理得到期望格式的时间
  59. String allData=String.valueOf(this.getHead().getHeadData());
  60. allData+=String.valueOf(this.getHead().getLength());
  61. allData+=time;
  62. allData+=new String(this.getContent());
  63. allData+="11111";//秘钥
  64. return Md5Utils.stringMD5(allData);
  65.  
  66. }
  67. /**
  68. * 验证是否认证通过
  69. * @param token
  70. * @return
  71. */
  72. public boolean authorization(String token) {
  73. //表示参数被修改
  74. if(!token.equals(this.getHead().getToken())) {
  75. return false;
  76. }
  77. //验证是否失效
  78. Long s = (System.currentTimeMillis() - getHead().getCreateDate().getTime()) / (1000 * 60);
  79. if(s>60) {
  80. return false;
  81. }
  82. return true;
  83. }
  84.  
  85. }

Head类

  1. package com.liqiang.SimpeEcode;
  2.  
  3. import java.text.SimpleDateFormat;
  4. import java.util.Date;
  5.  
  6. public class MessageHead {
  7. private int headData=0X76;//协议开始标志
  8. private int length;//包的长度
  9. private String token;
  10. private Date createDate;
  11.  
  12. public int getHeadData() {
  13. return headData;
  14. }
  15. public void setHeadData(int headData) {
  16. this.headData = headData;
  17. }
  18. public int getLength() {
  19. return length;
  20. }
  21. public void setLength(int length) {
  22. this.length = length;
  23. }
  24.  
  25. public String getToken() {
  26. return token;
  27. }
  28. public void setToken(String token) {
  29. this.token = token;
  30. }
  31. public Date getCreateDate() {
  32. return createDate;
  33. }
  34. public void setCreateDate(Date createDate) {
  35. this.createDate = createDate;
  36. }
  37. @Override
  38. public String toString() {
  39. SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  40. // TODO Auto-generated method stub
  41. return "headData:"+headData+",length:"+length+",token:"+token+",createDate:"+ simpleDateFormat.format(createDate);
  42. }
  43. }

自定义的编码器

  1. package com.liqiang.SimpeEcode;
  2.  
  3. import java.text.SimpleDateFormat;
  4. import java.util.Arrays;
  5. import java.util.Calendar;
  6.  
  7. import io.netty.buffer.ByteBuf;
  8. import io.netty.channel.ChannelHandlerContext;
  9. import io.netty.handler.codec.MessageToByteEncoder;
  10.  
  11. public class MessageEncoder extends MessageToByteEncoder<Message> {
  12.  
  13. @Override
  14. protected void encode(ChannelHandlerContext ctx, Message msg, ByteBuf out) throws Exception {
  15. // TODO Auto-generated method stub
  16. // 写入开头的标志
  17. out.writeInt(msg.getHead().getHeadData());
  18. // 写入包的的长度
  19. out.writeInt(msg.getContent().length);
  20. byte[] tokenByte = new byte[50];
  21. /**
  22. * token定长50个字节
  23. * 第一个参数 原数组
  24. * 第二个参数 原数组位置
  25. * 第三个参数 目标数组
  26. * 第四个参数 目标数组位置
  27. * 第五个参数 copy多少个长度
  28. */
  29. byte[] indexByte=msg.getHead().getToken().getBytes();
  30. try {
  31. System.arraycopy(indexByte, 0, tokenByte, 0,indexByte.length>tokenByte.length?tokenByte.length:indexByte.length);
  32. } catch (Exception e) {
  33. // TODO: handle exception
  34. e.printStackTrace();
  35. }
  36.  
  37. //写入令牌
  38. out.writeBytes(tokenByte);
  39. byte[] createTimeByte = new byte[50];
  40. SimpleDateFormat format0 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  41. String time = format0.format(msg.getHead().getCreateDate());
  42. indexByte=time.getBytes();
  43. System.arraycopy(indexByte, 0, createTimeByte, 0,indexByte.length>createTimeByte.length?createTimeByte.length:indexByte.length);
  44. //写入令牌生成时间
  45. out.writeBytes(createTimeByte);
  46.  
  47. // 写入消息主体
  48. out.writeBytes(msg.getContent());
  49.  
  50. }
  51.  
  52. }

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

解码器

  1. package com.liqiang.SimpeEcode;
  2.  
  3. import java.text.SimpleDateFormat;
  4. import java.util.List;
  5. import com.liqiang.nettyTest2.nettyMain;
  6.  
  7. import io.netty.buffer.ByteBuf;
  8. import io.netty.channel.ChannelHandlerContext;
  9. import io.netty.handler.codec.ByteToMessageDecoder;
  10. import io.netty.handler.codec.MessageToByteEncoder;
  11. import io.netty.handler.codec.MessageToMessageDecoder;
  12.  
  13. public class MessageDecode extends ByteToMessageDecoder{
  14.  
  15. private final int BASE_LENGTH=4+4+50+50;//协议头 类型 int+length 4个字节+令牌和 令牌生成时间50个字节
  16. private int headData=0X76;//协议开始标志
  17.  
  18. @Override
  19. protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) throws Exception {
  20. // 刻度长度必须大于基本长度
  21. if(buffer.readableBytes()>=BASE_LENGTH) {
  22. /**
  23. * 粘包 发送频繁 可能多次发送黏在一起 需要考虑 不过一个客户端发送太频繁也可以推断是否是攻击
  24. */
  25. //防止soket流攻击。客户端传过来的数据太大不合理
  26. if(buffer.readableBytes()>2048) {
  27. //buffer.skipBytes(buffer.readableBytes());
  28.  
  29. }
  30. }
  31. int beginIndex;//记录包开始位置
  32. while(true) {
  33. // 获取包头开始的index
  34. beginIndex = buffer.readerIndex();
  35. //如果读到开始标记位置 结束读取避免拆包和粘包
  36. if(buffer.readInt()==headData) {
  37. break;
  38. }
  39.  
  40. //初始化读的index为0
  41. buffer.resetReaderIndex();
  42. // 当略过,一个字节之后,
  43. //如果当前buffer数据小于基础数据 返回等待下一次读取
  44. if (buffer.readableBytes() < BASE_LENGTH) {
  45. return;
  46. }
  47. }
  48. // 消息的长度
  49. int length = buffer.readInt();
  50. // 判断请求数据包数据是否到齐
  51. if ((buffer.readableBytes()-100) < length) {
  52. //没有到期 返回读的指针 等待下一次数据到期再读
  53. buffer.readerIndex(beginIndex);
  54. return;
  55. }
  56. //读取令牌
  57. byte[] tokenByte=new byte[50];
  58. buffer.readBytes(tokenByte);
  59.  
  60. //读取令牌生成时间
  61. byte[]createDateByte=new byte[50];
  62. buffer.readBytes(createDateByte);
  63. //读取content
  64. byte[] data = new byte[length];
  65. buffer.readBytes(data);
  66. MessageHead head=new MessageHead();
  67. head.setHeadData(headData);
  68. head.setToken(new String(tokenByte).trim());
  69. SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  70. head.setCreateDate( simpleDateFormat.parse(new String(createDateByte).trim()));
  71. head.setLength(length);
  72. Message message=new Message(head, data);
  73. //认证不通过
  74. if(!message.authorization(message.buidToken())) {
  75. ctx.close();
  76.  
  77. return;
  78. }
  79. out.add(message);
  80. buffer.discardReadBytes();//回收已读字节
  81. }
  82.  
  83. }

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

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

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

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

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

5.message.authorization   

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

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

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

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

Server

  1. package com.liqiang.nettyTest2;
  2.  
  3. import com.liqiang.SimpeEcode.MessageDecode;
  4. import com.liqiang.SimpeEcode.MessageEncoder;
  5.  
  6. import io.netty.buffer.Unpooled;
  7. import io.netty.channel.ChannelInitializer;
  8. import io.netty.channel.socket.SocketChannel;
  9. import io.netty.handler.codec.LineBasedFrameDecoder;
  10. import io.netty.handler.codec.AsciiHeadersEncoder.NewlineType;
  11. import io.netty.handler.codec.DelimiterBasedFrameDecoder;
  12. import io.netty.handler.codec.string.StringDecoder;
  13. import io.netty.handler.codec.string.StringEncoder;
  14.  
  15. public class ServerChannelInitializer extends ChannelInitializer<SocketChannel> {
  16.  
  17. private Server server;
  18. public ServerChannelInitializer(Server server) {
  19. this.server=server;
  20. }
  21. @Override
  22. protected void initChannel(SocketChannel channel) throws Exception {
  23. // TODO Auto-generated method stub
  24. channel.pipeline()
  25.  
  26. .addLast("decoder",new MessageDecode())
  27. .addLast("encoder",new MessageEncoder())
  28. .addLast(new ServerHandle(server));
  29. }
  30.  
  31. }

Client

  1. package com.liqiang.nettyTest2;
  2.  
  3. import com.liqiang.SimpeEcode.MessageDecode;
  4. import com.liqiang.SimpeEcode.MessageEncoder;
  5.  
  6. import io.netty.channel.ChannelInitializer;
  7. import io.netty.channel.socket.SocketChannel;
  8. import io.netty.handler.codec.string.StringDecoder;
  9. import io.netty.handler.codec.string.StringEncoder;
  10.  
  11. public class ClientChannelInitializer extends ChannelInitializer<SocketChannel> {
  12.  
  13. private Client client;
  14. public ClientChannelInitializer(Client client) {
  15. // TODO Auto-generated constructor stub
  16. this.client=client;
  17. }
  18. @Override
  19. protected void initChannel(SocketChannel socketChannel) throws Exception {
  20. // TODO Auto-generated method stub
  21. socketChannel.pipeline()
  22. .addLast("encoder",new MessageEncoder())
  23. .addLast("decode",new MessageDecode())
  24. .addLast(new ClientHandle(client));//注册处理器
  25.  
  26. }
  27. }

测试运行

  1. package com.liqiang.nettyTest2;
  2.  
  3. import java.text.DateFormat;
  4. import java.text.SimpleDateFormat;
  5. import java.util.Date;
  6.  
  7. import javax.management.StringValueExp;
  8. import javax.swing.text.StringContent;
  9.  
  10. import com.liqiang.SimpeEcode.Message;
  11. import com.liqiang.SimpeEcode.MessageHead;
  12.  
  13. public class nettyMain {
  14. public static void main(String[] args) {
  15. new Thread(new Runnable() {
  16.  
  17. @Override
  18. public void run() {
  19. // TODO Auto-generated method stub
  20. Server server = new Server(8081);
  21. server.start();
  22.  
  23. }
  24. }).start();
  25. new Thread(new Runnable() {
  26.  
  27. @Override
  28. public void run() {
  29. // TODO Auto-generated method stub
  30. Client client1 = new Client("127.0.0.1", 8081);
  31. client1.connection();
  32. String content = "哈哈哈哈!";
  33. byte[] bts = content.getBytes();
  34. MessageHead head = new MessageHead();
  35. // 令牌生成时间
  36. head.setCreateDate(new Date());
  37.  
  38. head.setLength(bts.length);
  39. Message message = new Message(head, bts);
  40. message.getHead().setToken(message.buidToken());
  41.  
  42. message.getHead().setToken(message.buidToken());
  43. client1.sendMsg(message);
  44. try {
  45. Thread.sleep(2000);
  46. } catch (InterruptedException e) {
  47. // TODO Auto-generated catch block
  48. e.printStackTrace();
  49. }
  50.  
  51. //token错误 则认为是非法客户端会关闭连接
  52. message.getHead().setToken("fff");
  53.  
  54. client1.sendMsg(message);
  55. //再次发送 服务端则收不到
  56. message.getHead().setToken(message.buidToken());
  57. client1.sendMsg(message);
  58.  
  59. }
  60. }).start();
  61. }
  62.  
  63. }

输出

netty使用MessageToByteEncoder 自定义协议(四)的更多相关文章

  1. 物联网架构成长之路(35)-利用Netty解析物联网自定义协议

    一.前言 前面博客大部分介绍了基于EMQ中间件,通信协议使用的是MQTT,而传输的数据为纯文本数据,采用JSON格式.这种方式,大部分一看就知道是熟悉Web开发.软件开发的人喜欢用的方式.由于我也是做 ...

  2. 利用Netty构建自定义协议的通信

    在复杂的网络世界中,各种应用之间通信需要依赖各种各样的协议,比如:HTTP,Telnet,FTP,SMTP等等. 在开发过程中,有时候我们需要构建一些适应自己业务的应用层协议,Netty作为一个非常优 ...

  3. 【转】Netty之解决TCP粘包拆包(自定义协议)

    1.什么是粘包/拆包 一般所谓的TCP粘包是在一次接收数据不能完全地体现一个完整的消息数据.TCP通讯为何存在粘包呢?主要原因是TCP是以流的方式来处理数据,再加上网络上MTU的往往小于在应用处理的消 ...

  4. Netty之解决TCP粘包拆包(自定义协议)

    1.什么是粘包/拆包 一般所谓的TCP粘包是在一次接收数据不能完全地体现一个完整的消息数据.TCP通讯为何存在粘包呢?主要原因是TCP是以流的方式来处理数据,再加上网络上MTU的往往小于在应用处理的消 ...

  5. 《精通并发与Netty》学习笔记(14 - 解决TCP粘包拆包(二)Netty自定义协议解决粘包拆包)

    一.Netty粘包和拆包解决方案 Netty提供了多个解码器,可以进行分包的操作,分别是: * LineBasedFrameDecoder (换行)   LineBasedFrameDecoder是回 ...

  6. netty 自定义协议

    netty 自定义协议 netty 是什么呢? 相信很多人都被人问过这个问题.如果快速准确的回复这个问题呢?网络编程框架,netty可以让你快速和简单的开发出一个高性能的网络应用.netty是一个网络 ...

  7. Netty自定义协议解析原理与应用

    目前,大家都选择Netty做为游戏服务器框架网络通信的框架,而且目前也有很多优秀的产品是基于Netty开发的.它的稳定性,易用性和高效率性已得到广泛的认同.在游戏服务器开发中,选择netty一般就意味 ...

  8. netty源码解解析(4.0)-20 ChannelHandler: 自己实现一个自定义协议的服务器和客户端

    本章不会直接分析Netty源码,而是通过使用Netty的能力实现一个自定义协议的服务器和客户端.通过这样的实践,可以更深刻地理解Netty的相关代码,同时可以了解,在设计实现自定义协议的过程中需要解决 ...

  9. java-cef系列视频第四集:自定义协议

    上一集我们介绍了如何为java-cef添加flashplayer支持. 本视频介绍java-cef中的自定义协议 本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 中国大陆许可协议进行许可.

随机推荐

  1. 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 ...

  2. hihocoder 1680 hiho字符串2 dp求方案数+递归

    我们定义第一代hiho字符串是"hiho". 第N代hiho字符串是由第N-1代hiho字符串变化得到,规则是: h -> hio i -> hi o -> ho ...

  3. 【JLOI 2014】 松鼠的新家

    [题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=3631 [算法] 树上差分 [代码] #include<bits/stdc++. ...

  4. git 设定全局ignore

    创建: 2017/08/08   位置: $HOME/.config/git/ignore git/ignore 要自建 内容  https://github.com/github/gitignore ...

  5. Oracle创建用户教程

    计算机-->管理-->应用程序与服务-->(OracleOraDb11g_home1TNSListener 和 OracleServiceORCL 服务)->启动服务 打开Or ...

  6. POJ 3114 Tarjan+Dijkstra

    题意: 间谍在战争期间想要传递一份谍报回国,谍报可以在邮局之间传递,但这种传递是单向的,并且会少耗一些时间.但是如果两个邮局在同一个国家的话,那么谍报在这两个邮局之间传递是不消耗时间的.如果几个邮局发 ...

  7. C# Area 双重路由如何写

    在WebApi项目里面 一般除了接口, 还有管理端...一些乱七八糟的,你想展示的东西, 一种做法是分开写: 比如管理后台一个项目, 然后接口一个, 然后页面一个, 其实这样做也可以,但是这么做, 无 ...

  8. swift-delegate(代理)或者block传值

    1:delegate或者block传值 import UIKit class ViewController: UIViewController,TestDelegatePassValueDelegat ...

  9. div自动适应浏览器窗口水平和垂直居中

    html <body> <div id="centerDiv">自动适应水平和垂直居中</div> </body> css ;;} ...

  10. Android贝塞尔曲线应用-跳动的水滴

    主要通过6个控制点实现. val startPoint = PointF() val endPoint = PointF() val control1 = PointF() val control2 ...