I ont problème avec OutOfMemoryException intermittent, sur la ligneTcpClient lire OutOfMemoryException
buffer = new byte [metaDataSize];
(Sous // Lire les méta-données de la commande.)
Est-ce que cela veut dire que j'essaie de lire le message complet alors qu'une partie seulement de celui-ci a été reçue? Dans le cas, quelle est une manière fiable de gérer cela? Btw, j'ai besoin de messages de longueur variable, car la plupart sont très courts, alors que les messages occasionnels sont très volumineux. Dois-je joindre la taille complète du message devant le message? Pourtant, comment puis-je savoir combien le flux contient avant d'essayer de lire? (Comme il semble que la lecture échoue parfois lorsque vous essayez de lire une certaine longueur, comme je le fais actuellement)
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;
}
}
méthode 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:
J'ai mis dans certains WriteLine, et a constaté que certains paquets envoyés sont reconnus avec la mauvaise taille du côté du récepteur. Ainsi, le métaDataSize qui devrait être 9544 pour un certain message, est lu comme 5439488, ou une valeur incorrecte similaire. Je supposons dans quelques occasions ce nombre est si grand qu'il provoque l'exception OutOfMemoryException.
Semble la réponse de Douglas peut être sur la marque (?), Je vais tester. Pour info: Le programme serveur (expéditeur) est construit comme "Any CPU", fonctionnait sous Windows 7 x64 pc. Alors que le client (récepteur) est construit comme x86, et (pendant ce test) a fonctionné sur XP. Mais doit également être codé pour fonctionner sur d'autres fenêtres x86 ou x64.
cela signifie simplement que votre tas est plein ... – Yahia
Utilisez-vous ceci pour communiquer avec un ordinateur potentiellement malveillant? AFAIK 'BinaryFormatter' ne devrait être utilisé que sur des données de confiance. – CodesInChaos
Je code à la fois le client et l'application-serveur, et je ne lance des tests que sur mes propres ordinateurs. Mais certainement en production, tout ordinateur inconnu pourrait tenter de se connecter car le port est ouvert. – bretddog