Continuing from yesterday’s article where I talked about how I wrote the code to run the chat server portion of PoshChat, this article will now go into what I did to create the client interface that connects to the server and allows you to send messages to others connected to the server.

About half of the code is setting the UI of the client while another chunk of code sets up some of the controls. The rest is where I set up the connection to the server and send/receive messages that have been relayed from the server from other clients.

As with the server, there were some requirements that I wanted to lay out before starting on this client.

Requirements:

Make a connection to the server

Allow a username and server name to be defined for connection

Able to actively listen for messages while still being able to send messages

First thing that I am doing is setting up the runspace that will run in the background to free up the console while the UI is running. The key thing here is that I am setting the apartment state to ‘STA’ so the UI will work normally. This means that you can run the console in ‘MTA’ without worry about the UI not starting up.

Here is the beginning of the scriptblock that I will supply to the runspace. The first thing I do is load of the assemblies required to run the WPF client and display the UI. I am also setting up the UI using XAML code. the first line here is where I begin adding the code to the runspace via a scriptblock. The scriptblock will not be closed up until the end of the script. The XAML code is casted to XML using the [XML] type accelerator that then gets loaded into the System.XML.XMLNodeReader. Next, I connect to the controls that I need access to later on in the code to perform a variety of things such as handling button events or reading from a text box.

Now we are getting into the first control event being handled. This is what controls the connect button and is actually the biggest chunk of code as it has to handle the initial connection attempt to the chat server. A validation is made to be sure that a username and server name is supplied before proceeding. Once that is done a local endpoint is created using a random port number using System.Net.IPEndpoint class. Once that has been completed, a TCP object is created and begins an attempted connection to the chat server over port 15600 (currently hard-coded, but will changed in the next release) and if a connection is successful, then spawns a new runspace that will continue to handle the connection and listen for messages from the server.

The created runspace is saved to a synced hashtable ($ClientConnections) that will be used later if the client is closed so it can gracefully close out all of the runspaces and connections.

This ensures that while the connection is active, it will constantly look for messages from the server. The Read() method will block any more action in the runspace until a message has been received on the client. If the message contains data, it will be translated from bytes and sent to the message queue.

This piece of code handles the send button event when clicked. The text from the inputbox is collected with the “~M” appended to it so can be interpreted as a message, then it gets converted into a byte[] array before being sent to the chat server.

This really is a two-part chunk of code as it not only handles the initial loading of the form, but also sets up a form timer that performs an action with every tick. The ‘tick’ is set for every second. Two synchronized objects are created to handle the client/server connection and to handle all of the messages from the server.

MessageQueue

Handles all of the message traffic from the server

ClientConnection

Handles the client connection with the server

Next up is creating and configuring the form timer (System.Windows.Threading.DispatcherTimer) to check the message queue and print out messages to the client based on what type of message is received from the server. The following types of messages that are accepted are:

~M

Standard messages from other connected clients

~D

Handles messages when other clients are disconnected from server

~C

Handles messages when new clients are connected to server

~S

Handles the message when the server is shutdown or closes client connection unexpectedly

~Z

Handles the initial connection message sent from the server listing all of the currently connected clients.

Here we are handling the disconnect button event. A message is displayed on the window stating the client is disconnecting and then the socket connection is closed. Afterwards, the rest of the runspace starts shutting down and getting disposed. Finally the list of online clients is cleared and the Connect and Disconnect buttons swap availability to be enabled and disabled.

Because there is a chance that the window could be closed instead of using the Disconnect button, I want to be sure to handle that event and shut everything down gracefully. The main pieces are here to shutdown the socket and the runspace.

[void]$Window.showDialog()

This is the last piece of code in the runspace scriptblock and it is very vital as it is the code that starts up the UI for the client. I use ShowDialog() because it will bring up the window in the runspaces thread instead of using Show() which will cause the UI to lockup and become un-usable.

}).BeginInvoke()

And here is the last piece of code in the script! I close out the scriptblock and then at the same begin running the runspace using BeginInvoke(). Using BeginInvoke() calls the runspace asynchronously instead of synchronously which allows the console that it is called from to be free from waiting for the runspace to finish, which would have happened had I used Invoke().

When the code is run, you see this:

. .\Start-PoshChatClient.ps1

A nice clean chat client that you can now use to connect to the chat server that was shown in the previous article.

Remember, you can download PoshChat here and if you have any feature request or find bugs, to report those here.