3

As the most popular open source network programming framework software in China, t-io is known for its simplicity and ease of use. Compared with netty, t-io is much simpler to implement, and the amount of code is greatly reduced. If you want to use t well -io, we still need to learn some basic knowledge of t-io first. This article mainly introduces the basic knowledge of t-io from 8 aspects. For details, please refer to:
https://www.wanetech.com/doc/tio/88

t-io message sending and receiving process

The t-io message sending and receiving and processing process can be clearly expressed with a picture
image.png

application layer package: Packet

Packet is used to express the business data structure. We implement our own business data structure by inheriting the Packet. For everyone, just think of the Packet as an ordinary VO object.

Note: It is not recommended to use the Packet object directly, but to inherit the Packet

A simple Packet may look like this

package org.tio.study.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_LENGTH = 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;
    }
}

Can be combined with AioHandler.java to understand Packet

package org.tio.core.intf;
import java.nio.ByteBuffer;
import org.tio.core.ChannelContext;
import org.tio.core.TioConfig;
import org.tio.core.exception.AioDecodeException;
/**
 * 
 * @author tanyaowu 
 * 2017年10月19日 上午9:40:15
 */
public interface AioHandler {
    /**
     * 根据ByteBuffer解码成业务需要的Packet对象.
     * 如果收到的数据不全,导致解码失败,请返回null,在下次消息来时框架层会自动续上前面的收到的数据
     * @param buffer 参与本次希望解码的ByteBuffer
     * @param limit ByteBuffer的limit
     * @param position ByteBuffer的position,不一定是0哦
     * @param readableLength ByteBuffer参与本次解码的有效数据(= limit - position)
     * @param channelContext
     * @return
     * @throws AioDecodeException
     */
    Packet decode(ByteBuffer buffer, int limit, int position, int readableLength, ChannelContext channelContext) throws AioDecodeException;
    /**
     * 编码
     * @param packet
     * @param tioConfig
     * @param channelContext
     * @return
     * @author: tanyaowu
     */
    ByteBuffer encode(Packet packet, TioConfig tioConfig, ChannelContext channelContext);
    /**
     * 处理消息包
     * @param packet
     * @param channelContext
     * @throws Exception
     * @author: tanyaowu
     */
    void handler(Packet packet, ChannelContext channelContext) throws Exception;
}

single TCP connection context: ChannelContext

The establishment of each tcp connection will generate a ChannelContext object, which is an abstract class. If you are using t-io as a tcp client, then it is ClientChannelContext. If you are using tio as a tcp server, then it is ServerChannelContext.
image.png
The user can associate the business data with the TCP connection through the ChannelContext object, and set the properties as follows

ChannelContext.set(String key, Object value)

Then use the following method to get the attributes

ChannelContext.get(String key)

Of course, the most commonly used one is to use the bind function provided by t-io, which is too strong to be an opponent. For example, use the following code to bind userid

Tio.bindUser(ChannelContext channelContext, String userid)

Then you can operate by userid, the sample code is as follows

//获取某用户的ChannelContext集合
SetWithLock<ChannelContext> set = Tio.getChannelContextsByUserid(tioConfig, userid);
//给某用户发消息
Tio.sendToUser(TioConfig, userid, Packet)

In addition to binding userid, t-io also has the following binding APIs built-in

Binding business id

Tio.bindBsId(ChannelContext channelContext, String bsId)

Binding token

Tio.bindToken(ChannelContext channelContext, String token)

Bound group

Tio.bindGroup(ChannelContext channelContext, String group)

The ChannelContext object contains a lot of information, the main objects are shown in the figure below
image.png
description
ChannelContext is a very important class in t-io, it is the communication bridge between business and connection!

service configuration and maintenance: TioConfig

Scenario: When we write TCP Server, we will first select a port to monitor client connections, and then create N sets of thread pools to perform related tasks, such as sending messages, decoding data packets, processing data packets and other tasks, but also maintain The various data of the client connection, in order to interact with the business, it is necessary to bind these client connections to various business data, for example, bind a client to a group, bind to a userid, and bind Wait for a token.
TioConfig solves the above scenarios: configuring thread pools, listening ports, maintaining various data on the client side, and so on.

TioConfig is an abstract class

If you are using tio as a tcp client, then you need to create a ClientTioConfig object
The server side corresponds to a ClientTioConfig object
If you are using tio as a tcp server, then you need to create ServerTioConfig
A listening port corresponds to a ServerTioConfig, a jvm can monitor multiple ports, so a jvm can have multiple ServerTioConfig objects
The TioConfig object contains a lot of information, the main objects are shown in the figure below
image.png
How to get the TioConfig object
See: https://www.wanetech.com/doc/tio/253?pageNumber=1

encoding, decoding, processing: AioHandler

AioHandler is the core interface for processing messages. It has two sub-interfaces, ClientAioHandler and ServerAioHandler. When using tio as a tcp client, you need to implement ClientAioHandler. When using tio as a tcp server, you need to implement ServerAioHandler. It mainly defines 3 methods, see Down

package org.tio.core.intf;
import java.nio.ByteBuffer;
import org.tio.core.ChannelContext;
import org.tio.core.TioConfig;
import org.tio.core.exception.AioDecodeException;
/**
 * 
 * @author tanyaowu 
 * 2017年10月19日 上午9:40:15
 */
public interface AioHandler {
    /**
     * 根据ByteBuffer解码成业务需要的Packet对象.
     * 如果收到的数据不全,导致解码失败,请返回null,在下次消息来时框架层会自动续上前面的收到的数据
     * @param buffer 参与本次希望解码的ByteBuffer
     * @param limit ByteBuffer的limit
     * @param position ByteBuffer的position,不一定是0哦
     * @param readableLength ByteBuffer参与本次解码的有效数据(= limit - position)
     * @param channelContext
     * @return
     * @throws AioDecodeException
     */
    Packet decode(ByteBuffer buffer, int limit, int position, int readableLength, ChannelContext channelContext) throws AioDecodeException;
    /**
     * 编码
     * @param packet
     * @param tioConfig
     * @param channelContext
     * @return
     * @author: tanyaowu
     */
    ByteBuffer encode(Packet packet, TioConfig tioConfig, ChannelContext channelContext);
    /**
     * 处理消息包
     * @param packet
     * @param channelContext
     * @throws Exception
     * @author: tanyaowu
     */
    void handler(Packet packet, ChannelContext channelContext) throws Exception;
}

message exchange monitoring: AioListener

AioListener is the core interface for processing messages, it has two sub-interfaces: ClientAioListener and ServerAioListener

When using tio as a tcp client, you need to implement ClientAioListener
When using tio as a tcp server, you need to implement ServerAioListener
It mainly defines the following methods

package org.tio.core.intf;
import org.tio.core.ChannelContext;
/**
 *
 * @author tanyaowu
 * 2017年4月1日 上午9:34:08
 */
public interface AioListener {
    /**
     * 建链后触发本方法,注:建链不一定成功,需要关注参数isConnected
     * @param channelContext
     * @param isConnected 是否连接成功,true:表示连接成功,false:表示连接失败
     * @param isReconnect 是否是重连, true: 表示这是重新连接,false: 表示这是第一次连接
     * @throws Exception
     * @author: tanyaowu
     */
    public void onAfterConnected(ChannelContext channelContext, boolean isConnected, boolean isReconnect) throws Exception;
    /**
     * 原方法名:onAfterDecoded
     * 解码成功后触发本方法
     * @param channelContext
     * @param packet
     * @param packetSize
     * @throws Exception
     * @author: tanyaowu
     */
    public void onAfterDecoded(ChannelContext channelContext, Packet packet, int packetSize) throws Exception;
    /**
     * 接收到TCP层传过来的数据后
     * @param channelContext
     * @param receivedBytes 本次接收了多少字节
     * @throws Exception
     */
    public void onAfterReceivedBytes(ChannelContext channelContext, int receivedBytes) throws Exception;
    /**
     * 消息包发送之后触发本方法
     * @param channelContext
     * @param packet
     * @param isSentSuccess true:发送成功,false:发送失败
     * @throws Exception
     * @author tanyaowu
     */
    public void onAfterSent(ChannelContext channelContext, Packet packet, boolean isSentSuccess) throws Exception;
    /**
     * 处理一个消息包后
     * @param channelContext
     * @param packet
     * @param cost 本次处理消息耗时,单位:毫秒
     * @throws Exception
     */
    public void onAfterHandled(ChannelContext channelContext, Packet packet, long cost) throws Exception;
    /**
     * 连接关闭前触发本方法
     * @param channelContext the channelcontext
     * @param throwable the throwable 有可能为空
     * @param remark the remark 有可能为空
     * @param isRemove
     * @author tanyaowu
     * @throws Exception 
     */
    public void onBeforeClose(ChannelContext channelContext, Throwable throwable, String remark, boolean isRemove) throws Exception;
    /**
     * 连接关闭前后触发本方法
     * 警告:走到这个里面时,很多绑定的业务都已经解绑了,所以这个方法一般是空着不实现的
     * @param channelContext the channelcontext
     * @param throwable the throwable 有可能为空
     * @param remark the remark 有可能为空
     * @param isRemove 是否是删除
     * @throws Exception
     * @author: tanyaowu
     */
//    public void onAfterClose(ChannelContext channelContext, Throwable throwable, String remark, boolean isRemove) throws Exception;
}

