Note that there are some explanatory texts on larger screens.

plurals
  1. POHow do you close TCP connections gracefully without exceptions?
    text
    copied!<p>What is the pattern and/or how would you change the following code so that a connection can be closed gracefully from either server or client, without needing exception handling.</p> <ol> <li><p><strong>TcpClient vs. Socket:</strong> I'm not tied to using the TcpClient client class. I wrote the following code to try to demonstrate the simplest case possible. I had been using a Socket &amp; SocketAsyncEventArgs but things were getting too complicated dealing with this issue. </p></li> <li><p><strong>Blocking vs. Asynchronous:</strong> Because of the blocking calls this may be difficult or impossible. If so, that's fine, but how do you solve it in the asynchronous case?</p></li> <li><p><strong>Quit Token?:</strong> I've experimented with sending some kind of "Quit" token to other side so that it knows to shutdown, but haven't gotten it working and I wanted to present a minimal example here. </p></li> <li><p><strong>Exception Handling Needed Anyways:</strong> I know exception handling will be necessary in the <em>real</em> application as network connections etc will fail. But can not the <strong>expected</strong> case of a graceful shutdown be handled without exceptions??</p></li> </ol> <p><strong>Edit:</strong> I moved original and answer code to <a href="https://gist.github.com/958955" rel="nofollow noreferrer">gist</a>. </p> <p><strong>Original <em>failing</em> Example</strong>: Moved to here: <a href="https://gist.github.com/958955#file_original.cs" rel="nofollow noreferrer">https://gist.github.com/958955#file_original.cs</a></p> <p><strong>Current Working Answer</strong>: <a href="https://gist.github.com/958955#file_answer.cs" rel="nofollow noreferrer">https://gist.github.com/958955#file_answer.cs</a></p> <pre><code>class Channel { private readonly TcpClient client; private readonly NetworkStream stream; private readonly string name; private readonly ManualResetEvent quit = new ManualResetEvent(false); public Channel(string name, TcpClient client) { this.name = name; this.client = client; stream = client.GetStream(); } public void Run() { Console.WriteLine(name + ": connected"); byte[] buffer = new byte[client.Client.ReceiveBufferSize]; stream.BeginRead(buffer, 0, buffer.Length, this.Read, buffer); var writer = new StreamWriter(stream, Encoding.ASCII); while (true) { var line = Console.ReadLine(); if (String.IsNullOrEmpty(line) || !this.client.Connected) break; writer.WriteLine(line); writer.Flush(); } if (client.Connected) { Console.WriteLine(name + " shutting down send."); client.Client.Shutdown(SocketShutdown.Send); Console.WriteLine(name + " waiting for read to quit."); quit.WaitOne(); } else { Console.WriteLine(name + " socket already closed"); } Console.WriteLine(name + " quit, press key to exit."); Console.ReadKey(); } private void Read(IAsyncResult result) { var bytesRead = this.stream.EndRead(result); if (bytesRead == 0) { Console.WriteLine(name + " read stopped, closing socket."); this.client.Close(); this.quit.Set(); return; } var buffer = (byte[])result.AsyncState; Console.WriteLine(name + " recieved:" + Encoding.ASCII.GetString((byte[])result.AsyncState, 0, bytesRead)); stream.BeginRead(buffer, 0, buffer.Length, this.Read, buffer); } } </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload