TCP Chat - Server
I've seen many networking tutorials that start you with the client code. I think it's better to start off with the server code first. And there is quite a bit here.
The main two classes that are used here are
TcpListener. In a nutshell, we use
TcpListener to listen for incoming connections, then once someone has connected, we spin up a new
TcpClient to talk to that remote process.
Here is the code for the server:
When instancing the class
TcpChatServer, the listener is created but doesn't start listening until we call
Start() on it in the
IPAddress.Any, means "let anyone from any network interface connect to me."
Run() method is the main heart of the program. Like said above, when we make the call to
TcpListener will start listening for new incoming TCP connections the port we fed it (6000 our case). If there is a new pending connection (checked via the
Pending() method), we break into a function see who the new client is.
_handleNewConnection() does what the name implies. After the connection has been established we can grab it's
TcpClient instance by calling
AcceptTcpClient in the listener, the client should send a message stating either
- If we have a new Viewer, recognize that client as a viewer, and then send them a welcome message
- If we have a new Messenger, verify that
MY_NAMEhasn't been taken yet
- If so, enqueue a new message to tell the Viewers that a new friend has joined us
- Else, close the connection
- Anything else, say that we couldn't identify who they are, and then close the connection
Run(), we check for disconnects, new messages, and then send queued ones in the rest of the loop. There is also a call to
Thread.Sleep(10), so that we save on CPU resources.
_checkForDisconnects() are two
foreach loops that go through all of the clients and use the
_isDisconnected() method to test for the FIN/ACK packets that might have been sent.
Connected property of
TcpClient might seem like the right thing to use here instead of
_isDisconnected(), but there's a problem with that.
true only if the last IO operation was a success. This means that the FIN/ACK might have been sent, but since there were no IO operations on our end,
Connected will still return
true. Be careful of this.
_checkForNewMessages() is also a
foreach check of all the Messengers. We can use the
Available property to see how big their message is (in bytes), and then read that from the client's
_sendMessages() empties the
_messageQueue by writing the the Viewer's streams.
There is also the function
_cleanupClient(). It's a small helper that closes both the
NetworkStream and itself.