網路程式設計框架t-io的程式設計基本知識介紹
t-io作為目前國內最流行的開源網路程式設計框架軟體,以簡單易懂,上手容易而著稱,相同的功能比起netty實現起來,要簡單的多,程式碼量也大大減少,如果要使用好t-io,還是要先學習t-io的一些基本知識,這篇文章主要從8個方面介紹了t-io的基礎知識。 具體請參考:
t-io收發訊息過程
t-io收發訊息及處理過程,可以用一張圖清晰地表達出來
應用層包:Packet
Packet是用於表述業務資料結構的,我們透過 繼承 Packet來實現自己的業務資料結構,對於各位而言,把Packet看作是一個普通的VO物件即可。
注意:不建議直接使用Packet物件,而是要 繼承Packet
一個簡單的Packet可能長這樣
-
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 ;
-
}
-
}
可以結合AioHandler.java理解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 ;
-
-
}
單條TCP連線上下文:ChannelContext
每一個tcp連線的建立都會產生一個ChannelContext物件,這是個抽象類,如果你是用t-io作tcp客戶端,那麼就是ClientChannelContext,如果你是用tio作tcp伺服器,那麼就是ServerChannelContext
使用者可以把業務資料透過ChannelContext物件和TCP連線關聯起來,像下面這樣設定屬性
ChannelContext.set(String key, Object value)
然後用下面的方式獲取屬性
ChannelContext.get(String key)
當然最最常用的還是用t-io提供的強到沒對手的bind功能,譬如用下面的程式碼繫結userid
Tio.bindUser(ChannelContext channelContext, String userid)
然後可以透過userid進行操作,示範程式碼如下
//獲取某使用者的ChannelContext集合SetWithLock<ChannelContext> set = Tio.getChannelContextsByUserid(tioConfig, userid);//給某使用者發訊息Tio.sendToUser(TioConfig, userid, Packet)
除了可以繫結userid,t-io還內建瞭如下繫結API
-
繫結業務id
Tio.bindBsId(ChannelContext channelContext, String bsId)
-
繫結token
Tio.bindToken(ChannelContext channelContext, String token)
- 繫結群組
Tio.bindGroup(ChannelContext channelContext, String group)
ChannelContext物件包含的資訊非常多,主要物件見下圖
說明
ChannelContext是t-io中非常重要的類,他是業務和連線的溝通橋樑!
服務配置與維護:TioConfig
- 場景:我們在寫TCP Server時,都會先選好一個埠以監聽客戶端連線,再建立N組執行緒池來執行相關的任務,譬如傳送訊息、解碼資料包、處理資料包等任務,還要維護客戶端連線的各種資料,為了和業務互動,還要把這些客戶端連線和各種業務資料繫結起來,譬如把某個客戶端繫結到一個群組,繫結到一個userid,繫結到一個token等。
-
TioConfig就是解決以上場景的:配置執行緒池、監聽埠,維護客戶端各種資料等的。
-
TioConfig是個抽象類
- 如果你是用tio作tcp客戶端,那麼你需要建立ClientTioConfig物件
- 伺服器端對應一個ClientTioConfig物件
- 如果你是用tio作tcp伺服器,那麼你需要建立ServerTioConfig
- 一個監聽埠對應一個ServerTioConfig ,一個jvm可以監聽多個埠,所以一個jvm可以有多個ServerTioConfig物件
- 如果你是用tio作tcp客戶端,那麼你需要建立ClientTioConfig物件
-
TioConfig物件包含的資訊非常多,主要物件見下圖
如何獲取TioConfig物件
見:
編碼、解碼、處理:AioHandler
AioHandler是處理訊息的核心介面,它有兩個子介面,ClientAioHandler和ServerAioHandler,當用tio作tcp客戶端時需要實現ClientAioHandler,當用tio作tcp伺服器時需要實現ServerAioHandler,它主要定義了3個方法,見下
-
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 ;
-
-
}
訊息來往監聽:AioListener
AioListener是處理訊息的核心介面,它有兩個子介面:ClientAioListener和ServerAioListener
- 當用tio作tcp客戶端時需要實現ClientAioListener
- 當用tio作tcp伺服器時需要實現ServerAioListener
它主要定義瞭如下方法
-
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;
-
}
伺服器端入口:TioServer
這個物件大家稍微瞭解一下即可,伺服器啟動時會用到這個物件,簡單貼一下它的原始碼吧,大家只需要關注它有一個start()方法是用來啟動網路服務的即可
-
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 ;
-
}
-
}
客戶端入口:TioClient
只有當你在用t-io作為TCP客戶端時,才用得到TioClient,此處簡單貼一下它的原始碼,它的用法,見後面的showcase示範工程
-
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 ;
-
}
-
}
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70006579/viewspace-2846027/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- t-io網路程式設計基礎知識介紹程式設計
- 程式設計的基本知識點(浙大)程式設計
- 工控組態程式設計相關知識點介紹程式設計
- java物件導向程式設計的基本知識Java物件程式設計
- 再聊t-io網路程式設計架構的基礎知識:半包和粘包程式設計架構
- t-io 3.7.5 釋出,口碑炸裂的國產網路程式設計框架程式設計框架
- 函式程式設計基本原理介紹函式程式設計
- NIO程式設計介紹程式設計
- 如何向新手程式設計師介紹程式設計?程式設計師
- 網路程式設計基礎知識程式設計
- 邏輯程式設計與函式程式設計的介紹程式設計函式
- 國產開源網路程式設計框架t-io的炸裂效能介紹之30W長連線併發程式設計框架
- 面向方面程式設計的介紹----基本概念(1) (轉)程式設計
- 程式設計師程式設計知識經驗總結程式設計師
- 歐姆龍CX-one程式設計基本知識程式設計
- 程式設計師的知識管理程式設計師
- Shell程式設計 --- Shell介紹程式設計
- Delphi COM程式設計介紹程式設計
- 每個程式設計師應該知道的計算機網路知識程式設計師計算機網路
- 程式設計基礎知識程式設計
- 程式設計必備知識程式設計
- Web設計師需要程式設計知識嗎?Web程式設計
- 系統程式設計-網路-內容概要、整體介紹程式設計
- AOP 面向方面程式設計的介紹----基本概念(2) (轉)程式設計
- AOP 面向方面程式設計的介紹----基本概念(3) (轉)程式設計
- 程式設計師Web面試之前端框架等知識程式設計師Web面試前端框架
- 作為程式設計師應具備的基本知識 (轉)程式設計師
- 介紹Java Socket程式設計的文章Java程式設計
- 程式設計的知識點 (不斷積累,終身程式設計)程式設計
- 013 Rust 網路程式設計,SMTP 介紹Rust程式設計
- 005 Rust 網路程式設計,ipnet 介紹Rust程式設計
- 017 Rust 網路程式設計,TFTP 介紹Rust程式設計FTP
- Linux網路程式設計--初等網路函式介紹(TCP)(轉)Linux程式設計函式TCP
- MATLAB 超程式設計介紹Matlab程式設計
- Erlang/Elixir 中的 OTP 程式設計介紹程式設計
- 006 Rust 網路程式設計,mio 庫介紹Rust程式設計
- 015 Rust 網路程式設計,FTP 介紹Rust程式設計FTP
- Linux系統程式設計之程式介紹Linux程式設計