1、t-io是神马?
t-io是一个网络框架,从这一点来说是有点像 netty 的,但 t-io 为常见和网络相关的业务(如 IM、消息推送、RPC、监控)提供了近乎于现成的解决方案,即丰富的编程 API,极大减少业务层的编程难度。
2、怎么用?分为服务端和客户端,先看服务端!
① maven引入依赖
② TioServer
不知道要用什么,就先造一个TioServer,它就会告诉接下来该干什么。
TioServer tioServer = new TioServer(serverGroupContext);
需要一个ServerGroupContext。还是老样子,造一个ServerGroupContext,就知道接下来的步骤。
ServerGroupContext serverGroupContext = new ServerGroupContext("tio-server", new ServerAioHandler(), new ServerAioListener());
需要一个字符串String,表示的是服务端的名称。之后是ServerAioHandler和ServerAioListener,这两个是接口,需要自己实现。
先是ServerAioHandler的实现类,看其中的方法就知道是用来编码和解码,还有就是处理业务逻辑(handler方法)
package com.xxjsll.server.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.core.ChannelContext;
import org.tio.core.GroupContext;
import org.tio.core.Node;
import org.tio.core.Tio;
import org.tio.core.exception.AioDecodeException;
import org.tio.core.intf.Packet;
import org.tio.server.intf.ServerAioHandler;
import java.nio.ByteBuffer;
/**
* @author tsinghua
* @date 2018/6/20
*/
public class MServerAioHandler implements ServerAioHandler {
private static final Logger logger = LoggerFactory.getLogger(MServerAioHandler.class);
public Packet decode(ByteBuffer byteBuffer, int limit, int position, int readableLength, ChannelContext channelContext) throws AioDecodeException {
logger.debug("inside decode...");
if (readableLength < ServerPacket.PACKET_HEADER_LENGTH) {
return null;
}
int bodyLength = byteBuffer.getInt();
if (bodyLength < 0) {
throw new AioDecodeException("body length[" + bodyLength + "] is invalid. romote: " + channelContext.getServerNode());
}
int len = bodyLength + ServerPacket.PACKET_HEADER_LENGTH;
if (len > readableLength) {
return null;
} else {
byte[] bytes = new byte[len];
int i = 0;
while(true){
if(byteBuffer.remaining() == 0){
break;
}
byte b = byteBuffer.get();
bytes[i++] = b;
}
ServerPacket serverPacket = new ServerPacket();
serverPacket.setBody(bytes);
return serverPacket;
}
}
public ByteBuffer encode(Packet packet, GroupContext groupContext, ChannelContext channelContext) {
logger.info("inside encode...");
ServerPacket serverPacket = (ServerPacket) packet;
byte[] body = serverPacket.getBody();
int bodyLength = 0;
if(body != null){
bodyLength = body.length;
}
ByteBuffer byteBuffer = ByteBuffer.allocate(bodyLength + ServerPacket.PACKET_HEADER_LENGTH);
byteBuffer.order(groupContext.getByteOrder());
byteBuffer.putInt(bodyLength);
if (body != null) {
byteBuffer.put(body);
}
return byteBuffer;
}
public void handler(Packet packet, ChannelContext channelContext) throws Exception {
logger.debug("inside handler...");
channelContext.setServerNode(new Node("127.0.0.1", ServerPacket.PORT));
ServerPacket serverPacket = (ServerPacket) packet;
byte[] body = serverPacket.getBody();
if(body != null){
String bodyStr = new String(body, "utf-8");
ServerPacket serverPacket1 = new ServerPacket();
serverPacket1.setBody(("receive from ["+ channelContext.getClientNode() + "]: " + bodyStr).getBytes("utf-8"));
Tio.send(channelContext, serverPacket1);
}
}
}
接着就是ServerAioListener的实现类,是个监听类。可以很容易知道其中每一个方法的作用。
package com.xxjsll.server.util;
import org.tio.core.ChannelContext;
import org.tio.core.intf.Packet;
import org.tio.server.intf.ServerAioListener;
/**
* @author tsinghua
* @date 2018/6/20
*/
public class MServerAioListener implements ServerAioListener {
public void onAfterConnected(ChannelContext channelContext, boolean b, boolean b1) throws Exception {
}
public void onAfterDecoded(ChannelContext channelContext, Packet packet, int i) throws Exception {
}
public void onAfterReceivedBytes(ChannelContext channelContext, int i) throws Exception {
}
public void onAfterSent(ChannelContext channelContext, Packet packet, boolean b) throws Exception {
}
public void onAfterHandled(ChannelContext channelContext, Packet packet, long l) throws Exception {
}
public void onBeforeClose(ChannelContext channelContext, Throwable throwable, String s, boolean b) throws Exception {
}
}
在ServerAioHandler的实现类和ServerAioListener的实现类中,有出现一个Packet类,这就是携带数据的类,需要通过继承来打造自己的类。
package com.xxjsll.server.util;
import org.tio.core.intf.Packet;
/**
* @author tsinghua
* @date 2018/6/20
*/
public class ServerPacket extends Packet {
public static final Integer PACKET_HEADER_LENGTH = 4;
public static final Integer PORT = 8999;
byte[] body;//数据
public byte[] getBody() {
return body;
}
public void setBody(byte[] body) {
this.body = body;
}
}
至此,服务端基本完成了,现在就剩下启动。
package com.xxjsll.server; import com.xxjsll.server.util.MServerAioHandler; import com.xxjsll.server.util.MServerAioListener; import org.tio.server.ServerGroupContext; import org.tio.server.TioServer; import java.io.IOException; /** * @author tsinghua * @date 2018/6/20 */ public class TIOServer { public static void main(String[] args) throws IOException { ServerGroupContext serverGroupContext = new ServerGroupContext("tio-server", new MServerAioHandler(), new MServerAioListener()); TioServer server = new TioServer(serverGroupContext); TioServer tioServer = new TioServer(serverGroupContext); server.start("127.0.0.1", 8999); } }
服务端完成。
③ 接着就是客户端,其实和服务端差不多。
ClientAioHandler和ClientAioListener两个接口,还有一个Packet,和服务端差不多。
- ClientAioHandler的实现类。
package com.xxjsll.util; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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 java.nio.ByteBuffer; /** * @author tsinghua * @date 2018/6/20 */ public class MClientAioHander implements ClientAioHandler { Logger logger = LoggerFactory.getLogger(MClientAioHander.class); @Override public Packet heartbeatPacket() { return new ClientPacket(); } @Override public Packet decode(ByteBuffer byteBuffer, int limit, int position, int readableLength, ChannelContext channelContext) throws AioDecodeException { if(readableLength < ClientPacket.PACKET_HEADER_LENGTH){ return null; } int bodyLength = byteBuffer.getInt(); if(bodyLength < 0){ throw new AioDecodeException("body length is invalid.romote: " + channelContext.getServerNode()); } int usefulLength = ClientPacket.PACKET_HEADER_LENGTH + bodyLength; if(usefulLength > readableLength){ return null; }else { ClientPacket packet = new ClientPacket(); byte[] body = new byte[bodyLength]; byteBuffer.get(body); packet.setBody(body); return packet; } } @Override public ByteBuffer encode(Packet packet, GroupContext groupContext, ChannelContext channelContext) { ClientPacket clientPacket = (ClientPacket) packet; byte[] body = clientPacket.getBody(); int bodyLength = 0; if(body != null){ bodyLength = body.length; } int len = ClientPacket.PACKET_HEADER_LENGTH + bodyLength; ByteBuffer byteBuffer = ByteBuffer.allocate(len); byteBuffer.order(groupContext.getByteOrder()); byteBuffer.putInt(bodyLength); if(body != null){ byteBuffer.put(body); } return byteBuffer; } @Override public void handler(Packet packet, ChannelContext channelContext) throws Exception { ClientPacket clientPacket = (ClientPacket) packet; byte[] body = clientPacket.getBody(); if(body != null){ String bodyStr = new String(body, "utf-8"); logger.debug("客户端收到消息: " + bodyStr); } } }
- ClientAioListener的实现类
package com.xxjsll.util; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.tio.client.intf.ClientAioListener; import org.tio.core.ChannelContext; import org.tio.core.Tio; import org.tio.core.intf.Packet; /** * @author tsinghua * @date 2018/6/20 */ public class MClientAioListener implements ClientAioListener { Logger logger = LoggerFactory.getLogger(MClientAioListener.class); private static Integer count = 0; @Override public void onAfterConnected(ChannelContext channelContext, boolean b, boolean b1) throws Exception { logger.info("onAfterConnected!"); } @Override public void onAfterDecoded(ChannelContext channelContext, Packet packet, int i) throws Exception { logger.info("onAfterDecoded..."); } @Override public void onAfterReceivedBytes(ChannelContext channelContext, int i) throws Exception { logger.info("onAfterReceivedBytes-------------------" + i); } @Override public void onAfterSent(ChannelContext channelContext, Packet packet, boolean b) throws Exception { logger.info("onAfterSent..."); } @Override public void onAfterHandled(ChannelContext channelContext, Packet packet, long l) throws Exception { System.out.println("onAfterHandled..."); ClientPacket clientPacket = (ClientPacket) packet; String resData = new String(clientPacket.getBody(), "utf-8"); logger.info("[" + channelContext.getServerNode() + "]: " + resData); count++; ((ClientPacket) packet).setBody(("[" + channelContext.getServerNode() + "]: " + count).getBytes()); Thread.sleep(5000); Tio.send(channelContext, packet); } @Override public void onBeforeClose(ChannelContext channelContext, Throwable throwable, String s, boolean b) throws Exception { logger.error(throwable.getMessage()); logger.info(s); } }
- Packet
package com.xxjsll.util; import org.tio.core.intf.Packet; /** * @author tsinghua * @date 2018/6/20 */ public class ClientPacket extends Packet { public static final Integer PACKET_HEADER_LENGTH = 4; private byte[] body; public byte[] getBody() { return body; } public void setBody(byte[] body) { this.body = body; } }
接着就是向服务端发送连接请求,至于处理服务端的回复则是在ClientAioListener的实现类中。
package com.xxjsll; import com.xxjsll.util.ClientPacket; import com.xxjsll.util.MClientAioHander; import com.xxjsll.util.MClientAioListener; import org.tio.client.ClientChannelContext; import org.tio.client.ClientGroupContext; import org.tio.client.TioClient; import org.tio.core.Node; import org.tio.core.Tio; /** * Hello world! * */ public class App { public static void main( String[] args ) throws Exception { ClientGroupContext clientGroupContext = new ClientGroupContext(new MClientAioHander(), new MClientAioListener()); TioClient tioClient = new TioClient(clientGroupContext); ClientChannelContext clientChannelContext = tioClient.connect(new Node("127.0.0.1", 8999)); ClientPacket clientPacket = new ClientPacket(); clientPacket.setBody("hello,t-tio".getBytes("utf-8")); Tio.send(clientChannelContext, clientPacket); } }
④ 总结一下:这只是初步运用,算个helloworld。