新文章 网摘 文章 随笔 日记

自己写的TCP自定义二进制协议(二)

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;
        }
    }
}

 

posted @ 2020-10-27 16:57  岭南春  阅读(184)  评论(0)    收藏  举报