如何解决 .NET 套接字和 TCP 可能出现的丢包问题?

2024-04-15

我需要一些帮助来解决我在使用 .NET 套接字通过 TCP 传输大量数据时遇到的问题。

简而言之,当客户端应用程序启动时,它会连接到服务器上的特定端口。连接后,服务器开始向客户端发送实时数据,客户端在类似股票的 UI 中显示信息。服务器支持多个客户端工作站,因此数据将通过多个端口(多个套接字)发送。

一切都已实施,并且在缓慢进给和低产量的情况下运行良好。我正在对系统进行压力测试,以确保弹性和可扩展性。当我增加频率时,服务器运行完美。但是,我看到客户端上似乎丢失了数据包。这种情况随机发生。

目前,每条广播消息都以一个 4 字节值开头,用于标识该消息的长度。当我们在客户端接收数据时,我们将数据附加到缓冲区(流),直到收到该数量的字节。任何附加字节都被视为下一条消息的开始。再次强调,在我调高频率之前,这种方法效果很好。

在我的测试中,我发送了一个大约 225 字节的数据包,然后是一个大约 310kB 的数据包,另一个大约 40kb 的数据包。在大约 12 个客户端运行时,每 1 秒发送一条消息不会失败。将频率增加到 1/2 秒,我最终看到客户的一个显示器冻结了。到 1/4 秒,我可以在几秒钟内用最少 4 个客户端重现该问题。

查看我的代码(如果需要,我可以提供),我看到所有客户端都在接收数据,但不知何故信息“不同步”,并且预期长度值巨大(在 1 亿范围内) 。结果,我们只是不断地读取数据,却从未察觉到消息的结束。

我要么需要更好的方法,要么需要一种方法来确保我获得预期的数据并且不会丢失数据包。你能帮我吗?

UPDATE

我做了大量额外的测试,改变了消息的大小和发送频率。肯定存在相关性。消息大小越小,达到的频率就越高。但是,不可避免的是,我总是能够打破它。

因此,更准确地描述我正在寻找的是:

  1. 了解正在发生的事情。这将帮助我确定可能的解决方案,或者至少建立可靠行为的阈值。

  2. 实施故障安全机制,以便当问题发生时,我可以处理它并可能从中恢复。也许在数据流中添加校验和或类似的东西。

这是我在客户端(接收)应用程序中运行的代码:

public void StartListening(SocketAsyncEventArgs e)
{
    e.Completed += SocketReceive;
    socket.ReceiveAsync(e);
}

private void SocketReceive(Object sender, SocketAsyncEventArgs e)
{
    lock (_receiveLock)
    {
        ProcessData(e.Buffer, e.BytesTransferred);

        socket.ReceiveAsync(e);
    }
}

private void ProcessData(Byte[] bytes, Int32 count)
{
    if (_currentBuffer == null)
        _currentBuffer = new ReceiveBuffer();

    var numberOfBytesRead = _currentBuffer.Write(bytes, count);

    if (_currentBuffer.IsComplete)
    {
        // Notify the client that a message has been received (ignore zero-length, "keep alive", messages)
        if (_currentBuffer.DataLength > 0)
            NotifyMessageReceived(_currentBuffer);

        _currentBuffer = null;

        // If there are bytes remaining from the original message, recursively process
        var numberOfBytesRemaining = count - numberOfBytesRead;

        if (numberOfBytesRemaining > 0)
        {
            var remainingBytes = new Byte[numberOfBytesRemaining];
            var offset = bytes.Length - numberOfBytesRemaining;

            Array.Copy(bytes, offset, remainingBytes, 0, numberOfBytesRemaining);

            ProcessData(remainingBytes, numberOfBytesRemaining);
        }
    }
}


internal sealed class ReceiveBuffer
{
    public const Int32 LengthBufferSize = sizeof(Int32);

    private MemoryStream _dataBuffer = new MemoryStream();
    private MemoryStream _lengthBuffer = new MemoryStream();

    public Int32 DataLength { get; private set; }

    public Boolean IsComplete
    {
        get { return (RemainingDataBytesToWrite == 0); }
    }

    private Int32 RemainingDataBytesToWrite
    {
        get
        {
            if (DataLength > 0)
                return (DataLength - (Int32)_dataBuffer.Length);

            return 0;
        }
    }