server side entrance: TioServer

You can understand this object a little bit. It will be used when the server starts. Simply post its source code. You only need to pay attention to it. There is a start() method to start the network service.

package org.tio.server;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.net.InetSocketAddress;
import java.net.StandardSocketOptions;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.core.Node;
import org.tio.utils.SysConst;
import org.tio.utils.date.DateUtils;
import org.tio.utils.hutool.StrUtil;
/**
 * @author tanyaowu
 *
 */
public class TioServer {
    private static Logger log = LoggerFactory.getLogger(TioServer.class);
    private ServerTioConfig serverTioConfig;
    private AsynchronousServerSocketChannel serverSocketChannel;
    private AsynchronousChannelGroup channelGroup = null;
    private Node serverNode;
    private boolean isWaitingStop = false;
    /**
     *
     * @param serverTioConfig
     *
     * @author tanyaowu
     * 2017年1月2日 下午5:53:06
     *
     */
    public TioServer(ServerTioConfig serverTioConfig) {
        super();
        this.serverTioConfig = serverTioConfig;
    }
    /**
     * @return the serverTioConfig
     */
    public ServerTioConfig getServerTioConfig() {
        return serverTioConfig;
    }
    /**
     * @return the serverNode
     */
    public Node getServerNode() {
        return serverNode;
    }
    /**
     * @return the serverSocketChannel
     */
    public AsynchronousServerSocketChannel getServerSocketChannel() {
        return serverSocketChannel;
    }
    /**
     * @return the isWaitingStop
     */
    public boolean isWaitingStop() {
        return isWaitingStop;
    }
    /**
     * @param serverTioConfig the serverTioConfig to set
     */
    public void setServerTioConfig(ServerTioConfig serverTioConfig) {
        this.serverTioConfig = serverTioConfig;
    }
    /**
     * @param isWaitingStop the isWaitingStop to set
     */
    public void setWaitingStop(boolean isWaitingStop) {
        this.isWaitingStop = isWaitingStop;
    }
    public void start(String serverIp, int serverPort) throws IOException {
        long start = System.currentTimeMillis();
        this.serverNode = new Node(serverIp, serverPort);
        channelGroup = AsynchronousChannelGroup.withThreadPool(serverTioConfig.groupExecutor);
        serverSocketChannel = AsynchronousServerSocketChannel.open(channelGroup);
        serverSocketChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
        serverSocketChannel.setOption(StandardSocketOptions.SO_RCVBUF, 64 * 1024);
        InetSocketAddress listenAddress = null;
        if (StrUtil.isBlank(serverIp)) {
            listenAddress = new InetSocketAddress(serverPort);
        } else {
            listenAddress = new InetSocketAddress(serverIp, serverPort);
        }
        serverSocketChannel.bind(listenAddress, 0);
        AcceptCompletionHandler acceptCompletionHandler = serverTioConfig.getAcceptCompletionHandler();
        serverSocketChannel.accept(this, acceptCompletionHandler);
        serverTioConfig.startTime = System.currentTimeMillis();
        //下面这段代码有点无聊,写得随意,纯粹是为了打印好看些
        String baseStr = "|----------------------------------------------------------------------------------------|";
        int baseLen = baseStr.length();
        StackTraceElement[] ses = Thread.currentThread().getStackTrace();
        StackTraceElement se = ses[ses.length - 1];
        int xxLen = 18;
        int aaLen = baseLen - 3;
        List<String> infoList = new ArrayList<>();
        infoList.add(StrUtil.fillAfter("Tio gitee address", ' ', xxLen) + "| " + SysConst.TIO_URL_GITEE);
        infoList.add(StrUtil.fillAfter("Tio site address", ' ', xxLen) + "| " + SysConst.TIO_URL_SITE);
        infoList.add(StrUtil.fillAfter("Tio version", ' ', xxLen) + "| " + SysConst.TIO_CORE_VERSION);
        infoList.add(StrUtil.fillAfter("-", '-', aaLen));
        infoList.add(StrUtil.fillAfter("TioConfig name", ' ', xxLen) + "| " + serverTioConfig.getName());
        infoList.add(StrUtil.fillAfter("Started at", ' ', xxLen) + "| " + DateUtils.formatDateTime(new Date()));
        infoList.add(StrUtil.fillAfter("Listen on", ' ', xxLen) + "| " + this.serverNode);
        infoList.add(StrUtil.fillAfter("Main Class", ' ', xxLen) + "| " + se.getClassName());
        try {
            RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
            String runtimeName = runtimeMxBean.getName();
            String pid = runtimeName.split("@")[0];
            long startTime = runtimeMxBean.getStartTime();
            long startCost = System.currentTimeMillis() - startTime;
            infoList.add(StrUtil.fillAfter("Jvm start time", ' ', xxLen) + "| " + startCost + " ms");
            infoList.add(StrUtil.fillAfter("Tio start time", ' ', xxLen) + "| " + (System.currentTimeMillis() - start) + " ms");
            infoList.add(StrUtil.fillAfter("Pid", ' ', xxLen) + "| " + pid);
        } catch (Exception e) {
        }
        //100
        String printStr = "\r\n"+baseStr+"\r\n";
        //        printStr += "|--" + leftStr + " " + info + " " + rightStr + "--|\r\n";
        for (String string : infoList) {
            printStr += "| " + StrUtil.fillAfter(string, ' ', aaLen) + "|\r\n";
        }
        printStr += baseStr + "\r\n";
        if (log.isInfoEnabled()) {
            log.info(printStr);
        } else {
            System.out.println(printStr);
        }
    }
    /**
     * 
     * @return
     * @author tanyaowu
     */
    public boolean stop() {
        isWaitingStop = true;
        boolean ret = true;
        try {
            channelGroup.shutdownNow();
        } catch (Exception e) {
            log.error("channelGroup.shutdownNow()时报错", e);
        }
        try {
            serverSocketChannel.close();
        } catch (Exception e1) {
            log.error("serverSocketChannel.close()时报错", e1);
        }
        try {
            serverTioConfig.groupExecutor.shutdown();
        } catch (Exception e1) {
            log.error(e1.toString(), e1);
        }
        try {
            serverTioConfig.tioExecutor.shutdown();
        } catch (Exception e1) {
            log.error(e1.toString(), e1);
        }
        serverTioConfig.setStopped(true);
        try {
            ret = ret && serverTioConfig.groupExecutor.awaitTermination(6000, TimeUnit.SECONDS);
            ret = ret && serverTioConfig.tioExecutor.awaitTermination(6000, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            log.error(e.getLocalizedMessage(), e);
        }
        log.info(this.serverNode + " stopped");
        return ret;
    }
}

client entrance: TioClient

TioClient is only used when you are using t-io as a TCP client. Here is a brief post of its source code. For its usage, see the showcase demonstration project below.

package org.tio.client;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.StandardSocketOptions;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.AsynchronousSocketChannel;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.client.intf.ClientAioHandler;
import org.tio.core.ChannelContext;
import org.tio.core.Node;
import org.tio.core.Tio;
import org.tio.core.intf.Packet;
import org.tio.core.ssl.SslFacadeContext;
import org.tio.core.stat.ChannelStat;
import org.tio.utils.SystemTimer;
import org.tio.utils.hutool.StrUtil;
import org.tio.utils.lock.SetWithLock;
/**
 *
 * @author tanyaowu
 * 2017年4月1日 上午9:29:58
 */
public class TioClient {
    /**
     * 自动重连任务
     * @author tanyaowu
     *
     */
    private static class ReconnRunnable implements Runnable {
        ClientChannelContext channelContext = null;
        TioClient tioClient = null;
        //        private static Map<Node, Long> cacheMap = new HashMap<>();
        public ReconnRunnable(ClientChannelContext channelContext, TioClient tioClient) {
            this.channelContext = channelContext;
            this.tioClient = tioClient;
        }
        /**
         * @see java.lang.Runnable#run()
         *
         * @author tanyaowu
         * 2017年2月2日 下午8:24:40
         *
         */
        @Override
        public void run() {
            ReentrantReadWriteLock closeLock = channelContext.closeLock;
            WriteLock writeLock = closeLock.writeLock();
            writeLock.lock();
            try {
                if (!channelContext.isClosed) //已经连上了,不需要再重连了
                {
                    return;
                }
                long start = SystemTimer.currTime;
                tioClient.reconnect(channelContext, 2);
                long end = SystemTimer.currTime;
                long iv = end - start;
                if (iv >= 100) {
                    log.error("{},重连耗时:{} ms", channelContext, iv);
                } else {
                    log.info("{},重连耗时:{} ms", channelContext, iv);
                }
                if (channelContext.isClosed) {
                    channelContext.setReconnCount(channelContext.getReconnCount() + 1);
                    //                    cacheMap.put(channelContext.getServerNode(), SystemTimer.currTime);
                    return;
                }
            } catch (java.lang.Throwable e) {
                log.error(e.toString(), e);
            } finally {
                writeLock.unlock();
            }
        }
    }
    private static Logger log = LoggerFactory.getLogger(TioClient.class);
    private AsynchronousChannelGroup channelGroup;
    private ClientTioConfig clientTioConfig;
    /**
     * @param serverIp 可以为空
     * @param serverPort
     * @param aioDecoder
     * @param aioEncoder
     * @param aioHandler
     *
     * @author tanyaowu
     * @throws IOException
     *
     */
    public TioClient(final ClientTioConfig clientTioConfig) throws IOException {
        super();
        this.clientTioConfig = clientTioConfig;
        this.channelGroup = AsynchronousChannelGroup.withThreadPool(clientTioConfig.groupExecutor);
        startHeartbeatTask();
        startReconnTask();
    }
    /**
     *
     * @param serverNode
     * @throws Exception
     *
     * @author tanyaowu
     *
     */
    public void asynConnect(Node serverNode) throws Exception {
        asynConnect(serverNode, null);
    }
    /**
     *
     * @param serverNode
     * @param timeout
     * @throws Exception
     *
     * @author tanyaowu
     *
     */
    public void asynConnect(Node serverNode, Integer timeout) throws Exception {
        asynConnect(serverNode, null, null, timeout);
    }
    /**
     *
     * @param serverNode
     * @param bindIp
     * @param bindPort
     * @param timeout
     * @throws Exception
     *
     * @author tanyaowu
     *
     */
    public void asynConnect(Node serverNode, String bindIp, Integer bindPort, Integer timeout) throws Exception {
        connect(serverNode, bindIp, bindPort, null, timeout, false);
    }
    /**
     *
     * @param serverNode
     * @return
     * @throws Exception
     *
     * @author tanyaowu
     *
     */
    public ClientChannelContext connect(Node serverNode) throws Exception {
        return connect(serverNode, null);
    }
    /**
     *
     * @param serverNode
     * @param timeout
     * @return
     * @throws Exception
     * @author tanyaowu
     */
    public ClientChannelContext connect(Node serverNode, Integer timeout) throws Exception {
        return connect(serverNode, null, 0, timeout);
    }
    /**
     *
     * @param serverNode
     * @param bindIp
     * @param bindPort
     * @param initClientChannelContext
     * @param timeout 超时时间,单位秒
     * @return
     * @throws Exception
     * @author tanyaowu
     */
    public ClientChannelContext connect(Node serverNode, String bindIp, Integer bindPort, ClientChannelContext initClientChannelContext, Integer timeout) throws Exception {
        return connect(serverNode, bindIp, bindPort, initClientChannelContext, timeout, true);
    }
    /**
     *
     * @param serverNode
     * @param bindIp
     * @param bindPort
     * @param initClientChannelContext
     * @param timeout 超时时间,单位秒
     * @param isSyn true: 同步, false: 异步
     * @return
     * @throws Exception
     * @author tanyaowu
     */
    private ClientChannelContext connect(Node serverNode, String bindIp, Integer bindPort, ClientChannelContext initClientChannelContext, Integer timeout, boolean isSyn)
            throws Exception {
        AsynchronousSocketChannel asynchronousSocketChannel = null;
        ClientChannelContext channelContext = null;
        boolean isReconnect = initClientChannelContext != null;
        //        ClientAioListener clientAioListener = clientTioConfig.getClientAioListener();
        long start = SystemTimer.currTime;
        asynchronousSocketChannel = AsynchronousSocketChannel.open(channelGroup);
        long end = SystemTimer.currTime;
        long iv = end - start;
        if (iv >= 100) {
            log.error("{}, open 耗时:{} ms", channelContext, iv);
        }
        asynchronousSocketChannel.setOption(StandardSocketOptions.TCP_NODELAY, true);
        asynchronousSocketChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
        asynchronousSocketChannel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
        InetSocketAddress bind = null;
        if (bindPort != null && bindPort > 0) {
            if (false == StrUtil.isBlank(bindIp)) {
                bind = new InetSocketAddress(bindIp, bindPort);
            } else {
                bind = new InetSocketAddress(bindPort);
            }
        }
        if (bind != null) {
            asynchronousSocketChannel.bind(bind);
        }
        channelContext = initClientChannelContext;
        start = SystemTimer.currTime;
        InetSocketAddress inetSocketAddress = new InetSocketAddress(serverNode.getIp(), serverNode.getPort());
        ConnectionCompletionVo attachment = new ConnectionCompletionVo(channelContext, this, isReconnect, asynchronousSocketChannel, serverNode, bindIp, bindPort);
        if (isSyn) {
            Integer realTimeout = timeout;
            if (realTimeout == null) {
                realTimeout = 5;
            }
            CountDownLatch countDownLatch = new CountDownLatch(1);
            attachment.setCountDownLatch(countDownLatch);
            asynchronousSocketChannel.connect(inetSocketAddress, attachment, clientTioConfig.getConnectionCompletionHandler());
            boolean f = countDownLatch.await(realTimeout, TimeUnit.SECONDS);
            if (f) {
                return attachment.getChannelContext();
            } else {
                log.error("countDownLatch.await(realTimeout, TimeUnit.SECONDS) 返回false ");
                return attachment.getChannelContext();
            }
        } else {
            asynchronousSocketChannel.connect(inetSocketAddress, attachment, clientTioConfig.getConnectionCompletionHandler());
            return null;
        }
    }
    /**
     *
     * @param serverNode
     * @param bindIp
     * @param bindPort
     * @param timeout 超时时间,单位秒
     * @return
     * @throws Exception
     *
     * @author tanyaowu
     *
     */
    public ClientChannelContext connect(Node serverNode, String bindIp, Integer bindPort, Integer timeout) throws Exception {
        return connect(serverNode, bindIp, bindPort, null, timeout);
    }
    /**
     * @return the channelGroup
     */
    public AsynchronousChannelGroup getChannelGroup() {
        return channelGroup;
    }
    /**
     * @return the clientTioConfig
     */
    public ClientTioConfig getClientTioConfig() {
        return clientTioConfig;
    }
    /**
     *
     * @param channelContext
     * @param timeout
     * @return
     * @throws Exception
     *
     * @author tanyaowu
     *
     */
    public void reconnect(ClientChannelContext channelContext, Integer timeout) throws Exception {
        connect(channelContext.getServerNode(), channelContext.getBindIp(), channelContext.getBindPort(), channelContext, timeout);
    }
    /**
     * @param clientTioConfig the clientTioConfig to set
     */
    public void setClientTioConfig(ClientTioConfig clientTioConfig) {
        this.clientTioConfig = clientTioConfig;
    }
    /**
     * 定时任务:发心跳
     * @author tanyaowu
     *
     */
    private void startHeartbeatTask() {
        final ClientGroupStat clientGroupStat = (ClientGroupStat)clientTioConfig.groupStat;
        final ClientAioHandler aioHandler = clientTioConfig.getClientAioHandler();
        final String id = clientTioConfig.getId();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (!clientTioConfig.isStopped()) {
//                    final long heartbeatTimeout = clientTioConfig.heartbeatTimeout;
                    if (clientTioConfig.heartbeatTimeout <= 0) {
                        log.warn("用户取消了框架层面的心跳定时发送功能,请用户自己去完成心跳机制");
                        break;
                    }
                    SetWithLock<ChannelContext> setWithLock = clientTioConfig.connecteds;
                    ReadLock readLock = setWithLock.readLock();
                    readLock.lock();
                    try {
                        Set<ChannelContext> set = setWithLock.getObj();
                        long currtime = SystemTimer.currTime;
                        for (ChannelContext entry : set) {
                            ClientChannelContext channelContext = (ClientChannelContext) entry;
                            if (channelContext.isClosed || channelContext.isRemoved) {
                                continue;
                            }
                            ChannelStat stat = channelContext.stat;
                            long compareTime = Math.max(stat.latestTimeOfReceivedByte, stat.latestTimeOfSentPacket);
                            long interval = currtime - compareTime;
                            if (interval >= clientTioConfig.heartbeatTimeout / 2) {
                                Packet packet = aioHandler.heartbeatPacket(channelContext);
                                if (packet != null) {
                                    if (log.isInfoEnabled()) {
                                        log.info("{}发送心跳包", channelContext.toString());
                                    }
                                    Tio.send(channelContext, packet);
                                }
                            }
                        }
                        if (log.isInfoEnabled()) {
                            log.info("[{}]: curr:{}, closed:{}, received:({}p)({}b), handled:{}, sent:({}p)({}b)", id, set.size(), clientGroupStat.closed.get(),
                                    clientGroupStat.receivedPackets.get(), clientGroupStat.receivedBytes.get(), clientGroupStat.handledPackets.get(),
                                    clientGroupStat.sentPackets.get(), clientGroupStat.sentBytes.get());
                        }
                    } catch (Throwable e) {
                        log.error("", e);
                    } finally {
                        try {
                            readLock.unlock();
                            Thread.sleep(clientTioConfig.heartbeatTimeout / 4);
                        } catch (Throwable e) {
                            log.error(e.toString(), e);
                        } finally {
                        }
                    }
                }
            }
        }, "tio-timer-heartbeat" + id).start();
    }
    /**
     * 启动重连任务
     *
     *
     * @author tanyaowu
     *
     */
    private void startReconnTask() {
        final ReconnConf reconnConf = clientTioConfig.getReconnConf();
        if (reconnConf == null || reconnConf.getInterval() <= 0) {
            return;
        }
        final String id = clientTioConfig.getId();
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (!clientTioConfig.isStopped()) {
                    //log.info("准备重连");
                    LinkedBlockingQueue<ChannelContext> queue = reconnConf.getQueue();
                    ClientChannelContext channelContext = null;
                    try {
                        channelContext = (ClientChannelContext) queue.take();
                    } catch (InterruptedException e1) {
                        log.error(e1.toString(), e1);
                    }
                    if (channelContext == null) {
                        continue;
                        //                        return;
                    }
                    if (channelContext.isRemoved) //已经删除的,不需要重新再连
                    {
                        continue;
                    }
                    SslFacadeContext sslFacadeContext = channelContext.sslFacadeContext;
                    if (sslFacadeContext != null) {
                        sslFacadeContext.setHandshakeCompleted(false);
                    }
                    long sleeptime = reconnConf.getInterval() - (SystemTimer.currTime - channelContext.stat.timeInReconnQueue);
                    //log.info("sleeptime:{}, closetime:{}", sleeptime, timeInReconnQueue);
                    if (sleeptime > 0) {
                        try {
                            Thread.sleep(sleeptime);
                        } catch (InterruptedException e) {
                            log.error(e.toString(), e);
                        }
                    }
                    if (channelContext.isRemoved || !channelContext.isClosed) //已经删除的和已经连上的,不需要重新再连
                    {
                        continue;
                    }
                    ReconnRunnable runnable = new ReconnRunnable(channelContext, TioClient.this);
                    reconnConf.getThreadPoolExecutor().execute(runnable);
                }
            }
        });
        thread.setName("tio-timer-reconnect-" + id);
        thread.setDaemon(true);
        thread.start();
    }
    /**
     * 
     * @return
     * @author tanyaowu
     */
    public boolean stop() {
        boolean ret = true;
        try {
            clientTioConfig.groupExecutor.shutdown();
        } catch (Exception e1) {
            log.error(e1.toString(), e1);
        }
        try {
            clientTioConfig.tioExecutor.shutdown();
        } catch (Exception e1) {
            log.error(e1.toString(), e1);
        }
        clientTioConfig.setStopped(true);
        try {
            ret = ret && clientTioConfig.groupExecutor.awaitTermination(6000, TimeUnit.SECONDS);
            ret = ret && clientTioConfig.tioExecutor.awaitTermination(6000, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            log.error(e.getLocalizedMessage(), e);
        }
        log.info("client resource has released");
        return ret;
    }
}

技术控
1 声望4 粉丝

爱专研,爱学习