1

我有一个应用程序通过 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);
        }
    }
}
4

2 回答 2

4

关于缓冲区

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

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

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

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

另外,不久前我写了一个缓冲区管理器来提供固定大小的缓冲区。您可以在此处的代码审查中找到它。

关于线程

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

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

于 2012-11-03T09:38:16.013 回答
2

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

于 2012-11-03T09:02:02.960 回答