Ho avuto difficoltà con questo e non riesco a trovare una ragione per cui il mio codice non riesca a leggere correttamente da un server TCP che ho anche scritto. Sto usando la classe TcpClient
e il suo metodo GetStream()
ma qualcosa non funziona come previsto. O i blocchi di operazione indefinitamente (l'ultima operazione di lettura non ha scaduto come previsto), oi dati vengono ritagliati (per qualche ragione un'operazione di lettura restituisce 0 ed esce dal ciclo, forse il server non risponde abbastanza velocemente). Si tratta di tre tentativi di attuazione di questa funzione:Qual è il modo corretto di leggere da NetworkStream in .NET
// this will break from the loop without getting the entire 4804 bytes from the server
string SendCmd(string cmd, string ip, int port)
{
var client = new TcpClient(ip, port);
var data = Encoding.GetEncoding(1252).GetBytes(cmd);
var stm = client.GetStream();
stm.Write(data, 0, data.Length);
byte[] resp = new byte[2048];
var memStream = new MemoryStream();
int bytes = stm.Read(resp, 0, resp.Length);
while (bytes > 0)
{
memStream.Write(resp, 0, bytes);
bytes = 0;
if (stm.DataAvailable)
bytes = stm.Read(resp, 0, resp.Length);
}
return Encoding.GetEncoding(1252).GetString(memStream.ToArray());
}
// this will block forever. It reads everything but freezes when data is exhausted
string SendCmd(string cmd, string ip, int port)
{
var client = new TcpClient(ip, port);
var data = Encoding.GetEncoding(1252).GetBytes(cmd);
var stm = client.GetStream();
stm.Write(data, 0, data.Length);
byte[] resp = new byte[2048];
var memStream = new MemoryStream();
int bytes = stm.Read(resp, 0, resp.Length);
while (bytes > 0)
{
memStream.Write(resp, 0, bytes);
bytes = stm.Read(resp, 0, resp.Length);
}
return Encoding.GetEncoding(1252).GetString(memStream.ToArray());
}
// inserting a sleep inside the loop will make everything work perfectly
string SendCmd(string cmd, string ip, int port)
{
var client = new TcpClient(ip, port);
var data = Encoding.GetEncoding(1252).GetBytes(cmd);
var stm = client.GetStream();
stm.Write(data, 0, data.Length);
byte[] resp = new byte[2048];
var memStream = new MemoryStream();
int bytes = stm.Read(resp, 0, resp.Length);
while (bytes > 0)
{
memStream.Write(resp, 0, bytes);
Thread.Sleep(20);
bytes = 0;
if (stm.DataAvailable)
bytes = stm.Read(resp, 0, resp.Length);
}
return Encoding.GetEncoding(1252).GetString(memStream.ToArray());
}
L'ultimo "funziona", ma sembra certamente brutto a mettere un sonno hard-coded all'interno del ciclo si considera che le prese già supportano leggere timeout! Devo impostare alcune proprietà (i) sullo TcpClient
del NetworkStream
? Il problema risiede nel server? Il server non chiude le connessioni, è compito del client farlo. Quanto sopra è anche in esecuzione all'interno del contesto del thread UI (programma di test), forse ha qualcosa a che fare con questo ...
Qualcuno sa come utilizzare correttamente NetworkStream.Read
per leggere i dati fino a quando non sono disponibili più dati? Immagino che ciò che desidero sia qualcosa di simile alle vecchie proprietà di timeout Win32 di Win32 ... ReadTimeout
, ecc. Prova a leggere finché non viene raggiunto il timeout, quindi restituisce 0 ... Ma a volte sembra che restituisca 0 quando i dati dovrebbe essere disponibile (o sulla strada .. può leggere return 0 se è disponibile?) e quindi blocca indefinitamente sull'ultima lettura quando i dati non sono disponibili ...
Sì, sono in perdita!
Attenzione. Il codice nel tentativo (e le risposte) non chiude il client o il flusso, il che causa una perdita di risorse con grandi conseguenze se richiesto più volte. Probabilmente si dovrebbe fare 'utilizzando (var client = new System.Net.Sockets.TcpClient (ip, port)) utilizzando (var STM = client.GetStream())' quindi racchiudere il resto del metodo tra parentesi graffe. Ciò garantirà che all'uscita del metodo, indipendentemente dal motivo, le connessioni siano chiuse, le risorse vengano recuperate. –
Hai visto il codice per la classe TcpClient? Ti consiglio di dargli un'occhiata ...Se si desidera ancora rispondere all'endpoint, non chiudere il flusso né il tcpclient (che chiude lo stream, se non si sbaglia). Ma il codice che sto usando è diverso, lo scaverò e aggiornerò anche qui le risposte. – Loudenvier
grazie per il suggerimento. Ho trovato [TCPClient.cs] (http://referencesource.microsoft.com/#system/net/System/Net/Sockets/TCPClient.cs). Infatti la chiusura o lo smaltimento di 'TCPClient' elimina lo stream. In realtà ho visto guasti di connessione quando un programma creava migliaia di connessioni senza troppa cura (per altri, cattivi motivi). Perché dovremmo allontanarci dal solito schema identificabile qui? L'implementazione di IDisposable è soggetta a errori, 'using()' è facile e sicura. –