    private Int32 RemainingLengthBytesToWrite
    {
        get { return (LengthBufferSize - (Int32)_lengthBuffer.Length); }
    }

    public Int32 Write(Byte[] bytes, Int32 count)
    {
        var numberOfLengthBytesToWrite = Math.Min(RemainingLengthBytesToWrite, count);

        if (numberOfLengthBytesToWrite > 0)
            WriteToLengthBuffer(bytes, numberOfLengthBytesToWrite);

        var remainingCount = count - numberOfLengthBytesToWrite;

        // If this value is > 0, then we have still have more bytes after setting the length so write them to the data buffer
        var numberOfDataBytesToWrite = Math.Min(RemainingDataBytesToWrite, remainingCount);

        if (numberOfDataBytesToWrite > 0)
            _dataBuffer.Write(bytes, numberOfLengthBytesToWrite, numberOfDataBytesToWrite);

        return numberOfLengthBytesToWrite + numberOfDataBytesToWrite;
    }

    private void WriteToLengthBuffer(Byte[] bytes, Int32 count)
    {
        _lengthBuffer.Write(bytes, 0, count);

        if (RemainingLengthBytesToWrite == 0)
        {
            var length = BitConverter.ToInt32(_lengthBuffer.ToArray(), 0);

            DataLength = length;
        }
    }
}

如果没有看到您的代码,我们只能猜测。我的猜测是:您是否正在考虑您阅读的情况少于完整的 4 字节标头?您可能只读取其中的一个、两个或三个字节。更高的数据量会导致这种情况更频繁地发生。

由于 TCP 是一个可靠的协议,因此not由于丢包。任何丢失的数据包都会导致发生以下两种情况之一:

  1. 丢失的数据被重新传输,接收器会经历短暂的暂停,但永远不会看到丢失的数据或乱序的数据。
  2. 套接字已关闭。

UPDATE

