TCP Chat - Recap
And here we have the collection of programs running on many different Linux machines. The one on the left is my laptop, in the middle is a cloud instance I spun up, and on the right is this server hosting the Server portion. If you're wondering what my order of operations was:
- I start the server on 16bpp.net, port 6969
- I connect with the Viewer & Messenger on my laptop
- I connect with the Viewer & Messenger on the cloud VM
- I have the "two users," exchange some playful banter
- I exit out on my laptop
- I then kill the server on 16bpp.net
- The Viewer & Messenger on the cloud VM then detect the FIN/ACK packets and close
With all that has been said, done, and typed, there are still a few things to go over. This is not a good implementation of a internet chatting application. Keep in mind my goal is to teach you the C# # APIs of System.Net
. Lets go over what's oh-so-very wrong:
- The application protocol that was defined back here is pretty bad.
- One thing that there should be, is a message where one end tells the other end that it's disconnecting. I'm not talking about the FIN/ACK packets here. I'm saying that an actual
bye
message should be sent by the clients/server to tell the other that it will disconnect. Sure, the FIN/ACK packets will still be sent after thebye
is sent, but this is a much more graceful method and more descriptive. There are many applications out there (like online video games) that use this to tell if some has quit the application normally (i.e. ragequit) or not. - There should also be a "heartbeat," message somewhere. This is where the clients & server send each other an "I'm still alive," message every couple of seconds (e.g. 30). These are typically very tiny messages that would just have something such as the time the time the heartbeat was sent, but sometimes I've seen people stuff some extra metadata into them. Pings can also double as these too
- The server should also be sending error messages to clients. Say for example if a Messenger tries to use a name that is already taken, the server should respond with "Error, name taken by someone else." before closing the connection.
- One thing that there should be, is a message where one end tells the other end that it's disconnecting. I'm not talking about the FIN/ACK packets here. I'm saying that an actual
-
Look at the function
_handleNewConnection()
inTcpChatServer.cs
, there's something really-really-bad about it. Walk through it and see if you can spot the really-really-bad problem. Hint:netStream.Read()
If a client connects, but doesn't send a message. The server is just going to hold here. Forever. We didn't set any timeouts in the code, so by default the stream will just keep trying to read for a really-really-long time.
Someone could just telnet into the Chat Server, but not send a message to identify themselves. This would hold the server hostage. - We should be using timeouts when reading data from the network. Right now the
NetworkStream
s are waiting for data indefinitely. Take a look at the timeout related properties ofNetworkStream
. - Split the user-end application into two things (Viewer and Messenger) isn't the most user friendly thing to do. It's nice for those that just want to listen in, but a hassle for those who want to send & receive messages. This was done though so we wouldn't have to use any GUI code for this application.
- Any real world networking application should use
async
code, be multi-threaded, and have timeouts; there is none here. Normally, servers will spin up a new thread or process when a new client connects, so it can handle both the newbie as well as the currently connected clients. We could have also used the asynchronous read/write methods onNetworkStream
.
If you have anything to say about this, send me a message. There might have been some major flaws that I've missed. I would like to hear about them and put them up here. Also, if you want to have your own fun and try to make this server multi-threade, asynchronous and time'd-out, feel free to do that and send me a link to the code when you're done. It would be nice to see a "more correct," version of this.
This also was a much more lengthy example than my other stuff. I'll try to make the rest of them a bit simpler.