AIO系列文档(1)----图解ByteBuffer中介绍了ByteBuffer用法,下面通过介绍t-io介绍如何使用:

    1. hello world例子简介

      本例子演示的是一个典型的TCP长连接应用,代码位于example/helloworld目录中。

      • 服务端和客户端的消息协议比较简单,消息头为4个字节,用以表示消息体的长度,消息体为一个字符串的byte[]
      • 服务端先启动,监听6789端口
      • 客户端连接到服务端后,会主动向服务器发送一条消息
      • 服务器收到消息后会回应一条消息
      • 之后,框架层会自动从客户端发心跳到服务器,服务器也会检测心跳有没有超时
      • 框架层会在断链后自动重连(对t-io来说,只需多一行代码便拥有自动重连功能
    2. 公共模块代码

      在pom.xml文件中引入tio-core

      <dependency>
      <groupId>org.t-io</groupId>
      <artifactId>tio-core</artifactId>
      <version>2.0.1.v20171015-RELEASE</version>
      </dependency>

      定义Packet(注:有时候服务器和客户端的业务消息包结构不一样,这种情况下,消息包的定义就不要放在公共模块中,而是在服务端和客户端分别定义)

      package org.tio.examples.helloworld.common;
      
      import org.tio.core.intf.Packet;
      
      /**
      * @author tanyaowu
      */
      public class HelloPacket extends Packet {
      private static final long serialVersionUID = -172060606924066412L;
      public static final int HEADER_LENGHT = 4;//消息头的长度
      public static final String CHARSET = "utf-8";
      private byte[] body; /**
      * @return the body
      */
      public byte[] getBody() {
      return body;
      } /**
      * @param body the body to set
      */
      public void setBody(byte[] body) {
      this.body = body;
      }
      }

      定义服务器端和客户端都用得到的常量

      package org.tio.examples.helloworld.common;
      
      /**
      *
      * @author tanyaowu
      * 2017年3月30日 下午7:05:54
      */
      public interface Const {
      /**
      * 服务器地址
      */
      public static final String SERVER = "127.0.0.1"; /**
      * 监听端口
      */
      public static final int PORT = 6789; /**
      * 心跳超时时间
      */
      public static final int TIMEOUT = 5000;
      }
    3. 服务端代码

      实现org.tio.server.intf.ServerAioHandler

      package org.tio.examples.helloworld.server;
      
      import java.nio.ByteBuffer;
      
      import org.tio.core.Aio;
      import org.tio.core.ChannelContext;
      import org.tio.core.GroupContext;
      import org.tio.core.exception.AioDecodeException;
      import org.tio.core.intf.Packet;
      import org.tio.examples.helloworld.common.HelloPacket;
      import org.tio.server.intf.ServerAioHandler; /**
      * @author tanyaowu
      */
      public class HelloServerAioHandler implements ServerAioHandler { /**
      * 解码:把接收到的ByteBuffer,解码成应用可以识别的业务消息包
      * 总的消息结构:消息头 + 消息体
      * 消息头结构: 4个字节,存储消息体的长度
      * 消息体结构: 对象的json串的byte[]
      */
      @Override
      public HelloPacket decode(ByteBuffer buffer, ChannelContext channelContext) throws AioDecodeException {
      int readableLength = buffer.limit() - buffer.position();
      //收到的数据组不了业务包,则返回null以告诉框架数据不够
      if (readableLength < HelloPacket.HEADER_LENGHT) {
      return null;
      } //读取消息体的长度
      int bodyLength = buffer.getInt(); //数据不正确,则抛出AioDecodeException异常
      if (bodyLength < 0) {
      throw new AioDecodeException("bodyLength [" + bodyLength + "] is not right, remote:" + channelContext.getClientNode());
      } //计算本次需要的数据长度
      int neededLength = HelloPacket.HEADER_LENGHT + bodyLength;
      //收到的数据是否足够组包
      int isDataEnough = readableLength - neededLength;
      // 不够消息体长度(剩下的buffe组不了消息体)
      if (isDataEnough < 0) {
      return null;
      } else //组包成功
      {
      HelloPacket imPacket = new HelloPacket();
      if (bodyLength > 0) {
      byte[] dst = new byte[bodyLength];
      buffer.get(dst);
      imPacket.setBody(dst);
      }
      return imPacket;
      }
      } /**
      * 编码:把业务消息包编码为可以发送的ByteBuffer
      * 总的消息结构:消息头 + 消息体
      * 消息头结构: 4个字节,存储消息体的长度
      * 消息体结构: 对象的json串的byte[]
      */
      @Override
      public ByteBuffer encode(Packet packet, GroupContext groupContext, ChannelContext channelContext) {
      HelloPacket helloPacket = (HelloPacket) packet;
      byte[] body = helloPacket.getBody();
      int bodyLen = 0;
      if (body != null) {
      bodyLen = body.length;
      } //bytebuffer的总长度是 = 消息头的长度 + 消息体的长度
      int allLen = HelloPacket.HEADER_LENGHT + bodyLen;
      //创建一个新的bytebuffer
      ByteBuffer buffer = ByteBuffer.allocate(allLen);
      //设置字节序
      buffer.order(groupContext.getByteOrder()); //写入消息头----消息头的内容就是消息体的长度
      buffer.putInt(bodyLen); //写入消息体
      if (body != null) {
      buffer.put(body);
      }
      return buffer;
      } /**
      * 处理消息
      */
      @Override
      public void handler(Packet packet, ChannelContext channelContext) throws Exception {
      HelloPacket helloPacket = (HelloPacket) packet;
      byte[] body = helloPacket.getBody();
      if (body != null) {
      String str = new String(body, HelloPacket.CHARSET);
      System.out.println("收到消息:" + str); HelloPacket resppacket = new HelloPacket();
      resppacket.setBody(("收到了你的消息,你的消息是:" + str).getBytes(HelloPacket.CHARSET));
      Aio.send(channelContext, resppacket);
      }
      return;
      }
      }
      package org.tio.examples.helloworld.server;
      
      import java.io.IOException;
      
      import org.tio.examples.helloworld.common.Const;
      import org.tio.server.AioServer;
      import org.tio.server.ServerGroupContext;
      import org.tio.server.intf.ServerAioHandler;
      import org.tio.server.intf.ServerAioListener; /**
      *
      * @author tanyaowu
      * 2017年4月4日 下午12:22:58
      */
      public class HelloServerStarter {
      //handler, 包括编码、解码、消息处理
      public static ServerAioHandler aioHandler = new HelloServerAioHandler(); //事件监听器,可以为null,但建议自己实现该接口,可以参考showcase了解些接口
      public static ServerAioListener aioListener = null; //一组连接共用的上下文对象
      public static ServerGroupContext serverGroupContext = new ServerGroupContext(aioHandler, aioListener); //aioServer对象
      public static AioServer aioServer = new AioServer(serverGroupContext); //有时候需要绑定ip,不需要则null
      public static String serverIp = null; //监听的端口
      public static int serverPort = Const.PORT; /**
      * 启动程序入口
      */
      public static void main(String[] args) throws IOException {
      serverGroupContext.setHeartbeatTimeout(org.tio.examples.helloworld.common.Const.TIMEOUT); aioServer.start(serverIp, serverPort);
      }
      }
    4. 客户端代码

      实现org.tio.client.intf.ClientAioHandler

      package org.tio.examples.helloworld.client;
      
      import java.nio.ByteBuffer;
      
      import org.tio.client.intf.ClientAioHandler;
      import org.tio.core.ChannelContext;
      import org.tio.core.GroupContext;
      import org.tio.core.exception.AioDecodeException;
      import org.tio.core.intf.Packet;
      import org.tio.examples.helloworld.common.HelloPacket; /**
      *
      * @author tanyaowu
      */
      public class HelloClientAioHandler implements ClientAioHandler {
      private static HelloPacket heartbeatPacket = new HelloPacket(); /**
      * 解码:把接收到的ByteBuffer,解码成应用可以识别的业务消息包
      * 总的消息结构:消息头 + 消息体
      * 消息头结构: 4个字节,存储消息体的长度
      * 消息体结构: 对象的json串的byte[]
      */
      @Override
      public HelloPacket decode(ByteBuffer buffer, ChannelContext channelContext) throws AioDecodeException {
      int readableLength = buffer.limit() - buffer.position();
      //收到的数据组不了业务包,则返回null以告诉框架数据不够
      if (readableLength < HelloPacket.HEADER_LENGHT) {
      return null;
      } //读取消息体的长度
      int bodyLength = buffer.getInt(); //数据不正确,则抛出AioDecodeException异常
      if (bodyLength < 0) {
      throw new AioDecodeException("bodyLength [" + bodyLength + "] is not right, remote:" + channelContext.getClientNode());
      } //计算本次需要的数据长度
      int neededLength = HelloPacket.HEADER_LENGHT + bodyLength;
      //收到的数据是否足够组包
      int isDataEnough = readableLength - neededLength;
      // 不够消息体长度(剩下的buffe组不了消息体)
      if (isDataEnough < 0) {
      return null;
      } else //组包成功
      {
      HelloPacket imPacket = new HelloPacket();
      if (bodyLength > 0) {
      byte[] dst = new byte[bodyLength];
      buffer.get(dst);
      imPacket.setBody(dst);
      }
      return imPacket;
      }
      } /**
      * 编码:把业务消息包编码为可以发送的ByteBuffer
      * 总的消息结构:消息头 + 消息体
      * 消息头结构: 4个字节,存储消息体的长度
      * 消息体结构: 对象的json串的byte[]
      */
      @Override
      public ByteBuffer encode(Packet packet, GroupContext groupContext, ChannelContext channelContext) {
      HelloPacket helloPacket = (HelloPacket) packet;
      byte[] body = helloPacket.getBody();
      int bodyLen = 0;
      if (body != null) {
      bodyLen = body.length;
      } //bytebuffer的总长度是 = 消息头的长度 + 消息体的长度
      int allLen = HelloPacket.HEADER_LENGHT + bodyLen;
      //创建一个新的bytebuffer
      ByteBuffer buffer = ByteBuffer.allocate(allLen);
      //设置字节序
      buffer.order(groupContext.getByteOrder()); //写入消息头----消息头的内容就是消息体的长度
      buffer.putInt(bodyLen); //写入消息体
      if (body != null) {
      buffer.put(body);
      }
      return buffer;
      } /**
      * 处理消息
      */
      @Override
      public void handler(Packet packet, ChannelContext channelContext) throws Exception {
      HelloPacket helloPacket = (HelloPacket) packet;
      byte[] body = helloPacket.getBody();
      if (body != null) {
      String str = new String(body, HelloPacket.CHARSET);
      System.out.println("收到消息:" + str);
      } return;
      } /**
      * 此方法如果返回null,框架层面则不会发心跳;如果返回非null,框架层面会定时发本方法返回的消息包
      */
      @Override
      public HelloPacket heartbeatPacket() {
      return heartbeatPacket;
      }
      }
      package org.tio.examples.helloworld.client;
      
      import org.tio.client.AioClient;
      import org.tio.client.ClientChannelContext;
      import org.tio.client.ClientGroupContext;
      import org.tio.client.ReconnConf;
      import org.tio.client.intf.ClientAioHandler;
      import org.tio.client.intf.ClientAioListener;
      import org.tio.core.Aio;
      import org.tio.core.Node;
      import org.tio.examples.helloworld.common.Const;
      import org.tio.examples.helloworld.common.HelloPacket; /**
      *
      * @author tanyaowu
      *
      */
      public class HelloClientStarter {
      //服务器节点
      public static Node serverNode = new Node(Const.SERVER, Const.PORT); //handler, 包括编码、解码、消息处理
      public static ClientAioHandler aioClientHandler = new HelloClientAioHandler(); //事件监听器,可以为null,但建议自己实现该接口,可以参考showcase了解些接口
      public static ClientAioListener aioListener = null; //断链后自动连接的,不想自动连接请设为null
      private static ReconnConf reconnConf = new ReconnConf(5000L); //一组连接共用的上下文对象
      public static ClientGroupContext clientGroupContext = new ClientGroupContext(aioClientHandler, aioListener, reconnConf); public static AioClient aioClient = null;
      public static ClientChannelContext clientChannelContext = null; /**
      * 启动程序入口
      */
      public static void main(String[] args) throws Exception {
      clientGroupContext.setHeartbeatTimeout(Const.TIMEOUT);
      aioClient = new AioClient(clientGroupContext);
      clientChannelContext = aioClient.connect(serverNode);
      //连上后,发条消息玩玩
      send();
      } private static void send() throws Exception {
      HelloPacket packet = new HelloPacket();
      packet.setBody("hello world".getBytes(HelloPacket.CHARSET));
      Aio.send(clientChannelContext, packet);
      }
      }
    5. 运行程序

      运行服务器:org.tio.examples.helloworld.server.HelloServerStarter.main(String[])
      运行客户端:org.tio.examples.helloworld.client.HelloClientStarter.main(String[])

    6. 下一步

      https://gitee.com/tywo45/t-io下载源代码及例子,里面的showcase例子是专门为学习t-io而写的,其设计也是准生产级别的,可以直接拿来做您项目的手脚架。下载完成后,请按下面步骤导入到eclipse中

AIO系列文档(2)----TIO使用的更多相关文章

  1. AIO系列文档(1)----图解ByteBuffer

    因何而写 网上关于bytebuffer的文章真的很多,为何在此还要写一篇呢?主要是基于以下几点考虑 很多人在使用t-io时,还不会bytebuffer,只会照着t-io提供的例子照猫画虎,不利于灵活运 ...

  2. 老猿学5G扫盲贴:3GPP规范文档命名规则及同系列文档阅读指南

    专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 老猿学5G博文目录 在学习5G规范过程中,有些内容把握不定的时候,有时 ...

  3. Sharepoint学习笔记—ECM系列—文档列表的Metedata Navigation与Key Filter功能的实现

    如果一个文档列表中存放了成百上千的文档,想要快速的找到你想要的还真不是件容易的事,Sharepoint提供了Metedata Navigation与Key Filter功能可以帮助我们快速的过滤和定位 ...

  4. Sharepoint学习笔记—ECM系列--文档集(Document Set)的实现

    文档集是 SharePoint Server 2010 中的一项新功能,它使组织能够管理单个可交付文档或工作产品(可包含多个文档或文件).文档集是特殊类型的文件夹,它合并了唯一的文档集属性以及文件夹和 ...

  5. Thinking in Java系列 文档+代码+简评

    声明:本人无意侵犯原作者的版权,这里可下载的文档都属于作者自行开放下载的,统一放置在这里是因为不可预测的原因使得原文档和代码不方便下载,故将我所收集的内容统一在这里,如果这里的内容侵犯了别人,请告知我 ...

  6. 微软官方的.net系列文档

    闲下来的时候给自己补充补充基础,微软官方的相关技术文档地址,最新最全最官方:https://docs.microsoft.com/zh-cn/ 其中.NET专区:https://docs.micros ...

  7. 一起买Beta版本系列文档

    一起买beta版本文档报告汇总 031402401鲍亮 031402402曹鑫杰 031402403常松 031402412林淋 031402418汪培侨 031402426许秋鑫 一.Beta版本冲 ...

  8. JEECMS8——系列文档

    jeecms8 系列文章地址 https://blog.csdn.net/weixin_37490221/article/details/78652035

  9. 框架优化系列文档:SVN中非版本控制文件忽略上传的设置

    对于SVN代码库,只应该上传源代码.资源文件等内容进行版本管理,通常编译后的二进制文件.程序包等生成产物是不应该放到SVN上做版本管理的.因此在svn的客户端工具中设置svn的属性:svn:ignor ...

随机推荐

  1. 启动Eclipse发生错误:An internal error occurred during: "Initializing Java Tooling".

    问题描述   由于上一次关闭 Eclipse 时没有正常关闭,再次启动 Eclipse 时报错:An internal error occurred during: "Initializin ...

  2. Typescript---02 变量声明

    声明变量: let和const是JavaScript里相对较新的变量声明方式.let在很多方面与var是相似的,但是可以避免在JavaScript里常见一些问题. const是对let的一个增强,它能 ...

  3. spring-cloud-hystrix-dasboard服务调用监控

    除了隔离依赖服务的调用以外,hystrix还提供了准实时的调用监控(hystrix dashboard),hystrxi会持续的记录所有通过hyxtrix发起的请求的执行信息,并以统计报表和图形的形式 ...

  4. OpenStack--ntp组件时间同步服务

    作用:ntp主要是用于对计算机的时间同步管理操作 环境: 服务端: 192.168.245.172 客户端: 192.168.245.171 时间是对服务器来说是很重要的,一般很多网站都需要读取服务器 ...

  5. 注册中心(Eureka)

    1. pom.xml依赖 <dependencies> <dependency> <groupId>org.springframework.cloud</gr ...

  6. SpringBoot 整合 Redis缓存

    在我们的日常项目开发过程中缓存是无处不在的,因为它可以极大的提高系统的访问速度,关于缓存的框架也种类繁多,今天主要介绍的是使用现在非常流行的NoSQL数据库(Redis)来实现我们的缓存需求. Spr ...

  7. 为什么阿里巴巴禁止在 foreach 循环里进行元素的 remove/add 操作--java.util.ConcurrentModificationException

    摘要 foreach循环(Foreach loop)是计算机编程语言中的一种控制流程语句,通常用来循环遍历数组或集合中的元素. 在阿里巴巴Java开发手册中,有这样一条规定: 但是手册中并没有给出具体 ...

  8. JS & JQuery 动态处理select option

    原文 出处http://www.51xuediannao.com/html+css/htmlcssjq/cssbuhuanhang.html 今天你问了我一个关于在<select>里动态添 ...

  9. C#应用编程小例子-03-展示另一个窗体

    C#应用编程小例子-03-展示另一个窗体 using System; using System.Collections.Generic; using System.ComponentModel; us ...

  10. 自己动手造拖拉机轮子系列 -(react-loadable)

    最新消息:react官方已支持懒加载https://reactjs.org/docs/code-splitting.html#reactlazy 文章webpack分片chunk加载原理中深入探究了异 ...