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 ...
随机推荐
- zigbee 信道
以zigbee nxp5169 信道是:11-26 15d0f1-170923 15 ----- 信道 d0f1--- 是zigbee 物理地址 170923---时间2017年9月 ...
- python3.0j基语法-01
python基础语法,字符编码,python解释器在加载 .py 文件中的代码时,会对内容进行编码(默认ascill) print("Hello word") 一个简单hello ...
- Linux-进程描述(1)—进程控制块
进程概念介绍 进程是操作系统对运行程序的一种抽象. • 一个正在执行的程序: • 一个正在计算机上执行的程序实例: • 能分配给处理器并由处理器执行的实体: • 一个具有普以下特征的活动单元:一组指令 ...
- nginx 10054报错问题解决方案
使用nginx代理,端口8000.tomcat用于后端服务器,端口8080.nginx的error.log中报如下错误: 2018/09/21 09:08:06 [error] 12488#11600 ...
- DDD - 概述 - 聚合 - 限界上下文 (四)
最重要的一句话 DDD的所有有相关理论中,只有一句是至关重要的,但是也是最容易被忽略和最难做到的,抛弃传统的设计方式(思路)的思想,这句话起了决定性的作用,但是99%的人都忽略了或者在开始无法正视或理 ...
- react-native项目中禁止截屏与录屏
在android/app/src/main/java/com/projname/MainActivity.java文件中的onCreate方法添加一下代码即可 import android.view. ...
- P1522 牛的旅行 Cow Tours floyed
题目描述 农民 John的农场里有很多牧区.有的路径连接一些特定的牧区.一片所有连通的牧区称为一个牧场.但是就目前而言,你能看到至少有两个牧区通过任何路径都不连通.这样,Farmer John就有多个 ...
- P1119 灾后重建 floyd
题目背景 BB地区在地震过后,所有村庄都造成了一定的损毁,而这场地震却没对公路造成什么影响.但是在村庄重建好之前,所有与未重建完成的村庄的公路均无法通车.换句话说,只有连接着两个重建完成的村庄的公路才 ...
- 使用spring中4.2.6版本使用@Value取值失败,结果为${xxx}的情况
环境:spring 4.2.6 使用全注解来获取值的时候发生@Value为写好的表达式 简单的代码: 实体类: public class RichMan { @Value(value=" ...
- Sublime2 Package Control不可用修复
因为教程里用的是sublime2 所以就跟着用了,也没换3,但是最近安装Nodejs做配置时,发现插件安装器不能用了,已点安装就弹窗报错: there are no packages availabl ...