【问题标题】:Out of memory exception when receiving large amounts of data on TCP connection在 TCP 连接上接收大量数据时出现内存不足异常
【发布时间】:2012-10-23 19:08:02
【问题描述】:

我有一个应用程序通过 TCP 连接从现场的 GPRS 客户端接收数据。 GPRS 客户端设备有时会丢失连接并缓冲数据,当连接恢复时,所有缓冲的数据都会发送到 TCP 连接,导致我的应用程序抛出 System.OutOfMemoryException。

我认为这是因为接收到的数据大于我的缓冲区大小(设置为 int.MaxValue)。

  1. 如何防止我的应用程序内存不足?
  2. 如何确保不会丢失任何传入数据?

以下是用于侦听和处理传入数据的代码

public void Listen(string ip, int port)
{
    _logger.Debug("All.Tms.SensorDataServer : SensorDataListener : Listen");

    try
    {
        var listener = new TcpListener(IPAddress.Parse(ip), port);
        listener.Start();

        while (true)
        {
            var client = listener.AcceptTcpClient();
            client.SendBufferSize = int.MaxValue;

            var thread = new Thread(() => ReadAndHandleIncommingData(client));
            thread.IsBackground = true;
            thread.Start();
        }
    }
    catch (Exception ex)
    {
        _logger.Error("TMS.Sensor.SensorDataServer : SensorDataListener : Error : ", ex);
    }  
}

private void ReadAndHandleIncommingData(TcpClient connection)
{
     try
     {
         var stream = connection.GetStream();
         var data = new byte[connection.ReceiveBufferSize];

         var bytesRead = stream.Read(data, 0, System.Convert.ToInt32(connection.ReceiveBufferSize));

         var sensorDataMapper = new SensorDataMapperProvider().Get(data);

         if (sensorDataMapper != null)
         {
             _sensorDataHandler.Handle(sensorDataMapper.Map(data));
         }
    }
    catch (Exception ex)
    {
        _logger.Error("TMS.Sensor.SensorDataServer : SensorDataListener : ReadAndHandleIncommingData : Error : ", ex);
    }
    finally
    {
        try
        {
            connection.Close();
        }
        catch(Exception ex)
        {
            _logger.Error("All.Tms.SensorDataServer : SensorDataListener : ReadAndHandleIncommingData : Error : ", ex);
        }
    }
}

【问题讨论】:

    标签: c# networking tcp stream


    【解决方案1】:

    关于缓冲区

    OutOfMemoryExceptions 在没有剩余顺序内存时被抛出。
    在您的情况下,这意味着 connection.ReceiveBufferSize 太大而无法保存在一块内存中。不是因为收到的数据大于你的缓冲区大小

    您可以使用较小的固定缓冲区来获取接收到的字节,将其附加到某处并使用相同的缓冲区来接收其余数据,直到获得所有数据为止。

    需要注意的一点是您用来存储接收到的数据的集合。例如,您不能使用List<byte>,因为它将其元素存储在引擎盖下的单个数组中,这与一次性获取所有内容没有区别 - 就像您现在所做的那样。

    您可以看到MemoryTributary,这是一个旨在替换MemoryStream 的流实现。您可以将您的流复制到此并将其保留为流。此页面还包含很多信息,可以帮助您了解OutOfMemoryExceptions 的原因。

    另外,不久前我写了一个缓冲区管理器来提供固定大小的缓冲区。你可以在 Code Review 中找到,here

    关于线程

    每一个连接创建一个线程是残酷的。它们每个花费 1 MB,因此您应该使用 ThreadPool 或更好的 IOCP(通过 Socket 类的异步方法)。

    您可能想查看这些关于套接字编程的常见陷阱和最佳实践:

    【讨论】:

      【解决方案2】:

      使用固定的接收缓冲区,增量解析数据

      【讨论】:

      • 使用 System.Net.Sockets.SocketAsyncEventArgs 使用异步套接字
      • 是的,对于每个线程,您需要 1 MB 的堆栈空间。你有多少并发连接?
      • 谢谢,我会考虑所有的建议。 1+
      猜你喜欢
      • 2013-10-09
      • 2023-04-10
      • 1970-01-01
      • 1970-01-01
      • 2016-09-02
      • 1970-01-01
      • 2018-08-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多