C#[.Net5] repackages TcpListener

background

Today, I talked to my colleagues about code and engineering standardization. On a whim, I thought that TcpListener, which is commonly used in work, can also be standardized and packaged, so as to speed up programming and gain time for fishing. This article is inherent in this article.

plan

1. Configure the parameters into the file [TcpListenerLibrary.ini]

# 本机地址
ip=127.0.0.1
# 监听端口
port=3306
# 启动对具有最大挂起连接数的传入连接请求的侦听。
BackLog=10
# 接收数据缓存区多少K
K=10

2. Customize a C# library project [TcpListenerLibrary]

 

3. Write the code, load the parameters by reading the configuration file, and leave the interface for the subsequent program implementation by triggering the event

class file [BL_Server.cs]

using System;
using System.Collections.Concurrent;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace TcpListenerLibrary
{
    /// <summary>
    /// 自定义Tcp服务类
    /// </summary>
    public class BL_Server
    {
        /// <summary>
        /// 服务端
        /// </summary>
        public TcpListener Server { get; set; }

        /// <summary>
        /// 客户端集合,字典形式储存
        /// </summary>
        public ConcurrentDictionary<string, TcpClient> Clients { set; get; }

        /// <summary>
        /// 配置文件的物理路径
        /// </summary>
        private string IniPath { get; }

        /// <summary>
        /// ip地址
        /// </summary>
        private string Ip { get; }

        /// <summary>
        /// 端口号
        /// </summary>
        private int Port { get; }

        /// <summary>
        /// 最大挂起连接数
        /// </summary>

        private int BackLog { get; } = int.MaxValue;

        /// <summary>
        /// 缓冲区多少K
        /// </summary>
        private int K { get; } = 10;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="iniPath">配置文件的物理路径</param>
        public BL_Server(string iniPath)
        {
            IniPath = iniPath;

            string[] lines = File.ReadAllLines(IniPath, Encoding.UTF8);
            foreach (string line in lines)
            {
                // #开头的当作注释,不处理
                if (line.Trim().StartsWith("#") || line.Trim().Length == 0)
                {
                    continue;
                }

                string[] parameters = line.Split('=');
                switch (parameters[0].ToLower())
                {
                    case "ip":
                        Ip = parameters[1];
                        break;
                    case "port":
                        Port = int.Parse(parameters[1]);
                        break;
                    case "backlog":
                        BackLog = int.Parse(parameters[1]);
                        break;
                    case "k":
                        K = int.Parse(parameters[1]);
                        break;
                }
            }
        }

        /// <summary>
        /// 开启服务
        /// </summary>
        public void Start()
        {
            if (ReceiveEvent == null)
            {
                throw new Exception("接收消息事件[ReceiveEvent]未编写处理方法,开启服务失败");
            }

            IPAddress iPAddress = IPAddress.Parse(Ip);
            Server = new TcpListener(iPAddress, Port);
            Clients = new ConcurrentDictionary<string, TcpClient>();
            Server.Start(BackLog);
            Console.WriteLine($"Tcp服务端已开启,[ip={Ip},port={Port},backlog={BackLog},k={K}]");

            Task.Run(() =>
            {
                while (true)
                {
                    TcpClient client = Server.AcceptTcpClient();
                    string key = client.Client.RemoteEndPoint.ToString();
                    Clients.TryAdd(key, client);
                    Console.WriteLine($"客户端[{key}]已连接,当前连接数[{Clients.Count}]");

                    Task.Run(() =>
                    {
                        while (true)
                        {
                            try
                            {
                                byte[] buffer = new byte[1024 * K];
                                int length = client.Client.Receive(buffer);
                                //客户端主动终止连接
                                if (length == 0)
                                {
                                    try
                                    {
                                        client.Close();
                                    }
                                    catch { }
                                    Clients.TryRemove(key, out _);
                                    Console.WriteLine($"{key}客户端主动断开,当前连接数[{Clients.Count}]");
                                    break;
                                }
                                try
                                {
                                    ReceiveEvent.Invoke(key, buffer, length);
                                }
                                catch (Exception e)
                                {
                                    Console.WriteLine($"[{key}]消息处理异常,请检查[ReceiveEvent事件代码]");
                                    Console.WriteLine(e.StackTrace);
                                }
                            }
                            catch (Exception e)
                            {
                                try
                                {
                                    client.Close();
                                }
                                catch { }
                                Clients.TryRemove(key, out _);
                                Console.WriteLine($"{key}客户端异常断开,当前连接数[{Clients.Count}]");
                                Console.WriteLine(e.StackTrace);
                                break;
                            }
                        }
                    });
                }
            });
        }

        /// <summary>
        /// 向所有客户端发送数据
        /// </summary>
        /// <param name="buffer">消息内容</param>
        /// 
        public void SendToAllClients(byte[] buffer)
        {
            foreach (TcpClient client in Clients.Values)
            {
                client.Client.Send(buffer);
            }
        }

        /// <summary>
        /// 向特定客户端发送数据
        /// </summary>
        /// <param name="key">客户端标识</param>
        /// <param name="buffer">消息内容</param>
        public void SendToClient(string key, byte[] buffer)
        {
            Clients[key].Client.Send(buffer);
        }

        /// <summary>
        /// 接收消息委托
        /// </summary>
        /// <param name="key"></param>
        /// <param name="buffer"></param>
        /// <param name="length"></param>
        public delegate void ReceiveDelegate(string key, byte[] buffer, int length);

        /// <summary>
        /// 接收消息事件,开启服务前必须给此事件绑定处理消息方法
        /// </summary>
        public event ReceiveDelegate ReceiveEvent;
    }
}

4. Generate the solution, and you will see the corresponding dll file in the bin/Debug/net5.0 folder

use

1. Create a new console project [TcpListenerLibrary_Test], import the dll file generated above, and copy a configuration file to the bin/Debug/net5.0 folder of the test project

2. Rewrite the class file [Program.cs]

using System;
using System.Text;
using TcpListenerLibrary;

namespace TcpListenerLibrary_Test
{
    class Program
    {
        static void Main(string[] args)
        {
            BL_Server bL_Server = new("TcpListenerLibrary.ini");
            bL_Server.ReceiveEvent += (key, buffer, length) =>
            {
                string message = Encoding.ASCII.GetString(buffer, 0, length);
                Console.WriteLine($"{key}:{message}");

                bL_Server.SendToAllClients(Encoding.ASCII.GetBytes($"Server:hi[{DateTime.Now:HHmmss}]"));
            };
            bL_Server.Start();

            Console.ReadKey();
        }
    }
}

3. Open a network debugging assistant, test the connection, and send and receive messages

It can be seen that it was successfully completed and met expectations.

Summarize

It can be seen from the above examples that we can repackage some commonly used classes to achieve standards and reusability, and greatly improve programming efficiency. Although it is said that doing more and doing faster does not necessarily mean that you will earn more, but you must be in a panic. Wouldn’t it be nice to save more time for fishing? Sharpening the knife does not cut firewood by mistake, packaging and reuse is really easy! 

Guess you like

Origin blog.csdn.net/qq_36694133/article/details/129508830