C#实现Socket数据接收的三种经典方式

 更新时间:2025年08月15日 10:15:15   作者:小码编匠  
本文将深入探讨 C# 中实现 Socket 数据接收的三种经典方式,结合真实案例分析常见问题,并提供优化后的完整代码示例,希望对大家有一定的帮助

前言

在工业自动化、物联网(IoT)和网络通信等领域,Socket 编程是实现设备间数据交互的核心技术。C# 作为 .NET 平台的主要开发语言,提供强大的网络编程支持。然而,在实际开发中,很多开发在使用 Socket.Receive 方法接收数据时,常常遇到数据丢失、接收延迟、性能瓶颈等问题。

本文将深入探讨 C# 中实现 Socket 数据接收的三种经典方式,结合真实案例分析常见问题,并提供优化后的完整代码示例,帮助大家开发稳定、高效的网络通信系统。

问题背景

在传统的 Socket 数据接收方式中,通常采用如下代码:

int recv;//定义接收数据长度变量
IPEndPoint ipEnd = new IPEndPoint(IPAddress.Parse(textBox1.Text), int.Parse(textBox2.Text));
//接收端所监听的接口,ip也可以用IPAddress.Any
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//初始化一个Socket对象
socket.Bind(ipEnd);
//绑定套接字到一个IP地址和一个端口上(bind());
socket.Listen(10);
while (true)
{
    byte[] data = new byte[1024];//对data清零
    Socket clientSocket = socket.Accept(); 
    //一旦接受连接,创建一个客户端
    recv = clientSocket.Receive(data);
    if (recv == 0) 
    //如果收到的数据长度小于0,则退出
        break;
    string stringData = "0x" + BitConverter.ToString(data).Replace("-", " 0x").ToLower();
    
    this.Invoke((EventHandler)delegate
    {
        richTextBox1.Text += DateTime.Now.ToString("yy-MM-dd hh:mm:ss")  + stringData + "\n";
    });
}

这种写法在简单场景下看似可行,但在处理高频、小包数据(如金属门每10秒发送一次数据)时,会出现严重的数据积压和延迟——原本应60秒接收6组数据,却可能60秒才收到一组,导致数据丢失。

根本原因在于:Receive 方法被放在 Accept 之后的单次调用中,未能持续监听和接收数据流。

三种高效的数据接收方式

为解决上述问题,以下介绍三种经过验证的 C# Socket 数据接收方案。

方式一:基于NetworkStream.Read的持续监听

使用 TcpClientNetworkStream,通过 Stream.Read 方法实现阻塞式持续读取。

private void BGWorker_DoWork()
{
    var serverIPEndPoint = new IPEndPoint(IPAddress.Parse("192.168.1.99"), 8234); // 当前服务器使用的ip和端口
    TcpListener tcpListener = new TcpListener(serverIPEndPoint);
    tcpListener.Start();
    Console.WriteLine("服务端已启用......"); // 阻塞线程的执行,直到一个客户端连接
    tcpClient = tcpListener.AcceptTcpClient();
    Console.WriteLine("已连接.");
    stream = tcpClient.GetStream();          // 创建用于发送和接受数据的NetworkStream

    var t1 = new Thread(ReceiveMsg);
    t1.IsBackground = true;
    t1.Start();
}

void ReceiveMsg()
{
    byte[] buffer = new byte[1024]; // 预设最大接受1024个字节长度,可修改
    int count = 0;
    try
    {
        while ((count = stream.Read(buffer, 0, buffer.Length)) != 0)
        {
            string stringData = "0x" + BitConverter.ToString(buffer, 0, count).Replace("-", " 0x").ToLower();
            Console.WriteLine($"{tcpClient.Client.LocalEndPoint.ToString()}:{DateTime.Now.ToString("yy-MM-dd hh:mm:ss -*- ") + stringData + "\n"}");
            this.Invoke((EventHandler)delegate
            {
                richTextBox1.Text += DateTime.Now.ToString("yy-MM-dd hh:mm:ss -*- ") + stringData + "\n";
            });
        }
    }
    catch(Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}

优点:代码简洁,NetworkStream.Read 自动处理流的持续读取。

适用场景:单客户端通信,如设备与上位机一对一连接。

方式二:多线程处理多个客户端连接

为每个客户端连接创建独立线程,确保接收不阻塞主线程。

private void BGWorker_DoWork1()
{
    int recv;
    IPEndPoint ipEnd = new IPEndPoint(IPAddress.Parse(textBox1.Text), int.Parse(textBox2.Text));
    Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    socket.Bind(ipEnd);
    socket.Listen(10);

    Thread thread = new Thread(Listen);
    thread.IsBackground = true;
    thread.Start(socket);
}

void Listen(object o)
{
    try
    {
        Socket socketWatch = o as Socket;
        while (true)
        {
            socketSend = socketWatch.Accept();
            Thread r_thread = new Thread(Received);
            r_thread.IsBackground = true;
            r_thread.Start(socketSend);
        }
    }
    catch { }
}

void Received(object o)
{
    try
    {
        Socket socketSend = o as Socket;
        while (true)
        {
            byte[] buffer = new byte[1024 * 1024 * 3];
            int len = socketSend.Receive(buffer);
            if (len == 0)
            {
                break;
            }
            string stringData = "0x" + BitConverter.ToString(buffer, 0, len).Replace("-", " 0x").ToLower();
            this.Invoke((EventHandler)delegate
            {
                richTextBox1.Text += DateTime.Now.ToString("yy-MM-dd hh:mm:ss -*- ") + stringData + "\n";
            });
        }
    }
    catch { }
}

优点:支持多客户端并发连接,接收独立,互不干扰。

适用场景:多设备接入的服务器端程序。

方式三:Task + Background Thread 实现异步接收

结合 Task.Run 和后台线程,实现轻量级异步接收,避免线程资源浪费。

private void BGWorker_DoWork2()
{
    int recv;
    IPEndPoint ipEnd = new IPEndPoint(IPAddress.Parse(textBox1.Text), int.Parse(textBox2.Text));
    Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    socket.Bind(ipEnd);
    socket.Listen(10);
    new Thread(delegate ()
    {
        Socket clientSocket = null;
        while (true)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();

            clientSocket = socket.Accept();

            Task.Run(() =>
            {
                while (true)
                {
                    byte[] data = new byte[50];
                    recv = clientSocket.Receive(data, 0, data.Length, SocketFlags.None);
                    string stringData = "0x" + BitConverter.ToString(data, 0, recv).Replace("-", " 0x").ToLower();

                    this.Invoke((EventHandler)delegate
                    {
                        richTextBox1.Text += DateTime.Now.ToString("yy-MM-dd hh:mm:ss -*- ") + stringData + "\n";
                    });
                    sw.Stop();
                    long times = sw.ElapsedMilliseconds;
                    this.Invoke((EventHandler)delegate
                    {
                        richTextBox1.Text += "执行查询总共使用了" + times + "毫秒" + "\n";
                    });
                }
            });
        }
    })
    { IsBackground = true }.Start();
}

