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

文章目录

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

前言

我最近有个需求要写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="Str"></param>
    /// <param name="Bytes"></param>
    public record TcpData(string Str, byte[] Bytes);

    /// <summary>
    /// 同步阻塞读取数据
    /// </summary>
    /// <param name="client"></param>
    /// <returns></returns>
    public static TcpData ReadMsg(TcpClient client)
    {
        NetworkStream networkStream = client.GetStream();
        var resBytes = new byte[client.ReceiveBufferSize];
        var num = networkStream.Read(resBytes, 0, resBytes.Length);
        resBytes = resBytes.Take(num).ToArray();
        var resStr = UnicodeEncoding.ASCII.GetString(resBytes);
        if (!IsConnect(client))
        {
            throw new Exception($"{client.Client.RemoteEndPoint?.ToString()}Tcp连接已断开");
        }
        
        return new TcpData(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="msg"></param>
    /// <returns></returns>
    public static TcpData SendAndReceive(TcpClient tcpClient,string msg)
    {
        SendMsg(tcpClient,msg);
        return ReadMsg(tcpClient);
    }

    public static TcpData SendAndReceive(TcpClient tcpClient, byte[] msg)
    {
        SendMsg(tcpClient, msg);
        return ReadMsg(tcpClient);
    }
    


    /// <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.mfbz.cn/a/259118.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《数据结构、算法与应用C++语言描述》- 最小输者树模板的C++实现

输者树 完整可编译运行代码见&#xff1a;Github::Data-Structures-Algorithms-and-Applications/_31loserTree 输者树&#xff1a;每一个内部节点所记录的都是比赛的输者&#xff0c;晋级的节点记录在边上。本文中&#xff0c;赢者是分数较低的那个&#xff0c;输者是分数高…

线性回归中的似然函数、最大似然估计、最小二乘法怎么来的(让你彻底懂原理)收官之篇

图1 图2 图3 图4 问1&#xff1a;为什么要引入似然函数&#xff1f; 在线性回归中引入似然函数是为了通过概率统计的方法对模型参数进行估计。简单来说&#xff0c;我们希望找到一组参数&#xff0c;使得我们观测到的数据在给定这组参数的情况下最有可能发生。 问:1&#xf…

js之零碎工具(四)

一、数组的去重 简单类型的去重 let arr [1, 2, 2, 3, 4, 4, 5]; let uniqueArr [...new Set(arr)]; console.log(uniqueArr); // 输出&#xff1a;[1, 2, 3, 4, 5]在这个例子中&#xff0c;我们首先创建了一个新的 Set 对象&#xff0c;并将数组 arr 作为参数传递给 Set 的…

深度学习中的张量维度

1 深度学习中的张量 在深度学习框架中&#xff0c;Tensor&#xff08;张量&#xff09;是一种数据结构&#xff0c;用于存储和操作多维数组。张量可以被视为一种扩展的矩阵&#xff0c;它可以具有任意数量的维度。 在深度学习中&#xff0c;张量通常被用来表示神经网络的输入…

管理类联考——数学——真题篇——按题型分类——充分性判断题——蒙猜——按题号

上来先找C&#xff1a;一个等号一个不等号型&#xff0c;一个定量一个定性型&#xff0c;取值范围有交集型&#xff0c;最后找选项需要联立型&#xff08;因为是否需要联立&#xff0c;不要判断&#xff0c;一般如十字交叉&#xff09;。一般也就3-4个C。详见这里 找完C后找AB&…

Java开发框架和中间件面试题(1)

1.什么是Spring框架&#xff1f; Spring是一种轻量级框架&#xff0c;旨在提高开发人员的开发效率以及系统的可维护性。 我们一般说的Spring框架就是Spring Framework,它是很多模块的集合&#xff0c;使用这些模块可以很方便的协助我们进行开发。这些模块是核心容器、数据访…

制造行业定制软件解决方案——工业信息采集平台

摘要&#xff1a;针对目前企业在线检测数据信号种类繁多&#xff0c;缺乏统一监控人员和及时处置措施等问题。蓝鹏测控开发针对企业工业生产的在线数据的集中采集分析平台&#xff0c;通过该工业信息采集平台可将企业日常各种仪表设备能够得到数据进行集中分析处理存储&#xf…

vmware离线安装docker-compose

vmware离线安装docker-compose 最近安装docker-compose&#xff0c;发现git取拉取&#xff0c;不是拒绝连接就是报443错误&#xff0c;或者其他错误 最后发现用包直接传上去好用&#xff0c;不用git拉取了 离线安装docker-compose 本文章给的docker-compose离线包&#xff0c;…

【超详细】基于单片机控制的十字道路口交通灯控制

目录 最终效果 一、设计任务 二、设计报告 1 设计说明 1.1功能分析 1.1.1整体系统功能分析 1.1.2显示状态功能分析 1.1.3设置状态功能分析 1.1.4紧急状态功能分析 1.2方案比选 1.2.1车辆LED数码管倒计时显示板块 1.2.2车辆信号灯显示板块 1.2.3行人信号灯显示板块 …

Web请求与响应

目录 Postman Postman简介 Postman的使用 请求 简单参数 实体参数 数组参数 集合参数 日期参数 Json参数 路径参数 响应 ResponseBody 统一响应结果 Postman Postman简介 postman是一款功能强大的网页调试与发送网页http请求的Chrome插件&#xff0c;常用于进行…

Zoho Mail:1600万企业用户的信赖之选

Zoho Mail和Workplace在线办公套件一起&#xff0c;已经成长为一个集邮箱、即时通讯、生产力工具于一身的非常全面的强大平台。经过数十年持续深入的研发投入&#xff0c;我们的产品可以很好地服务大型企业。 这是Zoho创始人斯瑞达•温布在Zoho Mail15周年之际发布的感想。 过去…

MSVC编译 openssl windows 库

开发需要在windows下集成 openssl 库&#xff0c;参考官方指导完成了编译&#xff1a;openssl/NOTES-WINDOWS.md at master openssl/openssl 不过&#xff0c;最后还是走了直接下载的捷径。 1. 安装 ActivePerl 需要在 ActiveState 注册账户&#xff0c;之后彼会提供具体的…

跨境卖家必看!TikTok带货经验分享,TikTok直播带货怎么做?

如今直播带货正发展得如火如荼&#xff0c;不少跨境人也纷纷做起了带货&#xff0c;其中TikTok带货的力量不容小觑&#xff0c;也已经成为了跨境电商运营非常火爆的营销方式&#xff0c;有很多朋友问龙哥TikTok带货怎么做&#xff0c;其实以龙哥这么多年的经验来看&#xff0c;…

VMware vSphere 虚拟机迁移按钮灰色解决方案

现象&#xff1a;在 vCenter Server 中右键单击虚拟机&#xff0c;然后单击迁移时&#xff0c;迁移选项将灰显。 原因&#xff1a;在虚拟机备份完成后&#xff0c;没有移除 vCenter Server 数据库 vpx_disabled_methods 表中的条目时&#xff0c;可能会出现此问题。 解决方案&a…

保姆月嫂企业网站建设的效果如何

保姆月嫂月子中心成为很多家庭的选择&#xff0c;随着人们生活质量提升及消费升级&#xff0c;围绕母婴行业、居家场景的服务需求度越来越多&#xff0c;而市场同行竞争压力下&#xff0c;也促使着各家保姆月嫂品牌需要通过多种方式增长破圈。 虽然保姆月嫂的市场需求度较高&am…

【Python炫酷系列】祝考研的友友们金榜题名吖(完整代码)

文章目录 环境需求完整代码详细分析系列文章环境需求 python3.11.4及以上版本PyCharm Community Edition 2023.2.5pyinstaller6.2.0(可选,这个库用于打包,使程序没有python环境也可以运行,如果想发给好朋友的话需要这个库哦~)【注】 python环境搭建请见:https://want595.…

谷达冠楠科技:现在开抖音小店怎么样前景好不好

随着互联网的发展&#xff0c;电商平台已经成为了人们生活中不可或缺的一部分。而近年来&#xff0c;抖音作为短视频平台的领军者&#xff0c;也推出了自己的电商功能——抖音小店。那么&#xff0c;现在开抖音小店的前景如何呢? 首先&#xff0c;我们需要了解抖音的用户基数。…

CountDownLatch和Semaphore的区别?

CountDownLatch和Semaphore都是在Java中用于多线程协同的工具&#xff0c;但它们有一些重要的区别。 CountDownLatch&#xff1a; 用途&#xff1a; 主要用于等待一个或多个线程完成操作&#xff0c;它的计数器只能被减少&#xff0c;不能被增加。计数&#xff1a; 初始化时需…

致深空中最遥远的你

"旅行者1号" 的在11月14日飞行数据系统陷入了自动重复的状态&#xff0c;飞行数据系统的电信单元开始重复发回1和0模式就像陷入循环一样&#xff0c;旅行者1号目前离地球约240亿公里发回的消息需要大约22.5小时的传播时间。NASA分析故障来官探测器上的两台计算机&…

攻防世界-web-ics07

1. 题目描述 工控云管理系统项目管理页面解析漏洞 打开链接&#xff0c;是这样的一个界面 我们点击项目管理 可以看到&#xff0c;这里有一个查询界面&#xff0c;还有个view-source的链接&#xff0c;我们点击下view-source&#xff0c;可以看到这里面共有三段php代码 第一段…