C# Tcplistener,Tcp服务端简易封装

news2024/11/24 20:43:41

文章目录

  • 前言
  • 相关文章
  • 前言
  • 设计
  • 代码
  • 简单使用
  • 运行结果

前言

我最近有个需求要写Tcp服务端,我发现Tcp服务端的回调函数比较麻烦,简化Tcp的服务,我打算自己封装一个简单的Tcp服务端。

相关文章

C# TCP应用编程三 异步TCP应用编程

C# Tcpclient Tcplistener 服务器接收多个客户端消息通讯

关于C#Socket断开重连问题

前言

我最近有个Tcp服务端的项目,发现TcpListener 服务端官方写起来很麻烦。而且没有回调函数。现在做个简单的服务端封装

设计

TcpServerService
ShowMsg:打印消息
AddClient_CallBack:新增Tcp客户端回调函数
SendMsg/ReceiveMsg:Tcp客户端发送接受回调
Clients:Tcp客户端集合,连接增加,断开去除
其它函数

代码

 public class TcpServeService
 {
     public string Ip { get; set; }

     public int Port { get; set; }

     public TcpListener Server { get; set; }

     public List<TcpClient> Clients { get; set; }

     /// <summary>
     /// 客户端添加回调函数,如果要重写通讯逻辑需要覆盖
     /// </summary>
     public Action<TcpClient> AddClient_CallBack { get; set; }


     public Action<string> ShowMsg { get; set; }

     /// <summary>
     /// 默认自动回复Tcp服务端
     /// </summary>
     /// <param name="ip"></param>
     /// <param name="port"></param>
     public TcpServeService(string ip, int port)
     {
         Clients = new List<TcpClient>();
         ShowMsg = (msg) => Console.WriteLine(msg);
         AddClient_CallBack = (client) => AutoSendBack(client);
         this.Ip = ip;
         this.Port = port;
         Server = new TcpListener(IPAddress.Parse(ip), port);
     }


     /// <summary>
     /// Tcp添加Client回调
     /// </summary>
     /// <param name="ar"></param>
     private void DoAcceptTcpclient(IAsyncResult ar)
     {
         // Get the listener that handles the client request.
         TcpListener listener = (TcpListener)ar.AsyncState;

         // End the operation and display the received data on 
         // the console.
         TcpClient client = listener.EndAcceptTcpClient(ar);

         Clients.Add(client);

         // Process the connection here. (Add the client to a
         // server table, read data, etc.)
         ShowMsg($"Tcp客户端连接成功!,当前连接数{Clients.Count},Id[{client.Client.RemoteEndPoint.ToString()}]");
         AddClient_CallBack(client);
         //开启线程用来不断接收来自客户端的数据
         Server.BeginAcceptTcpClient(new AsyncCallback(DoAcceptTcpclient), Server);
     }

     /// <summary>
     /// 移除Tcp客户端
     /// </summary>
     /// <param name="client"></param>
     public void RemoveClient(TcpClient client)
     {
         NetworkStream stream = client.GetStream();
         ShowMsg($"Tcp客户端连接断开!,当前连接数{Clients.Count},Id[{client.Client.RemoteEndPoint.ToString()}]");
         stream.Close();
         client.Close();
         Clients.Remove(client);
     }

     /// <summary>
     /// 启动Tcp服务
     /// </summary>
     public void Start()
     {
         Server.Start();
         Server.BeginAcceptTcpClient(new AsyncCallback(DoAcceptTcpclient), Server);
         ShowMsg($"Tcp服务端启动成功!IP[{Ip}],Port[{Port}]");
     }

     /// <summary>
     /// 同步阻塞读取数据
     /// </summary>
     /// <param name="client"></param>
     /// <returns></returns>
     public static (string str, byte[] bytes) ReadMsg(TcpClient client)
     {
         NetworkStream networkStream = client.GetStream();
         var resBytes = new byte[client.ReceiveBufferSize];
         networkStream.Read(resBytes, 0, resBytes.Length);
         var resStr = UnicodeEncoding.ASCII.GetString(resBytes);
         if (!IsConnect(client))
         {
             throw new Exception($"{client.Client.RemoteEndPoint?.ToString()}Tcp连接已断开");
         }
         return (resStr, resBytes);
     }
     /// <summary>
     /// 发送Ascll数据
     /// </summary>
     /// <param name="tcpClient"></param>
     /// <param name="msg"></param>
     public static void SendMsg(TcpClient tcpClient, string msg)
     {
         byte[] arrSendMsg = Encoding.UTF8.GetBytes(msg);
         SendMsg(tcpClient, arrSendMsg);
     }

     /// <summary>
     /// Tcp客户端连接是否断开
     /// </summary>
     /// <param name="tcpClient"></param>
     /// <returns></returns>
     public static bool IsConnect(TcpClient tcpClient)
     {
         if (tcpClient.Client.Poll(1, SelectMode.SelectRead) && tcpClient.Available == 0)
         {
             return false;
         }
         else { return true; }
     }

     /// <summary>
     /// 发送Bytes[]数据
     /// </summary>
     /// <param name="tcpClient"></param>
     /// <param name="msg"></param>
     public static void SendMsg(TcpClient tcpClient, byte[] msg)
     {
         NetworkStream networkStream = tcpClient.GetStream();
         networkStream.Write(msg, 0, msg.Length);
     }


     /// <summary>
     /// 默认自动回复,异常捕捉
     /// </summary>
     /// <param name="tcpClient"></param>
     /// <param name="timeOut">超时时间</param>
     /// <returns></returns>
     public async Task AutoSendBack(TcpClient tcpClient, int timeOut = 10 * 1000)
     {
         //超时时间
         tcpClient.ReceiveTimeout = timeOut;
         tcpClient.SendTimeout = timeOut;
         while (true)
         {
             try
             {
                 if (!Clients.Contains(tcpClient))
                 {
                     throw new Exception("Tcp客户端已被移除!");
                 }
                 var receive = ReadMsg(tcpClient);
                 ShowMsg($"TcpClient[{tcpClient.Client.RemoteEndPoint?.ToString()}]:收到数据{receive.str}");
                 SendMsg(tcpClient, receive.str);
             }
             catch (Exception ex)
             {
                 RemoveClient(tcpClient);
                 ShowMsg("发送失败");
                 ShowMsg(ex.Message);
             }
         }
     }

简单使用

//对tcpServeService进行了默认配置,默认自动回复,自动维护Client集合
TcpServeService tcpServeService = new TcpServeService("192.168.100.21", 10003);

//如果想要自定义回复,需要覆盖AddClient_CallBack函数,使用异步任务处理连接
//tcpServeService.AddClient_CallBack = ((client) => {
//    Task.Run(() =>
//    {
//        //你的客户端连接异步任务
//    });
//});

//如果想要打印在Winfrom/WPF的界面,覆盖此回调
//tcpServeService.ShowMsg = (msg) =>
//{
//    //你的消息打印函数
//};
//tcpServeService.Start();
tcpServeService.Start();

运行结果

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1321517.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

开源一个超好用的接口Mock工具——Msw-Tools

作为一名前端开发&#xff0c;是不是总有这样的体验&#xff1a;基础功能逻辑和页面UI开发很快速&#xff0c;本来可以提前完成&#xff0c;但是接口数据联调很费劲&#xff0c;耗时又耗力&#xff0c;有时为了保证进度还不得不加加班。 为了摆脱这种痛苦&#xff0c;经过一周的…

uniapp 用于开发H5项目展示饼图,使用ucharts 饼图示例

先下载ucharts H5示例源码&#xff1a; uCharts: 高性能跨平台图表库&#xff0c;支持H5、APP、小程序&#xff08;微信小程序、支付宝小程序、钉钉小程序、百度小程序、头条小程序、QQ小程序、快手小程序、360小程序&#xff09;、Vue、Taro等更多支持canvas的框架平台&#…

掌握电脑开机密码设置技巧,让你的电脑数据更安全!

在现代社会&#xff0c;电脑已经成为了我们日常生活中必不可少的工具。然而&#xff0c;随着科技的发展&#xff0c;我们使用电脑也面临着一些安全隐患。为了保护个人数据的安全&#xff0c;设置开机密码就变得十分重要。本文将为大家介绍电脑怎么设置开机密码&#xff0c;以保…

在RTOS中验证互斥量有效解决优先级反转现象

我们在stm32f103c8t6单片机上验证RTOS互斥量有效解决优先级反转现象&#xff0c;利用stm32cube进行RTOS的配置。在选择TIM2当做RTOS的时钟&#xff0c;裸机的时钟源默认是 SysTick&#xff0c;但是开启 FreeRTOS 后&#xff0c;FreeRTOS会占用 SysTick &#xff08;用来生成1ms…

数组去重及去除指定值,每一个对象添加属性值

1、数组去重ES6写法 Set() // 数组去重 let arr [1,2,4,6,3,2,6,7,7,2,9,0,1,5] arr [...new Set(arr)] console.log(arr); 2、数组去除指定值 filter() // 数组去除指定值 let arr [1,2,4,6,3,2,6,7,7,2,9,0,1,5] const num 7 arr arr.filter(item>item!num) cons…

Go集成elasticsearch8极简demo,光速入门

Go集成elasticsearch8极简demo,光速入门 配置go环境创件go mod工程代码实现配置go环境 编辑器添加goproxy GO111MODULE=on;GOPROXY=https://mirrors.wps.cn/go/,https://goproxy.cn,direct;GOSUMDB=off创件go mod工程 mkdir demo cd demo go mod init demo代码实现 在demo…

测试用例的修改更新

测试用例的修改更新是指测试过程中由于用户需求的改变&#xff0c;或者测试过程中发现有新的需求产生&#xff0c;使得测试用例需要进行修改。修改更新测试用例不仅是一种测试技术&#xff0c;更是一种质量保证的方法。但修改和更新测试用例的技术要点在于&#xff1a; 1、执行…

设计模式之创建型设计模式(一):单例模式 原型模式

单例模式 Singleton 1、什么是单例模式 在软件设计中&#xff0c;单例模式是一种创建型设计模式&#xff0c;其主要目的是确保一个类只有一个实例&#xff0c;并提供一个全局访问点。 这意味着无论何时需要该类的实例&#xff0c;都可以获得相同的实例&#xff0c;而不会创建…

API接口能力不足?Bug处理慢?Lazada开放平台API商品接入

7月30日正式发布的Lazada开放平台2.0&#xff08;Lazada Open Platform 2.0&#xff09;&#xff0c;从商品API、订单API、IM&#xff08;即时通信&#xff09; API、营销工具等几大方向&#xff0c;带来全新升级的API体系&#xff0c;共新增47个接口、优化19个接口&#xff0c…

JVM-9-Class类文件的结构

Java技术能够一直保持着非常良好的向后兼容性&#xff0c;Class文件结构的稳定功不可没。 Class文件是一组以8个字节为基础单位的二进制流&#xff0c;各个数据项目严格按照顺序紧凑地排列在文件之中。 Class文件格式采用一种类似于C语言结构体的伪结构来存储数据&#xff0c…

详细教程 - 从零开发 鸿蒙harmonyOS应用 第八节——鸿蒙操作系统中的文件读写操作封装

一、引言 鸿蒙操作系统是华为自主研发的全场景操作系统。在这篇博客中&#xff0c;我们将探讨如何在鸿蒙操作系统中实现文件读写操作的封装。 二、文件读写操作 在鸿蒙操作系统中&#xff0c;文件读写操作是一个常见的需求。下面是一个简单的文件读写操作的封装示例&#xff1…

数据分析场景下,企业大模型选型的思路与建议

来源/作者&#xff1a;爱分析 随着大模型带来能力突破&#xff0c;让AI与数据分析相互结合&#xff0c;使分析结果更好支撑业务&#xff0c;促进企业内部数据价值释放&#xff0c;成为了当下企业用户尤为关注的话题。本次分享主要围绕数据分析场景下大模型底座的选型思路&#…

上传xml文件进行跨站脚本攻

文件路径或者URL&#xff1a;xxxxx <?xml version"1.0" encoding"iso-8859-1"?> <xsl:stylesheet version"1.0" xmlns:xsl"http://www.w3.org/1999/XSL/Transform"> <xsl:template match"/"> <html…

部署threestudio | stable zero123

选择我在autodl的stable-zero123这个镜像&#xff0c;或者直接选这个基础环境 开机后切换到conda的base环境 这里注意一点就是目前stable-zero123这个镜像还没解决的问题&#xff0c;就是没法使用xformers&#xff0c;所以如果重新配置&#xff0c;在这里就要先安装指定版本的…

liunx下用C++使用freetype库在opencv上打中文字

1、/visualizer.cpp:11:10: fatal error: ft2build.h: 没有那个文件或目录 11 | #include <ft2build.h> freetype安装问题&#xff0c;要把文件拉到根目录&#xff0c;不然找不到文件 2、编译失败找不到定义 /usr/bin/ld: CMakeFiles/interactive_face_detection_de…

树莓派Ubuntu系统安装OpenCV教程

硬件&#xff1a;树莓派4B 2G 内存卡32G 软件&#xff1a;系统是ubuntu-mate-20.04.1-desktop-arm64raspi.img 步骤如下&#xff1a;一、更新系统软件包列表和安装基本工具 sudo apt-get update sudo apt-get upgrade sudo apt-get install build-essential cmake git二、安装…

【Linux】Linux运维基础

Linux简介&#xff1a; Linux是一个开源的操作系统内核&#xff0c;最初由Linus Torvalds创建。它通常与GNU工具一起使用&#xff0c;以创建一个完整的操作系统。Linux操作系统有许多基于内核的发行版&#xff0c;如Ubuntu、CentOS、Debian等&#xff0c;每个发行版都有其独特的…

解决docker alpine /bin/sh: ./main: not found

解决docker alpine /bin/sh: ./main: not found golang中编译之后的二进制文件部署在alpine镜像中出现了not found问题解决这种情况是因为动态链接库位置错误导致的&#xff0c;alpine镜像使用的是musl libc而不是gun libc。因而动态链接库的位置不一致。在基础镜像内执行&…

13. 从零用Rust编写正反向代理, HTTP中的压缩gzip,deflate,brotli算法

wmproxy wmproxy是由Rust编写&#xff0c;已实现http/https代理&#xff0c;socks5代理&#xff0c; 反向代理&#xff0c;静态文件服务器&#xff0c;内网穿透&#xff0c;配置热更新等&#xff0c; 后续将实现websocket代理等&#xff0c;同时会将实现过程分享出来&#xff…

IDEA版SSM入门到实战(Maven+MyBatis+Spring+SpringMVC) -Spring的AOP前奏

第一章 AOP前奏 1.1 代理模式 代理模式&#xff1a;我们需要做一件事情&#xff0c;又不期望自己亲力亲为&#xff0c;此时&#xff0c;可以找一个代理【中介】 我们【目标对象】与中介【代理对象】不能相互转换&#xff0c;因为是“兄弟”关系 1.2 为什么需要代理【程序中…