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.
TcpClient vs. Socket: 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 & SocketAsyncEventArgs but things were getting too complicated dealing with this issue.
Blocking vs. Asynchronous: 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?
Quit Token?: 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.
Exception Handling Needed Anyways: I know exception handling will be necessary in the real application as network connections etc will fail. But can not the expected case of a graceful shutdown be handled without exceptions??
Edit: Please note the following code will throw an exception when either side closes the connection. I am looking for a (likely application level) pattern to avoid throwing an exception entirely if either side "quits".
Channel
using System;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading;
public class Channel
{
private readonly TcpClient client;
private readonly NetworkStream stream;
private readonly TextReader reader;
private readonly TextWriter writer;
private readonly string name;
public Channel(string name, TcpClient client)
{
SocketAsyncEventArgs
this.name = name;
this.client = client;
stream = client.GetStream();
stream.ReadTimeout = 1000;
reader = new StreamReader(stream, Encoding.ASCII);
writer = new StreamWriter(stream, Encoding.ASCII);
}
public void Run()
{
Console.WriteLine(name + ": connected");
Thread readThread = new Thread(Read);
readThread.Start();
while (true)
{
var line = Console.ReadLine();
if (String.IsNullOrEmpty(line)) break;
writer.WriteLine(line);
writer.Flush();
}
Console.WriteLine(name + " quitting");
stream.Flush();
client.Close();
}
private void Read()
{
while (true)
{
var line = reader.ReadLine();
Console.WriteLine(name + " recieved: " + line);
}
}
}
Server
class Server
{
static void Main(string[] args)
{
TcpListener listener = new TcpListener(IPAddress.Loopback, 5000);
listener.Start();
Console.WriteLine("server: listener started");
var channel = new Channel("server", listener.AcceptTcpClient());
channel.Run();
listener.Stop();
}
}
Client
class Client
{
static void Main(string[] args)
{
TcpClient client = new TcpClient(AddressFamily.InterNetwork);
Console.WriteLine("client: created, press key to connect");
Console.ReadKey();
client.Connect(IPAddress.Loopback, 5000);
var channel = new Channel("client", client);
channel.Run();
}
}