simple TcpClient echo client/server example
I was trying to help a beginner dev that (like many early on) wanted to tackle an irc bot as their first project. They had apparently googled for C# irc and came across this code. Now, having spent a couple of my college years running my own irc server on EFNet, I'm not really inclined to post another irc bot by any means, but I would like to post a tiny and intentionally naïve TcpClient example that improves on the linked code on a couple of issues, specifically:
- Dispose of your IDisposable types! Brad and Joe already know how I feel about that, of course :) (Hint: Close() was there so they called it - the API didn't guide them forcefully enough to use Dispose() instead, but that's IMHO of course)
- Never flush manually if the API will do it for you. You're sure to miss a Flush call that was needed.
static void Main(string[] args) {
Thread serverThread = new Thread((ThreadStart)delegate
{
TcpListener listener = new TcpListener(IPAddress.Loopback, 1234);
listener.Start();
using (TcpClient client = listener.AcceptTcpClient())
using (NetworkStream stream = client.GetStream())
using (StreamReader reader = new StreamReader(stream))
using (StreamWriter writer = new StreamWriter(stream))
{
writer.AutoFlush = true;
string inputLine;
while ((inputLine = reader.ReadLine()) != null)
{
writer.WriteLine("Echoing string: " + inputLine);
}
Console.WriteLine("Server saw disconnect from client");
}
});
serverThread.Start();
string[] linesToSend = new string[] { "foo", "bar", "ack" };
using (TcpClient client = new TcpClient("127.0.0.1", 1234))
using (NetworkStream stream = client.GetStream())
using (StreamReader reader = new StreamReader(stream))
using (StreamWriter writer = new StreamWriter(stream))
{
writer.AutoFlush = true;
foreach (string lineToSend in linesToSend)
{
Console.WriteLine("Sending to server: {0}", lineToSend);
writer.WriteLine(lineToSend);
string lineWeRead = reader.ReadLine();
Console.WriteLine("Received from server: {0}", lineWeRead);
Thread.Sleep(2000); // just for effect
}
Console.WriteLine("Client is disconnecting from server");
}
// wait for the server to exit before Main
// this way, we make sure to keep Console
// around since the server writes to it
serverThread.Join();
}
What do you think about stacking using blocks like this? Should each have its own brackets, putting the current body 3 more levels deep? Is this formatting obvious or does the sacrifice of additional whitespace put more burden on the person reading the code?
I like to take advantage of my disposable types that need to live (approximately) the same lifetimes in this manner, but it’s surprisingly (to me) controversial as it’s a departure from the conventional “always add the brackets” stance. However, that stance is more about not trying to save vertical whitespace (which in those if/for/while loops, I tend to like the additional vertical whitespace the brackets bring) but instead saving horizontal whitespace.
I tend to rationalize this kind of departure as working around a weakness in the using syntax – had these 4 instances all been of the same type, I could have combined them into a single using statement that would have clearly been one level of indentation instead of 4, so it doesn’t seem fair that them being 4 different types should result in a punishment of a 12x8 block of additional whitespace.
Also, I’m cheating in that the server could fail to bind the port (listener.Start()) fast enough for it to be ready for the client’s attempted connect.