using System;
using System.IO;
using System.Net.Sockets;
namespace BinaryProtocol.Core.Tcp
{
public class ConnectContext
{
public MessageServer TcpServer { get; }
public TcpClient Client { get; }
public BinaryReader Br { get; }
public BinaryWriter Bw { get; }
public string ClientId { get; set; }
public Action<string> LogAction { get; }
/// <summary>
/// 客户端是否还活着
/// </summary>
public bool IsClientActive { get; set; }
/// <summary>
/// 上次活动时间
/// </summary>
public DateTime LastActiveTime { get; set; }
public ConnectContext(TcpClient client,MessageServer tcpServer, Action<string> logAction)
{
TcpServer = tcpServer;
Client = client;
LogAction = logAction;
NetworkStream networkStream = client.GetStream();
Br = new BinaryReader(networkStream);
Bw = new BinaryWriter(networkStream);
}
public void Close()
{
Br.Close();
Bw.Close();
Client.Close();
}
}
}
using System;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using BinaryProtocol.Core.Constants;
using BinaryProtocol.Core.Events;
using BinaryProtocol.Core.Handles;
using BinaryProtocol.Core.Models;
namespace BinaryProtocol.Core.Tcp
{
public class MessageClient
{
private ConnectContext _connectContext;
private readonly Action<string> _logAction;
public CancellationToken Ct;
private CancellationTokenSource _cts = new CancellationTokenSource();
private readonly string _serverIp;
private readonly int _serverPort;
private static int _messageId;
private Timer _timer;
private int _heartBeatTimeSpanMillion = 10000;
private bool _isConnecting;
public string ClientId { get; }
public MessageClient(string clientId, string ip, int port, Action<string> logAction)
{
ClientId = clientId;
_logAction = logAction;
_serverIp = ip;
_serverPort = port;
//注册系统消息处理器映射器
MessageDispatcher.RegisterMessageHandleMapper(
new MessageHandleMapper(
(byte)Constants.ProtocolType.Common,
(byte)MessageType.SysMessage,
typeof(SysMessageHandle)));
Connect();
}
/// <summary>
/// 异步接收数据
/// </summary>
private async Task ReceiveAsync(ConnectContext connectContext)
{
await Task.Run(() =>
{
try
{
var headBuffer = new byte[MessageHead.HeadLength];
int headReadCount = 0;
int headUnReadCount = MessageHead.HeadLength;
while (!Ct.IsCancellationRequested)
{
int x = connectContext.Br.Read(headBuffer, headReadCount, headUnReadCount);
headReadCount += x;
headUnReadCount -= x;
//如果读取了一个完整的消息头
if (headUnReadCount == 0)
{
MessageHead head = new MessageHead(headBuffer);
//读消息体
var bodyBuffer = new byte[head.BodyLength];
int bodyReadCount = 0;
int bodyUnReadCount = head.BodyLength;
while (bodyUnReadCount > 0)
{
int y = connectContext.Br.Read(bodyBuffer, bodyReadCount, bodyUnReadCount);
bodyReadCount += y;
bodyUnReadCount -= y;
}
//分派消息
MessageDispatcher.Dispatch(head, bodyBuffer, connectContext);
//重置消息头的缓冲区
headBuffer = new byte[MessageHead.HeadLength];
headReadCount = 0;
headUnReadCount = MessageHead.HeadLength;
}
}
}
catch (Exception e)
{
_logAction(e.Message);
}
}, Ct);
}
/// <summary>
/// 异步发送数据
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="clientId"></param>
/// <param name="message"></param>
/// <returns></returns>
public async Task SendAsync<T>(string clientId, IMessage<T> message)
{
await Task.Run(() =>
{
if (!IsOnline())
{
_logAction("没有连接服务器。");
return;
}
try
{
_connectContext.Bw.Write(message.Bytes, 0, message.Bytes.Length);
_connectContext.Bw.Flush();
_logAction($"已发送消息:{Newtonsoft.Json.JsonConvert.SerializeObject(message.Body.Value)}");
}
catch (Exception e)
{
_logAction(e.Message);
}
}, Ct);
} /// <summary>
/// 停止接收传入请求
/// </summary>
public void Stop()
{
//如果侦听器被取消了,直接退出
if (Ct.IsCancellationRequested)
{
_logAction("连接已断开");
return;
}
//取消侦听
_cts.Cancel();
//休眠一下,以确保所有操作都接收到有关取消的信息。
Thread.Sleep(100);
_connectContext.Close();
_logAction("连接已断开");
}
public async Task Connect()
{
try
{
if (_isConnecting)
{
return;
}
_isConnecting = true;
//设置心跳
_timer?.Dispose();
_timer = new Timer(HeartBeat);
//每5秒发送一次心跳
_timer.Change(0, _heartBeatTimeSpanMillion);
_logAction("正在尝试连接到服务器……");
var client = new TcpClient(_serverIp, _serverPort);
_connectContext = new ConnectContext(client, null, _logAction);
_connectContext.IsClientActive = true;
_connectContext.LastActiveTime = DateTime.Now;
_logAction("已连接到服务器");
//开启接收数据的进程
var thread = new Thread(async () =>
{
await ReceiveAsync(_connectContext);
});
thread.Start();
//发送握手消息
Message<MessageEvent> connectMsg = SysMessageBuilder.CreateConnectMessage(ClientId);
await SendAsync(ClientId, connectMsg);
_isConnecting = false;
}
catch (Exception e)
{
_logAction(e.Message);
_isConnecting = false;
}
}
/// <summary>
/// 检测TcpClient是否在线
/// </summary>
/// <returns></returns>
public bool IsOnline()
{
try
{
if (_connectContext?.Client == null)
{
return false;
}
if (!_connectContext.IsClientActive)
{
return false;
}
var c = _connectContext.Client;
if (c.Client.Poll(0, SelectMode.SelectRead))
{
byte[] buff = new byte[1];
if (c.Client.Receive(buff, SocketFlags.Peek) == 0)
{
return false;
}
return true;
}
return true;
}
catch (Exception)
{
return false;
}
}
/// <summary>
/// 发送心跳包
/// </summary>
/// <param name="state"></param>
private async void HeartBeat(object state)
{
if (_connectContext==null)
{
_logAction("未连接服务器");
//重连
await Connect();
return;
}
var time = DateTime.Now.AddMilliseconds(-_heartBeatTimeSpanMillion * 2);
if (!_connectContext.IsClientActive
|| _connectContext.LastActiveTime < time
|| !IsOnline())
{
_connectContext.IsClientActive = false;
//重连
await Connect();
return;
}
Message<MessageEvent> msg = SysMessageBuilder.CreateHeartBeatMessage(ClientId);
await SendAsync(ClientId, msg);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using BinaryProtocol.Core.Handles;
using BinaryProtocol.Core.Models;
namespace BinaryProtocol.Core.Tcp
{
/// <summary>
///简单的非阻塞tcp服务器
/// </summary>
public class MessageServer
{
#region Fields.
public CancellationToken Ct;
private CancellationTokenSource _cts;
private readonly TcpListener _listener;
public readonly Action<string> LogAction;
public static List<ConnectContext> ClientContexts = new List<ConnectContext>();
private Timer _timer;
private int _heartBeatTimeSpanMillion = 10000;
#endregion
#region Public.
/// <summary>
/// 创建TcpServer的新实例
/// </summary>
/// <param name="ip">Ip of server.</param>
/// <param name="port">Port of server.</param>
/// <param name="logAction"></param>
public MessageServer(string ip, int port, Action<string> logAction)
{
//注册系统消息处理器映射器
MessageDispatcher.RegisterMessageHandleMapper(
new MessageHandleMapper(
(byte)Constants.ProtocolType.Common,
(byte)Constants.MessageType.SysMessage,
typeof(SysMessageHandle)));
LogAction = logAction;
_listener = new TcpListener(IPAddress.Parse(ip), port);
}
/// <summary>
/// 开始接收传入请求
/// </summary>
public void Start()
{
_cts = new CancellationTokenSource();
Ct = _cts.Token;
_listener.Start();
LogAction("正在等待客户端连接……");
//设置定期检查活动状态
_timer?.Dispose();
_timer = new Timer(CheckClientsStatus);
//10秒后启动,每10秒检查一次
_timer.Change(10000, _heartBeatTimeSpanMillion);
_listener.BeginAcceptTcpClient(ProcessRequest, _listener);
}
/// <summary>
/// 停止接收传入请求
/// </summary>
public void Stop()
{
_timer?.Dispose();
//如果侦听器被取消了,直接退出
if (Ct.IsCancellationRequested)
{
LogAction("侦听器已停止");
return;
}
//取消侦听
_cts.Cancel();
//休眠一下,以确保所有操作都接收到有关取消的信息。
Thread.Sleep(100);
_listener.Stop();
LogAction("侦听器已停止");
}
/// <summary>
/// 异步接收数据
/// </summary>
private async Task ReceiveAsync(ConnectContext connectContext)
{
await Task.Run(() =>
{
try
{
var headBuffer = new byte[MessageHead.HeadLength];
int headReadCount = 0;
int headUnReadCount = MessageHead.HeadLength;
while (!Ct.IsCancellationRequested)
{
int x = connectContext.Br.Read(headBuffer, headReadCount, headUnReadCount);
headReadCount += x;
headUnReadCount -= x;
//如果读取了一个完整的消息头
if (headUnReadCount == 0)
{
MessageHead head = new MessageHead(headBuffer);
//读消息体
var bodyBuffer = new byte[head.BodyLength];
int bodyReadCount = 0;
int bodyUnReadCount = head.BodyLength;
while (bodyUnReadCount > 0)
{
int y = connectContext.Br.Read(bodyBuffer, bodyReadCount, bodyUnReadCount);
bodyReadCount += y;
bodyUnReadCount -= y;
}
//分派消息
MessageDispatcher.Dispatch(head, bodyBuffer, connectContext);
//重置消息头的缓冲区
headBuffer = new byte[MessageHead.HeadLength];
headReadCount = 0;
headUnReadCount = MessageHead.HeadLength;
}
}
}
catch (Exception e)
{
connectContext.LogAction(e.Message);
}
}, Ct);
}
/// <summary>
/// 异步发送数据
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="clientId"></param>
/// <param name="message"></param>
/// <returns></returns>
public async Task SendAsync<T>(string clientId, IMessage<T> message)
{
await Task.Run(() =>
{
if (Ct.IsCancellationRequested)
{
LogAction("连接已断开。");
return;
}
var connectContext = ClientContexts.FirstOrDefault(r => r.ClientId == clientId);
if (!IsOnline(connectContext))
{
LogAction("Client没有连接。");
return;
}
try
{
connectContext.Bw.Write(message.Bytes, 0, message.Bytes.Length);
connectContext.Bw.Flush();
LogAction($"已发送消息:{Newtonsoft.Json.JsonConvert.SerializeObject(message.Body.Value)}");
}
catch (Exception e)
{
LogAction(e.Message);
}
}, Ct);
}
#endregion
#region Private.
//处理单个请求
private void ProcessRequest(IAsyncResult ar)
{
//如果操作被取消,则停止.
if (Ct.IsCancellationRequested)
{
LogAction("操作已被取消");
return;
}
var listener = ar.AsyncState as TcpListener;
if (listener == null)
{
LogAction("侦听器为空");
return;
}
//再次检查取消。如果操作被取消,则停止.
if (Ct.IsCancellationRequested)
{
LogAction("操作已被取消");
return;
}
//开始等待下一个请求
LogAction("正在等待下一个连接接入……");
listener.BeginAcceptTcpClient(ProcessRequest, listener);
//获取客户端并开始处理接收到的请求.
TcpClient client = listener.EndAcceptTcpClient(ar);
LogAction("客户端已连接");
var connectContext = new ConnectContext(client, this, LogAction);
ClientContexts.Add(connectContext);
var thread = new Thread(async () =>
{
await ReceiveAsync(connectContext);
});
thread.Start();
}
/// <summary>
/// 检测TcpClient是否在线
/// </summary>
/// <returns></returns>
public bool IsOnline(ConnectContext connectContext)
{
try
{
if (connectContext?.Client == null)
{
return false;
}
var c = connectContext.Client;
if (c.Client.Poll(0, SelectMode.SelectRead))
{
byte[] buff = new byte[1];
if (c.Client.Receive(buff, SocketFlags.Peek) == 0)
{
return false;
}
return true;
}
return true;
}
catch (Exception)
{
return false;
}
}
/// <summary>
/// 检查客户端状态
/// </summary>
/// <param name="state"></param>
private async void CheckClientsStatus(object state)
{
await Task.Run(() =>
{
var time = DateTime.Now.AddMilliseconds(-_heartBeatTimeSpanMillion * 2);
var deadClients = ClientContexts.Where(r => !r.IsClientActive || r.LastActiveTime < time).ToList();
foreach (var client in deadClients)
{
LogAction($"{client.ClientId}已断开连接");
ClientContexts.Remove(client);
}
});
}
#endregion
}
}
using BinaryProtocol.Core.Constants;
using BinaryProtocol.Core.Events;
using BinaryProtocol.Core.Models;
namespace BinaryProtocol.Core.Tcp
{
public class SysMessageBuilder
{
/// <summary>
/// 创建心跳包
/// </summary>
/// <param name="clientId"></param>
/// <returns></returns>
public static Message<MessageEvent> CreateHeartBeatMessage(string clientId)
{
return CreateSysMessage(clientId, Command.HeartBeat);
}
/// <summary>
/// 创建心跳包响应
/// </summary>
/// <param name="clientId"></param>
/// <returns></returns>
public static Message<MessageEvent> CreateHeartBeatResultMessage(string clientId)
{
return CreateSysMessage(clientId, Command.HeartBeatResult);
}
/// <summary>
/// 创建连接消息
/// </summary>
/// <param name="clientId"></param>
/// <returns></returns>
public static Message<MessageEvent> CreateConnectMessage(string clientId)
{
return CreateSysMessage(clientId, Command.Connect);
}
/// <summary>
/// 创建连接响应消息
/// </summary>
/// <param name="clientId"></param>
/// <returns></returns>
public static Message<MessageEvent> CreateConnectResultMessage(string clientId)
{
return CreateSysMessage(clientId, Command.ConnectResult);
}
private static Message<MessageEvent> CreateSysMessage(string clientId, Command cmd)
{
MessageBody<MessageEvent> connectEventBody = new MessageBody<MessageEvent>(new MessageEvent()
{
ClientId = clientId,
Command = (byte)cmd
});
MessageHead head = new MessageHead(0,
(byte)ProtocolType.Common,
(byte)MessageType.SysMessage,
1, connectEventBody.Bytes.Length);
Message<MessageEvent> msg = new Message<MessageEvent>(head, connectEventBody);
return msg;
}
}
}