C#[.Net5]对TcpListener进行再封装
迪丽瓦拉
2024-06-03 05:03:27
0

背景

今天跟同事谈及代码以及工程标准化,心血来潮,联想到工作中常用的TcpListener也可以标准化封装,加快编程,争取摸鱼时间,固有此一文。

方案

1.把参数配置化到文件中[TcpListenerLibrary.ini]

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

2.自定义一个C#库项目[TcpListenerLibrary]

 

3. 编写代码,以读取配置文件的方式加载参数,以引发事件的方式留接口给后续使用的程序实现

类文件[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
{/// /// 自定义Tcp服务类/// public class BL_Server{/// /// 服务端/// public TcpListener Server { get; set; }/// /// 客户端集合,字典形式储存/// public ConcurrentDictionary Clients { set; get; }/// /// 配置文件的物理路径/// private string IniPath { get; }/// /// ip地址/// private string Ip { get; }/// /// 端口号/// private int Port { get; }/// /// 最大挂起连接数/// private int BackLog { get; } = int.MaxValue;/// /// 缓冲区多少K/// private int K { get; } = 10;/// /// 构造函数/// /// 配置文件的物理路径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;}}}/// /// 开启服务/// public void Start(){if (ReceiveEvent == null){throw new Exception("接收消息事件[ReceiveEvent]未编写处理方法,开启服务失败");}IPAddress iPAddress = IPAddress.Parse(Ip);Server = new TcpListener(iPAddress, Port);Clients = new ConcurrentDictionary();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;}}});}});}/// /// 向所有客户端发送数据/// /// 消息内容/// public void SendToAllClients(byte[] buffer){foreach (TcpClient client in Clients.Values){client.Client.Send(buffer);}}/// /// 向特定客户端发送数据/// /// 客户端标识/// 消息内容public void SendToClient(string key, byte[] buffer){Clients[key].Client.Send(buffer);}/// /// 接收消息委托/// /// /// /// public delegate void ReceiveDelegate(string key, byte[] buffer, int length);/// /// 接收消息事件,开启服务前必须给此事件绑定处理消息方法/// public event ReceiveDelegate ReceiveEvent;}
}

4.生成解决方案,会在bin/Debug/net5.0文件夹下看到对应的dll文件

使用

1.新建一个控制台项目[TcpListenerLibrary_Test ],引入上述生成的dll文件,并复制一份配置文件到这个测试项目的bin/Debug/net5.0文件夹下

2.改写类文件[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.打开一个网络调试助手,进行测试连接,以及消息发送接收

可以看到,顺利完成,达到预期 。

总结

上述例子可以看出,一些常用的类我们可以进行再封装,以达到标准和重用性,大大提供编程效率。虽然说做的多做的快不一定赚的多,但是肯定闲得慌,多留点时间摸鱼它不香吗?磨刀不误砍柴工,封装复用真轻松! 

相关内容