TCP Chat
Working with those HTTP helper classes might cover a lot of the bases, but I don't think that's what everyone wants to use to communicate. When you want to talk to someone over the internet, make sure the message gets there, and in order, then it's the job for TCP based sockets.
Keep in mind that this is not a tutorial on how TCP works, just how to use C#'s TCP APIs. If you are looking for one of those, this looks like a good place to start. I'd also recommend watching a few YouTube videos. You don't need to know the exact details of how TCP works, but it's a good idea to learn what goes on under the hood.
To start out simply, we are going to make a very simple chat program. I don't want to do your standard "echo server," like many other tutorials do. If you want one of those you can find one here. This is a much more complex example of using TCP communications, but I think it should be easy to grasp.
There are two main ways of using TCP communications in C#:
- Using a raw
Socket
object - Using the
TcpClient
andTcpListener
classes
The former looks pretty close to the Berkeley/POSIX socket API, whereas the latter is a handy wrapper around Socket
objects that are using TCP (go figure by the name). Since I want to start things off lightly we will be using TcpClient
and TcpListener
for our TCP Chat program. Don't worry, if you're interested in Socket
, we'll cover that later.
I also want to note that we will be using synchronous, single-threaded code for this project. In any real world application, you should be using async
& multi-threaded code when working with network operations. I want to keep things simple right now, and after this, I'll give you a multi-threaded example sometime later. The async
methods of TcpClient
and TcpListener
should be easy to use if you know how to do async
programming and know the non-async methods of these two classes.
Design
We're going to break the TCP Chat application into three chunks. A Server (to distribute messages), a Messenger (to post messages), a Viewer (to receive messages). If you're wondering why I'm breaking this into three parts, it's because I don't want to have to deal with any GUI code. Since each part will be its own executable, make sure to add three separate Projects to your Solution. We'll do the Server first, the Messenger second, and the Viewer last.
It drives me nuts when people write tutorials but make me use things that really aren't necessary to teach a concept. An example of this would be including GUI code in a tutorial that doesn't really need it; I've seen this way too many times in other C# tutorials.
Application Protocol
For those of you who know their OSI model, we're going to define our Application Layer protocol now. If you don't know what that is, it's how our application will transmit data and interpret it. Note that we have two different types of clients (Messenger and Viewer), so the server needs to treat them different. We will only use plain text (no binary data), and it will all be in UTF-8 encoding.
Starting with the Viewer client:
- After a Viewer establishes a connection with the Server, it will send the message:
viewer
. The server will then recognize that client as a Viewer. - The Server will just send plain text messages to every Viewer client. The Viewer should just display what it has been told.
The Messenger client:
- After a Messenger establishes a connection with the Server, it will send the message
name:MY_NAME
, where MY_NAME is the name the messenger wants to use (e.g.name:Kevin Bacon
). If the name is already taken by another Messenger client, the server should send the messagename taken
and disconnect the client. - Though if the Messenger was successful in connection, the Server should just then listen in for any messages that are sent, and then distribute them to the Viewers.
This really isn't the most feature rich protocol design (and not the best), but it's a nice simple example. If you need some help figuring out the flow of things, take a look at the diagram below.
A Note Before Starting
There is a lot more code here than my other examples, but there is also a lot of redundancy between the three files. Don't get discouraged. Take a break if you need to. The Server and the Viewer have "Ctrl-C," handlers FYI. There are many lines of code that have a comment next to them that tell you if a call blocks or not. Also, around most of the code there aren't any try-catch blocks like there should be in production code.
There is... one exception. We're not sending any messages to the clients or server to notify that one might be disconnecting. Instead we just close the TcpClient
and NetworkStream
on whichever side wants to end the connection. This still will transmit FIN & ACK TCP packets between both ends. We can use this code on a local end to check if those packets have been sent.
This code is found in all three files, so once you understand it, feel free to copy & paste it.
In the Main functions for each file, I left commented out some command line argument parsing. It's not very complex (or the proper thing to do in production code), but it's there if you want to try to run the application without hard set values. Just make sure you get the arguments write when entering them or you'll get some errors.