因爲DotNetty是從java的Netty框架仿寫過來的,介紹的文檔特別少,加之官方也沒有提供api文檔,所以之前一直不理解心跳的用法。最近忙裏偷閒,稍稍研究了一番,終於有點明白了。
現在將代碼複製上來,留作日後查看(ps:精髓都在代碼裏):
Uptime.Client:
public class Program
{
const string HOST = "127.0.0.1";
const int PORT = 8045;
// Sleep 5 seconds before a reconnection attempt.
public const int RECONNECT_DELAY = 5;
// Reconnect when the server sends nothing for 10 seconds.
private const int READ_TIMEOUT = 10;
private static UptimeClientHandler handler = new UptimeClientHandler();
private static Bootstrap bs = new Bootstrap();
static void Main(string[] args)
{
EventLoopGroup group = new EventLoopGroup();
bs.Group(group)
.Channel<TcpSocketChannel>()
.RemoteAddress(HOST, PORT)
.Handler(new ActionChannelInitializer<ISocketChannel>(channel =>
{
//IdleStateHandler心跳檢測處理器,添加自定義處理Handler類實現userEventTriggered()方法作爲超時事件的邏輯處理
//IdleStateHandler心跳檢測每十秒進行一次讀檢測,如果十秒內ChannelRead()方法未被調用則觸發一次userEventTrigger()方法.
IChannelPipeline pipeline = channel.Pipeline;
pipeline.AddLast(new IdleStateHandler(READ_TIMEOUT, 0, 0), handler);//第一個參數爲讀,第二個爲寫,第三個爲讀寫全部
}));
}
}
Uptime.Client.UptimeClientHandler:
public class UptimeClientHandler : SimpleChannelInboundHandler<object>
{
long startTime = -1;
//ChannelActive:活躍狀態,可接收和發送數據
public override void ChannelActive(IChannelHandlerContext ctx)
{
if (startTime < 0)
{
startTime = GetTimeStamp();
}
Console.WriteLine("Connected to: " + ctx.Channel.RemoteAddress);
}
protected override void ChannelRead0(IChannelHandlerContext context, object message)
{
var byteBuffer = message as IByteBuffer;
if (byteBuffer != null)
{
Console.WriteLine("Received from server: " + byteBuffer.ToString(Encoding.UTF8));
}
context.WriteAsync(message);
}
public override void UserEventTriggered(IChannelHandlerContext ctx, object evt)
{
if (!(evt is IdleStateEvent))
{
return;
}
IdleStateEvent e = evt as IdleStateEvent;
if (e.State == IdleState.ReaderIdle)
{
// The connection was OK but there was no traffic for last period.
Console.WriteLine("Disconnecting due to no inbound traffic");
ctx.CloseAsync();
}
}
//channelInactive: 處於非活躍狀態,沒有連接到遠程主機。
public override void ChannelInactive(IChannelHandlerContext context)
{
Console.WriteLine("Disconnected from: " + context.Channel.RemoteAddress);
}
//channelUnregistered: 已創建但未註冊到一個 EventLoop。
public override void ChannelUnregistered(IChannelHandlerContext context)
{
}
public override void ExceptionCaught(IChannelHandlerContext context, Exception exception)
{
Console.WriteLine("Exception: " + exception);
context.CloseAsync();
}
/// <summary>
/// 獲取時間戳
/// </summary>
/// <returns></returns>
private long GetTimeStamp()
{
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
return Convert.ToInt64(ts.TotalMilliseconds);
}
}
Uptime.Server:
class Program
{
private static readonly int PORT = 8045;
private static readonly UptimeServerHandler handler = new UptimeServerHandler();
static void Main(string[] args) => RunServerAsync().Wait();
static async Task RunServerAsync()
{
IEventLoopGroup bossGroup = new MultithreadEventLoopGroup(1);
IEventLoopGroup workerGroup = new MultithreadEventLoopGroup();
try
{
ServerBootstrap b = new ServerBootstrap();
b.Group(bossGroup, workerGroup)
.Channel<TcpServerSocketChannel>()
.Handler(new LoggingHandler("SRV-LSTN"))
.ChildHandler(new ActionChannelInitializer<IChannel>(channel =>
{
//工作線程連接器 是設置了一個管道,服務端主線程所有接收到的信息都會通過這個管道一層層往下傳輸
//同時所有出棧的消息也要這個管道的所有處理器進行一步步處理
IChannelPipeline pipeline = channel.Pipeline;
pipeline.AddLast("Uptime", handler);
}));
// Bind and start to accept incoming connections.
IChannel boundChannel = await b.BindAsync(PORT);
// Wait until the server socket is closed.
// In this example, this does not happen, but you can do that to gracefully
// shut down your server.
await boundChannel.CloseAsync();
}
finally
{
await Task.WhenAll(
bossGroup.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1)),
workerGroup.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1)));
}
}
}
Uptime.Server.UptimeServerHandler:
public class UptimeServerHandler : SimpleChannelInboundHandler<object> {
protected override void ChannelRead0(IChannelHandlerContext ctx, object msg)
{
// discard
}
//捕獲 異常,並輸出到控制檯後斷開鏈接,提示:客戶端意外斷開鏈接,也會觸發
public override void ExceptionCaught(IChannelHandlerContext context, Exception exception)
{
Console.WriteLine("Exception: " + exception);
context.CloseAsync();
}
}
最後,附上參考文檔鏈接:DotNetty系列三:編碼解碼器,IdleStateHandler心跳機制,羣發