当前位置: 首页 > article >正文

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

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

  • 1使用说明
  • 2封装

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

http://www.kler.cn/a/613680.html

相关文章:

  • Dify 0.15.3版本 本地部署指南
  • 【Spiffo】光速项目:LVGL v9框架下的MIPI简易相机_Part1
  • Unity中的MaterialPropertyBlock的作用和 Material 的区别
  • 【蓝桥杯】每日练习 Day14 递归
  • 项目复盘:websocket不受跨域限制的原理
  • 原型模式为什么可以解决构建复杂对象的资源消耗问题
  • 如何将Spring Boot项目与DeepSeek AI集成并优化为AI系统
  • 为什么要将项目部署到外部tomcat
  • Tracking Area Code(跟踪区码)
  • 【NLP 46、大模型技术发展】
  • 对于后端已经实现逻辑了,而前端还没有设置显示的改造
  • 抽象代数:群论
  • Apache HttpClient使用
  • SpringMVC——数据传递的多种方式
  • 数据库三级填空+应用(2)
  • 开源模型应用落地-语音转文本-whisper模型-AIGC应用探索(四)
  • 记一次线上环境JAR冲突导致程序报错org.springframework.web.util.NestedServletException
  • 【学Rust写CAD】13 图像结构体(Image.rs)
  • 卷积神经网络在图像分割中的应用:原理、方法与进展介绍
  • OpenHarmony子系统开发 - init启动引导组件(三)