У меня проблема с прерывистым OutOfMemoryException , на линии
buffer = новый байт [metaDataSize];
(Под // Считать метаданные команды.)
Означает ли это, что я пытаюсь прочитать полное сообщение, пока получена только его часть? В случае, если надежный способ справиться с этим? Кстати, мне нужны сообщения переменной длины, так как большинство из них очень короткие, а случайные сообщения очень большие. Должен ли я прикрепить полный размер сообщения перед сообщением? Тем не менее, как я могу узнать, сколько содержится в потоке, прежде чем пытаться читать с него? (кажется, что чтение иногда завершается неудачно при попытке прочитать определенную длину, как я в настоящее время делаю)
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
EDIT:
Я вставил несколько WriteLine и обнаружил, что некоторые отправленные пакеты распознаются с неправильным размером на стороне получателя. Таким образом, metaDataSize, который должен быть 9544 для определенного сообщения, читается как 5439488, или аналогичное неправильное значение. Я предполагаю в некоторых случаях это число настолько велико, что вызывает исключение OutOfMemoryException.
Кажется, ответ Дугласа может быть на отметке (?), Я проверю. Для информации: Программа сервера (отправителя) построена как «Любой ЦП», работает на Windows 7 x64 ПК. Пока Клиент (получатель) построен как x86, и (во время этого теста) работал на XP. Но также должен быть закодирован для работы на других Windows x86 или x64.