优点:异步非阻塞,资源利用率高,适合高并发场景。

适用场景:需要高性能、低延迟的工业通信系统。

总结

本文通过一个真实的数据接收延迟案例,揭示了传统 Socket.Receive 使用方式的局限性,并提供了三种经过实践验证的优化方案:

1、NetworkStream.Read:适合简单、稳定的单连接场景,代码简洁。

2、多线程 Accept + Receive:适合多客户端并发接入,结构清晰。

3、Task + Thread 异步模式:适合高性能、高并发系统,资源利用更优。

选择哪种方式,取决于具体的应用场景和性能需求。

无论哪种方式,核心原则是:必须将数据接收逻辑置于持续监听的循环中,避免一次性调用导致的数据丢失

另外,结合 Stopwatch 进行性能监控、使用 Invoke 安全更新 UI、合理设置缓冲区大小,都是构建稳定 Socket 通信系统的关键。

到此这篇关于C#实现Socket数据接收的三种经典方式的文章就介绍到这了,更多相关C# Socket数据接收内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C#的Socket实现UDP协议通信示例代码

    C#的Socket实现UDP协议通信示例代码

    本篇文章主要介绍了C#的Socket实现UDP协议通信示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-01-01
  • 基于DateTime.ParseExact方法的使用详解

    基于DateTime.ParseExact方法的使用详解

    本篇文章是对DateTime.ParseExact方法的使用进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • C#利用QrCode.Net生成二维码(Qr码)的方法

    C#利用QrCode.Net生成二维码(Qr码)的方法

    QrCode.Net是一个使用C#编写的用于生成二维码图片的类库,使用它可以非常方便的为WinForm、WebForm、WPF、Silverlight和Windows Phone 7应用程序提供二维码编码输出功能。可以将二维码文件导出为eps格式
    2016-12-12
  • WPF实现动画效果(二)之From/To/By动画

    WPF实现动画效果(二)之From/To/By动画

    这篇文章介绍了WPF实现动画效果之From/To/By动画,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-06-06
  • 使用VS2005自带的混淆器防止你的程序被反编译的方法

    使用VS2005自带的混淆器防止你的程序被反编译的方法

    使用VS2005自带的混淆器防止你的程序被反编译的方法...
    2007-07-07
  • C#中多态、重载、重写区别分析

    C#中多态、重载、重写区别分析

    这篇文章主要介绍了C#中多态、重载、重写区别,采用实例较为通俗易懂的分析了多态、重载的重写的概念与用法,对于C#初学者有非常不错的借鉴价值,需要的朋友可以参考下
    2014-09-09
  • .net2.0+ Winform项目实现弹出容器层

    .net2.0+ Winform项目实现弹出容器层

    在实际工作中,如果能像菜单一样弹出自定义内容,会方便很多,比如查询时,比如下拉列表显示多列信息时,比如在填写某个信息需要查看一些信息树时。这个时候自定义弹出界面就显的非常重要了
    2015-08-08
  • C# 文字代码页 文字编码的代码页名称速查表

    C# 文字代码页 文字编码的代码页名称速查表

    最近有项目需要用到韩语,日语的编码转换,最重要的就是需要一个代码页的对照,找了一下,最新的代码页或者说编码表如下
    2011-11-11
  • C#使用正则表达式过滤html标签

    C#使用正则表达式过滤html标签

    最近在开发一个项目,其中有需求要求我们把一段html转换为一般文本返回,使用正则表达式是明智的选择,下面小编给介绍下C#使用正则表达式过滤html标签,需要的朋友参考下
    2016-08-08
  • 详解C#操作XML的方法总结

    详解C#操作XML的方法总结

    这篇文章主要为大家详细介绍了C#对XML文件进行一些基本操作的方法,譬如:生成xml文件、修改xml文件的节点信息等,需要的可以参考一下
    2022-11-11

最新评论