我有一个应用程序,它通过TCP连接从现场的GPRS客户端接收数据。 GPRS客户端设备不时丢失连接并缓冲数据,当连接恢复时,所有缓冲的数据都被发送到TCP连接,告诫我的应用程序抛出System.OutOfMemoryException。
我认为这是因为收到的数据大于我的缓冲区大小(设置为int.MaxValue)。
以下是用于监听和处理数据的代码
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);
}
}
}
答案 0 :(得分:4)
OutOfMemoryException
在您的情况下,这意味着connection.ReceiveBufferSize
太大而无法保存在一个内存中。不是因为收到的数据大于缓冲区大小。
您可以使用较小的固定缓冲区来获取接收到的字节,将其附加到某处并使用相同的缓冲区来接收其余数据,直到您拥有它为止。
需要注意的一件事是用于存储接收数据的集合。例如,你不能使用List<byte>
因为它将它的元素存储在引擎盖下的单个数组中,这与一次性完成所有操作没有区别 - 就像你现在一样。
您可以看到MemoryTributary,一个用于替换MemoryStream
的流实现。您可以将流复制到此流并将其保存为流。此页面还包含大量可帮助您了解OutOfMemoryException
的原因的信息。
此外,我写了一个缓冲管理器,以提供固定大小的缓冲区。您可以在代码审核中找到here。
为每个连接创建一个线程残酷。它们每个花费1 MB,因此您应该使用ThreadPool
或更好的IOCP(通过Socket
类的异步方法)。
您可能希望了解有关套接字编程的常见缺陷和最佳实践:
答案 1 :(得分:2)
使用固定的接收缓冲区,并逐步解析数据