Your IsComplete方法返回true将部分长度写入缓冲区后。这会导致您的接收器代码ProcessData()丢弃已接收到的长度缓冲区字节,然后失去同步。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何解决 .NET 套接字和 TCP 可能出现的丢包问题? 的相关文章

  • 我的 .NET 库列表中缺少 System.Windows.Data

    您好 我想使用 System Windows Data IValueConverter 但是当我尝试在 VS2010 中添加对 System Windows Data 的引用时 我只看到 System Windows Forms 和 Sys
  • 标准化 C# 中的换行符

    我有一个数据流 可能包含 r n r n n r 或它们的任意组合 有没有一种简单的方法来规范化数据 使它们全部变成 r n 对 使显示更加一致 所以会产生这种转换表 r gt r n n gt r n n n gt r n r n n r
  • .NET 标准与 .NET 核心

    我已经了解了 NET Standard 和 NET Core 之间的区别 但我真的不知道区别是什么 或者何时选择 NET Standard 库项目以及何时选择 NET Core 库项目 我读到 NET Standard 是为了确保一组 AP
  • 如何将 Razor 视图转换为字符串?

    我想使用我的 Razor 视图作为某种发送电子邮件的模板 所以我想将我的模板 保存 在视图中 将其作为字符串读入控制器 进行一些必要的替换 然后发送它 我有有效的解决方案 我的模板作为 HTML 页面托管在某处 但我想将其放入我的应用程序中
  • 使用 XML 的 WCF 请求验证

    我有一个WCF使用 Net 4 0框架的SOAP Web服务 我在用contract first方法 即 服务代码是使用 WCSF Blue 工具从手写 WSDL 生成的 我对请求消息有以下要求 如果价格小于 100 则不得有税项 但如果大
  • 如何使用 .NET 以编程方式沙箱进程

    我计划设计一个系统 本质上允许用户在我的机器上运行 PHP Ruby 等脚本代码 我想将它们放入沙箱中 以防止它们访问机器的关键方面 哪些 NET API 可用于此目的 我计划从主流程创建一个子流程 并希望以编程方式从主流程中沙箱该子流程
  • 如何在VS2017中从.net项目引用netstandard项目?

    我有一个 netstandard2 0 项目 用于与第三方 Web 服务交互 我需要在同一解决方案中引用旧的 net 4 6 2 项目中的该项目 但是当我这样做时 我会收到一堆关于需要引用我定义的类型的错误 例如 我将调用 netstand
  • Inno Setup:验证是否已安装 .NET 4.0

    我有一个需要 NET 4 0 才能运行的组件 我的 Inno Setup 安装程序如何验证它是否已安装 如果没有 则提示用户安装它 The InitializeSetup运行 Inno Setup 可执行文件时调用该函数 为自定义脚本插入此
  • 在后台进程中访问 WPF FlowDocument

    在后台访问 WPF FlowDocument 我的问题涉及在 WPF 后台访问 UI 对象 我见过几十个示例应用程序 它们都很简单 易于理解 其中 95 告诉你如何显示进度条 这并不是我想要的 我的问题是这样的 我想通过访问 RichTex
  • 任务并行库周围是否有一个接口包装器,以便我可以将其交换用于单元测试?

    I asked 这个问题 https stackoverflow com questions 3362734 unit testing concurrent software what do you do不久以前 我现在知道这是一个坏主意
  • Python 无法使用套接字绑定我的外部/公共 IP 地址,给出错误但是当使用本地 IP 地址时,错误不会显示

    这是出现主要错误的代码 与我的本地 IP 的绑定将起作用 s bind 192 168 1 4 port 与我的公共 IP 的绑定失败并出现以下错误 s bind 99 99 99 99 port WinError 10049 请求的地址在
  • 剪贴板在 .NET 3.5 和 4 中的行为有所不同,但为什么呢?

    我们最近将一个非常大的项目从 NET Framework 3 5 升级到 4 最初一切似乎都工作正常 但现在复制粘贴操作开始出现错误 我已经成功制作了一个小型的可复制应用程序 它显示了 NET 3 5 和 4 中的不同行为 我还找到了一种解
  • 使用 JSON.net 反序列化

    我对 json JSON net 等都很陌生 在这里阅读类似的问题后 我无法让我的代码工作 我的错误到底是什么 我监督了什么 出于测试目的是否可以跳过 链接 和 元 类 或者我是否必须定义每个属性 我得到以下 REST 输出 codes h
  • 如何对私有方法进行单元测试?

    我正在构建一个类库 它将有一些公共和私有方法 我希望能够对私有方法进行单元测试 主要是在开发时 但它也可能对未来的重构有用 这样做的正确方法是什么 如果您想对私有方法进行单元测试 则可能会出现问题 单元测试 一般来说 旨在测试类的接口 即其
  • 为什么微软在 .net 3.5 SP1 之前就废弃了 JavaScriptSerializer,而在 .net 3.5 SP1 之后又重新启用了 JavaScriptSerializer?

    JavaScriptSerializer 在 net 3 5 SP1 之后并没有过时 我应该使用 JavaScriptSerializer 还是之前推荐的 DataContractJsonSerializer 还有为什么它被淘汰了 我很乐意
  • PhotoChooserTask 抛出未处理的异常

    我已经有了这段代码 我使用它来显示一个按钮 该按钮允许用户从他的库中选择图像并将其用作我的应用程序的背景 所以我创建了一个PhotoChooserTask 将其设置为显示相机并将其绑定到任务完成时必须执行的方法 该按钮将通过显示PhotoC
  • 如何对ArrayList(int)进行排序

    我怎样才能排序Arraylist按升序和降序排列 例子 ArrayList list new ArrayList list Add 2 list Add 8 list Add 0 list Add 1 如何按升序和降序对上面的列表进行排序
  • RegularExpressionValidator 限制输入长度而不限制字符类型

    我正在尝试使用RegularExpressionValidator验证一个TextBox输入长度 我可以使用它 但它只接受字母和数字 我想允许any个字符 唯一的检查是不超过 25 个字符
  • .NET 程序集差异/比较工具 - 有什么可用? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我希望能够在两个程序集之间进行代码级差异 Reflector 的 Diff 插件是迄今为止我发现的最接
  • SocketIO + Flask 检测断开连接

    我在这里有一个不同的问题 但意识到它可以简化为 如何检测客户端何时从页面断开连接 关闭其页面或单击链接 换句话说 套接字连接关闭 我想制作一个带有更新用户列表的聊天应用程序 并且我在 Python 上使用 Flask 当用户连接时 浏览器发

随机推荐