C#TCP通讯封装服务器工具类

C#TCP通讯封装服务器工具类

1使用说明

  • 添加接受数据回调函数事件

方式1:通过有参构造函数添加
方式2:调用:public EventHandler<byte[]> AddEventToDataReceived

  • 添加输出日志回调函数事件

方式1:通过有参构造函数添加
方式2:调用:public Action<EMessage, IPEndPoint, int, string> AddEventToOutLog

  • 监听客户端发送数据线程向其他客户端转发消息的数据如何输出日志信息

方式:设置属性【OutputReceivedLog 】、设置属性【OutputReplyLog】
类型:

Null :不输出
Length:仅输出接受数据的长度信息
UTF8:UTF8-解析输出
ASCII:ASCII-解析输出
ByteToString:输出字节的字符串形式

2封装

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Tcp_Server
{
    public class ServerModel
    {
        private bool _firstStartServer = true;
        private TcpListener _server;
        private List<TcpClient> _tcpClientsList = new List<TcpClient>();

        /// <summary>
        /// 接受消息回调函数
        /// </summary>
        private EventHandler<byte[]> _callbackEventDataReceived;

        /// <summary>
        /// 日志信息回调函数
        /// int:线程ID
        /// </summary>
        private Action<EMessage, IPEndPoint, int, string> _callbackEventOutLog;

        public ServerModel(EventHandler<byte[]> callbackEventDataReceived, Action<EMessage, IPEndPoint, int, string> callbackEventOutLog)
        {
            AddEventToDataReceived = callbackEventDataReceived;
            AddEventToOutLog = callbackEventOutLog;
        }

        public ServerModel()
        {
        }

        ~ServerModel()
        {
            CloseServer();
        }

        /// <summary>
        /// 监听接入客户端
        /// </summary>
        private void ListeningClient()
        {
            TcpClient client = null;

            try
            {
                //等待客户端连接
                client = _server.AcceptTcpClient();

                AddLog(client, EMessage.State, "已连接");

                //为(每个)客户端创建线程处理通信
                new Task(() => HandleClient(client)).Start();
            }
            catch (Exception ex)
            {
                AddLog(client, EMessage.Exception, $"ListeningClient: {ex.Message}");
            }
        }

        private void HandleClient(TcpClient client)
        {
            try
            {
                NetworkStream stream = client.GetStream();
                _tcpClientsList.Add(client);
                byte[] buffer = new byte[1024];

                while (IsOpen)
                {
                    // 异步读取客户端发送的消息
                    int bytesRead = stream.Read(buffer, 0, buffer.Length);
                    if (bytesRead == 0)
                    {
                        // 客户端断开连接
                        break;
                    }

                    var receive = new byte[bytesRead];
                    Array.Copy(buffer, receive, bytesRead);
                    _callbackEventDataReceived?.Invoke(null, receive);
                    PacketParseLog(EMessage.Receive, OutputReceivedLog, client, receive);

                    //转发消息给其他客户端
                    BroadcastMessage(client, buffer);
                }
            }
            catch (Exception ex)
            {
                AddLog(client, EMessage.Exception, $"HandleClient: {ex.Message}");
            }
        }

        private void BroadcastMessage(TcpClient sender, byte[] buffer)
        {
            int length = buffer.Length;
            foreach (TcpClient client in _tcpClientsList)
            {
                if (client != sender && client.Connected)
                {
                    try
                    {
                        NetworkStream stream = client.GetStream();
                        stream.Write(buffer, 0, length);
                        PacketParseLog(EMessage.Reply, OutputReplyLog, client, buffer);
                    }
                    catch (Exception ex)
                    {
                        AddLog(client, EMessage.Exception, $"BroadcastMessage: {ex.Message}");
                    }
                }
            }
        }

        /// <summary>
        /// 解析数据包输出至日志
        /// </summary>
        /// <param name="messageType"></param>
        /// <param name="type"></param>
        /// <param name="client"></param>
        /// <param name="bytes"></param>
        private void PacketParseLog(EMessage messageType, ETranscoding type, TcpClient client, byte[] bytes)
        {
            string message;
            switch (type)
            {
                case ETranscoding.Length:
                    message = bytes.Length.ToString();
                    break;
                case ETranscoding.UTF8:
                    message = Encoding.UTF8.GetString(bytes);
                    break;
                case ETranscoding.ASCII:
                    message = Encoding.ASCII.GetString(bytes);
                    break;
                case ETranscoding.ByteToString:
                    var builder = new StringBuilder();
                    builder.Append("[ ");
                    foreach (var b in bytes)
                    {
                        builder.Append(Convert.ToString(b, 16).PadLeft(2, '0').ToUpper() + " ");
                    }
                    builder.Append("]");
                    message = builder.ToString();
                    break;
                default:
                    return;
            }
            AddLog(client, messageType, message);
        }

        private void AddLog(TcpClient client, EMessage type, string description)
        {
            int threadId = Thread.CurrentThread.ManagedThreadId;
            IPEndPoint ipEndPoint;
            if (client != null)
            {
                ipEndPoint = (IPEndPoint)client.Client.RemoteEndPoint;
            }
            else
            {
                ipEndPoint = (IPEndPoint)_server.LocalEndpoint;
            }

            _callbackEventOutLog?.Invoke(type, ipEndPoint, threadId, description);

        }

        /********隔离线********隔离线********隔离线********隔离线********隔离线********隔离线********隔离线********/

        public EventHandler<byte[]> AddEventToDataReceived
        {
            set
            {
                if (value != null) _callbackEventDataReceived += value;
            }
        }

        public Action<EMessage, IPEndPoint, int, string> AddEventToOutLog
        {
            set
            {
                if (value != null) _callbackEventOutLog += value;
            }
        }

        /// <summary>
        /// 接受到客户端数据时输出日志信息的方式,默认:不输出
        /// </summary>
        public ETranscoding OutputReceivedLog { get; set; } = ETranscoding.Null;

        /// <summary>
        /// 转发消息Log时数据解析方式
        /// </summary>
        public ETranscoding OutputReplyLog { get; set; } = ETranscoding.Null;

        public bool StartServer(string ip, int port)
        {
            try
            {
                CloseServer();
                //创建一个 TCP 监听器,监听本地 IP 地址和端口
                _server = new TcpListener(IPAddress.Parse(ip), port);
                // 启动服务器
                _server.Start();
                AddLog(null, EMessage.State, "服务器已启动");

                IsOpen = true;
                if (_firstStartServer)
                {
                    _firstStartServer = false;
                    new Task(() =>
                    {
                        while (IsOpen)
                        {
                            Thread.Sleep(1000);
                            ListeningClient();
                        }
                    }).Start();
                }
            }
            catch (Exception ex)
            {
                AddLog(null, EMessage.Exception, $"StartServer: {ex.Message}");
            }

            return IsOpen;
        }

        public void CloseServer()
        {
            if (IsOpen)
            {
                // 停止服务器
                _server.Stop();
            }

            IsOpen = false;
            _server = null;
        }

        public bool IsOpen { get; private set; }

        public enum EMessage
        {
            [Description("状态信息")]
            State = 1,

            [Description("发送")]
            Send = 3,

            [Description("接受")]
            Receive = 5,

            [Description("异常")]
            Exception = 7,

            [Description("回复")]
            Reply = 9,
        }

        /// <summary>
        /// 接受到客户端数据时字节码解析方式,解析后输出至日志
        /// </summary>
        public enum ETranscoding
        {
            /// <summary>
            /// 不输出
            /// </summary>
            Null = 101,

            /// <summary>
            /// 仅输出接受数据的长度信息
            /// </summary>
            Length,

            /// <summary>
            /// UTF8-解析输出
            /// </summary>
            UTF8,

            /// <summary>
            /// ASCII-解析输出
            /// </summary>
            ASCII,

            /// <summary>
            /// 输出字节的字符串形式
            /// </summary>
            ByteToString
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值