2009-02-01 8 views
7

Ho fatto molte ricerche sul modo migliore di scrivere codice di rete "corretto" in C#.Programmazione della rete C# e utilizzo delle risorse

Ho visto un certo numero di esempi usando l'istruzione "using" di C#, e penso che questo sia un buon approccio, tuttavia ho visto l'uso incoerente di esso con varie espressioni.

Per esempio, supponiamo di avere un po 'di codice come questo:

TcpClient tcpClient = new TcpClient("url.com", 80); 
NetworkStream tcpStream = tcpClient.GetStream(); 
StreamReader tcpReader = new StreamReader(tcpStream); 
StreamWriter tcpWriter = new StreamWriter(tcpStream); 

Ovviamente, questo codice sta per essere molto traballante. Quindi, ho visto del codice che mette l'uso sul tcpClient, che sembra buono. Tuttavia, NetworkStream non dispone anche di risorse che devono essere pulite? Che mi dici di StreamReader/Writer?

Devo eseguire il wrap di tutte e 4 le istruzioni in istruzioni nidificate?

E se sì, cosa succede quando è giunto il momento di disporre? StreamWriter non chiuderà il flusso e di conseguenza il socket? Allora cosa succede quando StreamReader, poi NetworkStream, poi TcpClient passano attraverso le loro cessioni?

Che fa sorgere un'altra domanda. Con StreamReader e StreamWriter composti dallo stesso flusso, chi lo possiede? Non pensano entrambi di possederlo, e quindi tenteranno entrambi di distruggerlo? O il framework sa che il flusso è già stato distrutto e silenziosamente lo ignora?

Sembra quasi che l'istruzione using sia necessaria solo per l'ultima istruzione nella catena, ma cosa succede se viene generata un'eccezione in GetStream()? Quindi non penso che pulire correttamente il socket, quindi sembra che gli usi ridondanti siano necessari per garantire che ciò non avvenga.

Qualcuno sa di buoni libri recenti sulla programmazione di rete con .net e C#, che include in modo prefissato i capitoli sulla gestione delle eccezioni e la gestione delle risorse? O forse qualche buon articolo online? Tutti i libri che riesco a trovare provengono dall'era .NET 1.1 (Programmazione di rete per Microsoft .NET Framework, Network Programming in.NET, ecc.), Quindi questo sembra un argomento che richiede delle buone risorse.

EDIT:

Per favore, non lasciate che molto buon commento di Marc fermare chiunque altro dal commentare questo :)

Mi piacerebbe sentire qualcuno elses suggerimenti su libri o opinioni sulla gestione delle risorse, in particolare per quanto riguarda l'uso asincrono.

risposta

14

In genere, gli oggetti devono gestire internamente più chiamate Dispose() e eseguire solo il codice principale una volta; quindi, un flusso che riceve più volte Dispose() d non è un problema. Personalmente, vorrei usare un sacco di using lì; si noti che non è necessario per il rientro/nido, anche se (a meno che i diversi livelli hanno differenti di vita volte):

using(TcpClient tcpClient = new TcpClient("url.com", 80)) 
using(NetworkStream tcpStream = tcpClient.GetStream()) 
using(StreamReader tcpReader = new StreamReader(tcpStream)) 
using(StreamWriter tcpWriter = new StreamWriter(tcpStream)) 
{ 
    ... 
} 

Come dici tu, questo garantisce che se si verifica un errore durante l'inizializzazione, tutto è ancora ripulito correttamente. Ciò garantisce anche che ogni livello abbia la possibilità (nel giusto ordine) di trattare correttamente qualsiasi dato bufferizzato, ecc.

Re ownership; NetworkStream è in realtà una stranezza in primo luogo ... la maggior parte dei flussi sono o input output xor.NetworkStream piega alcune regole e inserisce due direzioni in un'unica API; quindi questa è un'eccezione ... normalmente la proprietà sarebbe più chiara. Inoltre, molti wrapper hanno un flag per determinare se devono chiudere il flusso avvolto. no, ma alcuni lo fanno (come ad esempio GZipStream, che ha un'opzione ctor leaveOpen). Se non si desidera trasferire la proprietà, questa è un'opzione - o utilizzare un intermediario del flusso non di chiusura - uno è here (NonClosingStream o simile).

Re libri; Ho preso una copia di "Socket TCP/IP in C#: Guida pratica per programmatori" (here) - adeguata, ma non eccezionale.

+0

Grazie per il feedback. Questo è in gran parte ciò che sospettavo. Sono d'accordo, NetworkStream è una stranezza. Non sono sicuro che sarebbe stato meno problematico creare flussi di lettura e scrittura separati, ma è quello che è. Grazie per la raccomandazione del libro. –

+0

Inoltre, non ho visto un'implementazione NonClosingStream nel sito che hai menzionato. –

+0

È lì - MiscUtil.IO.NonClosingStreamWrapper –

0

Se un oggetto supporta IDisposable, è meglio inserirlo in un blocco {} perché il metodo di eliminazione viene chiamato automaticamente per te. Questo rende anche meno codice da parte tua. È importante notare che l'utilizzo di un 'uso' non gestisce eccezioni. Devi ancora farlo se vuoi gestire eventuali errori. Una volta che il blocco utilizza non rientra nell'ambito, anche il tuo oggetto.

Old Style Code 

object obj; 

try 
{ 
    obj= new object(); 
    //Do something with the object 
} 
catch 
{ 
    //Handle Exception 
} 
finally 
{ 

    if (obj != null) 
    { 
    obj.Dispose(); 
    } 
} 

Newer Style Code 

try 
{ 
    using (object obj = new object()) 
    { 
    //Do something with the object 
    } 
catch 
{ 
    //Handle Exception 
} 
-1

E le prese? E 'ok per fare:

serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 
serverSocket.Connect(serverEndPoint, m_NegotiationPort); 
. 
. 
. 
serverSocket.Close(); 

o meglio

using (Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) 
{ 
. 
. 
. 
} 
Problemi correlati