【发布时间】:2012-01-21 15:22:13
【问题描述】:
我遇到间歇性 OutOfMemoryException 的问题,在线
buffer = 新字节[metaDataSize];
(在 //Read the command's Meta data 下)
这是否意味着我在只收到部分信息时尝试阅读完整信息?万一,有什么可靠的方法来处理呢?顺便说一句,我需要可变长度的消息,因为大多数消息都很短,而偶尔的消息非常大。我应该在邮件前面附加完整的邮件大小吗?不过,在尝试从中读取之前,我怎么知道流包含多少? (因为在尝试读取特定长度时,读取有时会失败,就像我目前所做的那样)
public static Command Read(NetworkStream ns)
{
try
{
//Read the command's Type.
byte[] buffer = new byte[4];
int readBytes = ns.Read(buffer, 0, 4);
if (readBytes == 0)
return null;
CommandType cmdType = (CommandType)(BitConverter.ToInt32(buffer, 0));
//Read cmdID
buffer = new byte[4];
readBytes = ns.Read(buffer, 0, 4);
if (readBytes == 0)
return null;
int cmdID = BitConverter.ToInt32(buffer, 0);
//Read MetaDataType
buffer = new byte[4];
readBytes = ns.Read(buffer, 0, 4);
if (readBytes == 0)
return null;
var metaType = (MetaTypeEnum)(BitConverter.ToInt32(buffer, 0));
//Read the command's MetaData size.
buffer = new byte[4];
readBytes = ns.Read(buffer, 0, 4);
if (readBytes == 0)
return null;
int metaDataSize = BitConverter.ToInt32(buffer, 0);
//Read the command's Meta data.
object cmdMetaData = null;
if (metaDataSize > 0)
{
buffer = new byte[metaDataSize];
int read = 0, offset = 0, toRead = metaDataSize;
//While
while (toRead > 0 && (read = ns.Read(buffer, offset, toRead)) > 0)
{
toRead -= read;
offset += read;
}
if (toRead > 0) throw new EndOfStreamException();
// readBytes = ns.Read(buffer, 0, metaDataSize);
//if (readBytes == 0)
// return null;
// readBytes should be metaDataSize, should we check?
BinaryFormatter bf = new BinaryFormatter();
MemoryStream ms = new MemoryStream(buffer);
ms.Position = 0;
cmdMetaData = bf.Deserialize(ms);
ms.Close();
}
//Build and return Command
Command cmd = new Command(cmdType, cmdID, metaType, cmdMetaData);
return cmd;
}
catch (Exception)
{
throw;
}
}
WRITE 方法:
public static void Write(NetworkStream ns, Command cmd)
{
try
{
if (!ns.CanWrite)
return;
//Type [4]
// Type is an enum, of fixed 4 byte length. So we can just write it.
byte[] buffer = new byte[4];
buffer = BitConverter.GetBytes((int)cmd.CommandType);
ns.Write(buffer, 0, 4);
ns.Flush();
// Write CmdID, fixed length [4]
buffer = new byte[4]; // using same buffer
buffer = BitConverter.GetBytes(cmd.CmdID);
ns.Write(buffer, 0, 4);
ns.Flush();
//MetaDataType [4]
buffer = new byte[4];
buffer = BitConverter.GetBytes((int)cmd.MetaDataType);
ns.Write(buffer, 0, 4);
ns.Flush();
//MetaData (object) [4,len]
if (cmd.MetaData != null)
{
BinaryFormatter bf = new BinaryFormatter();
MemoryStream ms = new MemoryStream();
bf.Serialize(ms, cmd.MetaData);
ms.Seek(0, SeekOrigin.Begin);
byte[] metaBuffer = ms.ToArray();
ms.Close();
buffer = new byte[4];
buffer = BitConverter.GetBytes(metaBuffer.Length);
ns.Write(buffer, 0, 4);
ns.Flush();
ns.Write(metaBuffer, 0, metaBuffer.Length);
ns.Flush();
if (cmd.MetaDataType != MetaTypeEnum.s_Tick)
Console.WriteLine(cmd.MetaDataType.ToString() + " Meta: " + metaBuffer.Length);
}
else
{
//Write 0 length MetaDataSize
buffer = new byte[4];
buffer = BitConverter.GetBytes(0);
ns.Write(buffer, 0, 4);
ns.Flush();
}
}
catch (Exception)
{
throw;
}
}
VB.NET:
Private tcp As New TcpClient
Private messenger As InMessenger
Private ns As NetworkStream
Public Sub New(ByVal messenger As InMessenger)
Me.messenger = messenger
End Sub
Public Sub Connect(ByVal ip As String, ByVal port As Integer)
Try
tcp = New TcpClient
Debug.Print("Connecting to " & ip & " " & port)
'Connect with a 5sec timeout
Dim res = tcp.BeginConnect(ip, port, Nothing, Nothing)
Dim success = res.AsyncWaitHandle.WaitOne(5000, True)
If Not success Then
tcp.Close()
Else
If tcp.Connected Then
ns = New NetworkStream(tcp.Client)
Dim bw As New System.ComponentModel.BackgroundWorker
AddHandler bw.DoWork, AddressOf DoRead
bw.RunWorkerAsync()
End If
End If
Catch ex As Exception
Trac.Exception("Connection Attempt Exception", ex.ToString)
CloseConnection()
End Try
End Sub
Private Sub DoRead()
Try
While Me.tcp.Connected
' read continuously :
Dim cmd = CommandCoder.Read(ns)
If cmd IsNot Nothing Then
HandleCommand(cmd)
Else
Trac.TraceError("Socket.DoRead", "cmd is Nothing")
CloseConnection()
Exit While
End If
If tcp.Client Is Nothing Then
Trac.TraceError("Socket.DoRead", "tcp.client = nothing")
Exit While
End If
End While
Catch ex As Exception
Trac.Exception("Socket.DoRead Exception", ex.ToString())
CloseConnection()
EventBus.RaiseErrorDisconnect()
End Try
End Sub
编辑:
我放入了一些WriteLine,发现有些发送的包在接收端被识别为错误的大小。因此,对于某个消息,应该为 9544 的 metaDataSize 被读取为 5439488,或类似的错误值。我假设在少数情况下,这个数字太大会导致 OutOfMemoryException。
看来道格拉斯的答案可能是对的(?),我会测试。有关信息:服务器(发送方)程序构建为“任何 CPU”,在 Windows 7 x64 pc 上运行。虽然客户端(接收器)构建为 x86,并且(在此测试期间)在 XP 上运行。但也必须编码才能在其他 Windows x86 或 x64 上工作。
【问题讨论】:
-
这只是意味着你的堆已满......
-
您是否正在使用它与潜在的恶意计算机进行通信? AFAIK
BinaryFormatter只能用于受信任的数据。 -
我对客户端和服务器应用程序都进行了编码,并且仅在我自己的计算机上运行测试。但在生产中,任何未知计算机都可以在端口打开时尝试连接。
-
我会为您的代码与流中的字节乱序付出代价。某个地方的一个错误。首先读取所有字节,然后转换它们,从而更容易诊断。
-
@Hans,异常发生大约 10 次中的 1 次(按启动顺序),并且在从与服务器应用程序相同的计算机运行时无法复制。 (仍然通过公共 IP 地址连接)。不确定这是否与您的假设一致..?将添加更多诊断代码,看看我能找到什么..
标签: c# .net out-of-memory tcpclient