AIO系列文档(2)----TIO使用
AIO系列文档(1)----图解ByteBuffer中介绍了ByteBuffer用法,下面通过介绍t-io介绍如何使用:
- hello world例子简介- 本例子演示的是一个典型的TCP长连接应用,代码位于example/helloworld目录中。 - 服务端和客户端的消息协议比较简单,消息头为4个字节,用以表示消息体的长度,消息体为一个字符串的byte[]
- 服务端先启动,监听6789端口
- 客户端连接到服务端后,会主动向服务器发送一条消息
- 服务器收到消息后会回应一条消息
- 之后,框架层会自动从客户端发心跳到服务器,服务器也会检测心跳有没有超时
- 框架层会在断链后自动重连(对t-io来说,只需多一行代码便拥有自动重连功能)
 
- 公共模块代码- 在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;
 }
 
- 服务端代码- 实现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);
 }
 }
- 客户端代码- 实现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);
 }
 }
 
- 运行程序- 运行服务器:org.tio.examples.helloworld.server.HelloServerStarter.main(String[]) 
 运行客户端:org.tio.examples.helloworld.client.HelloClientStarter.main(String[])
- 下一步- 去https://gitee.com/tywo45/t-io下载源代码及例子,里面的showcase例子是专门为学习t-io而写的,其设计也是准生产级别的,可以直接拿来做您项目的手脚架。下载完成后,请按下面步骤导入到eclipse中    
AIO系列文档(2)----TIO使用的更多相关文章
- AIO系列文档(1)----图解ByteBuffer
		因何而写 网上关于bytebuffer的文章真的很多,为何在此还要写一篇呢?主要是基于以下几点考虑 很多人在使用t-io时,还不会bytebuffer,只会照着t-io提供的例子照猫画虎,不利于灵活运 ... 
- 老猿学5G扫盲贴:3GPP规范文档命名规则及同系列文档阅读指南
		专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 老猿学5G博文目录 在学习5G规范过程中,有些内容把握不定的时候,有时 ... 
- Sharepoint学习笔记—ECM系列—文档列表的Metedata Navigation与Key Filter功能的实现
		如果一个文档列表中存放了成百上千的文档,想要快速的找到你想要的还真不是件容易的事,Sharepoint提供了Metedata Navigation与Key Filter功能可以帮助我们快速的过滤和定位 ... 
- Sharepoint学习笔记—ECM系列--文档集(Document Set)的实现
		文档集是 SharePoint Server 2010 中的一项新功能,它使组织能够管理单个可交付文档或工作产品(可包含多个文档或文件).文档集是特殊类型的文件夹,它合并了唯一的文档集属性以及文件夹和 ... 
- Thinking in Java系列 文档+代码+简评
		声明:本人无意侵犯原作者的版权,这里可下载的文档都属于作者自行开放下载的,统一放置在这里是因为不可预测的原因使得原文档和代码不方便下载,故将我所收集的内容统一在这里,如果这里的内容侵犯了别人,请告知我 ... 
- 微软官方的.net系列文档
		闲下来的时候给自己补充补充基础,微软官方的相关技术文档地址,最新最全最官方:https://docs.microsoft.com/zh-cn/ 其中.NET专区:https://docs.micros ... 
- 一起买Beta版本系列文档
		一起买beta版本文档报告汇总 031402401鲍亮 031402402曹鑫杰 031402403常松 031402412林淋 031402418汪培侨 031402426许秋鑫 一.Beta版本冲 ... 
- JEECMS8——系列文档
		jeecms8 系列文章地址 https://blog.csdn.net/weixin_37490221/article/details/78652035 
- 框架优化系列文档:SVN中非版本控制文件忽略上传的设置
		对于SVN代码库,只应该上传源代码.资源文件等内容进行版本管理,通常编译后的二进制文件.程序包等生成产物是不应该放到SVN上做版本管理的.因此在svn的客户端工具中设置svn的属性:svn:ignor ... 
随机推荐
- C# 高级编程02----手动创建C#程序
			在日常工作中使用C# 开发的时候,通常使用宇宙第一神器VS进行开发.为了了解编译过程,这里采用文本编辑器的方式编写一个C#程序 一.创建一个C#程序 1.使用记事本工具创建一个名为First.cs的文 ... 
- Nginx 中利用 Lua 脚本做访问控制
			使用场景 需要在后端服务之前做访问控制,或没有后端服务的场景,如静态文件. 实验环境 Ubuntu 14.04 Nginx 1.4.6 安装 Lua 运行环境 sudo apt-get install ... 
- 写交互式脚本时,遇到到报错:not a regular file
			场景:在写两台ubuntu之间需要交互式登录操作shell脚本.就是在ubantu A上,做点什么事,然后远程到ubuntu B上进行操作,shell脚本存放在ubuntu A上. 我遇到这个报错:n ... 
- sql 语句中count()有条件的时候为什么要加上or null
			参考:https://blog.csdn.net/qq_32719287/article/details/79513164 1.sql 语句中count()有条件的时候为什么要加上or null. 如 ... 
- Anaconda安装python tensorflow 环境
			1.安装Anaconda3 https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/ 2.安装python 3.6 (base) C:\Users\ ... 
- 一起学爬虫——如何爬取通过ajax加载数据的网站
			目前很多网站都使用ajax技术动态加载数据,和常规的网站不一样,数据时动态加载的,如果我们使用常规的方法爬取网页,得到的只是一堆html代码,没有任何的数据. 请看下面的代码: url = 'http ... 
- UOJ#416. 【APIO2018】铁人两项
			原文链接www.cnblogs.com/zhouzhendong/p/UOJ416.html 前言 完了完了SB选手Tarjan写挂. 题解 考虑先Tarjan缩个点双建个圆方树. 然后发现,确定起点 ... 
- CMD运行命令每次都要进入很麻烦
			20:35:52 win+r 输入regedit 进入注册表 找到HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor项 创建AutoRun ... 
- hdu 2005 java
			题意: 输入数据格式为YYYY/MM/DD,对于每组输入数据,输出一行,表示该日期是该年的第几天. 思路: 使用Calendar.DAY_OF_YEAR import java.text.ParseE ... 
- BZOJ2143: 飞飞侠
			2143: 飞飞侠 题意: 给出两个 n ∗ m 的矩阵 A,B,以及 3 个人的坐标 在 (i, j) 支付 Ai,j 的费用可以弹射到曼哈顿距离不超过 Bi,j 的位置 问三个人汇合所需要的最小总 ... 
