iNetwork Tutorial #2: Chat

back to iNetwork

Readings

Prior to beginning this tutorial example, the following provides a short reading list that contains discussions and detailed descriptions of different aspects of the iNetworking Toolkit. If you have not already done so, it is recommended that you read through these before setting out to attempt the provided tutorials.

Introduction

Summary

The Chat tutorial example simulates an instant messaging environment. Just as in any instant messaging application clients are able to send chat messages to each other via the server.

Features

  • Users are prompted for a username.
  • Multi-user conversations.
  • The server's client list displays a user's chosen username rather than the connection.
  • All clients are alerted when another client either connects or disconnects.

High Level Overview

As with any instant messaging environment each user has a chat window where in they are able to type and send their message and see the chat log in a display.

When a client connects to the server this window is displayed to the user and they are prompted to enter a username. It is set-up in such a way that the user will not be able to send instant messages unless a username has been provided. Once the username has been entered it is stored by the client. A message containing the client's username is sent to the server. It is then kept in a list of online users. The server also updates its UI to show the user's username rather than the connection. At this point, the user is considered to have come online and so the server sends out a another message to all clients, with the exception of that one. The client UI's are updated to show the string of text that says the user is now online.

When a user sends an instant message by, either, clicking the send button or pressing the enter key the client sends a message to the server containing the string of text and the client's username. In return the server sends out messages to all clients. Upon receiving this message, the clients display the string of text and the username on their UI.

At the point at which a user goes offline, the client sends one last message to the server containing the client's username. The client then disconnects and the server updates its UI and removes the user from the list. A message is also sent by the server to all clients left behind to tell them that the user went offline by showing the string of text saying the user is now offline..

Reminder: The executable file, called ChatClient, for this example has been provided in a folder named Tutotrial2 when the toolkit was installed. You will find this in the Tutorial Examples folder within the GroupLab iNetwork Toolkit directory. Be sure to extract the files before opening them.

back to top

Setting-Up the Form

  1. Begin this tutorial by using a . Name the client project ChatClient and the server project ChatServer.
  2. Once you have completed the initial set-up above,from the Solution Explorer open the XAML file labelled MainWindow.xaml for the ChatClient''.
  3. In the designer view (XAML) add two TextBoxes. One will serve as the chat window where all messages will be displayed. The other will be the typing plane where users can type in their messages.
    • In the Properties tab, change the name of TextBox1 to _chatLog.
    • Similarly, change the name of TextBox2 to _messageText.

    • In the Properties window for TextBox1 find isReadOnly and make sure that it is selected.

    • In the Properties window for TextBox2 find isEnabled and make sure that it is not selected or ticked just as shown below.

  4. Add a Send Button.
    • In the Properties tab, change this button's name to _sendButton.

  5. Lastly, add another TextBox that will be used as a username input textbox and a Label as shown below.
    • In the Properties tab, change the name of this textbox to _usernameTextbox.
    • Similarly, change the name of the label to _instructionLabel.

back to top

Adding Code

This part of the tutorial will build on the "skeleton"code in the client and server templates.

Client

Open up the C# code file for the client.

Class Instance Variables Region
  1. Begin by creating a string variable that will remember the client's username.
    private string _username;
Constructors Region
  1. Within the MainWindow constructor add a KeyUp event listener.
    this._usernameTextbox.KeyUp += new KeyEventHandler(OnUsernameEntered);
Initialization Region
  1. Change Name-of-Service to Chat, found in the InitializeConnection method.
    Connection.Discover("Chat", new SingleConnectionDiscoveryEventHandler(OnConnectionDiscovered));
Main Body Region

Note: This region is called Sending Chat Messages within the executable files provided during installation.

  1. In order to successfully update the UI, changes need to be done through the GUI thread. Adjust the code in the OnMessageReceived event handler method so that it looks like the following:
    1. this.Dispatcher.Invoke(
    2.     new Action(
    3.         delegate()
    4.             {
    5.                 if (msg != null)
    6.                 {
    7.                     switch (msg.Name)
    8.                     {...}
    9.                 }
    10.             }
    11.         }
    12. ));
  2. Within the switch (msg.Name) portion [line 8, step 4] of the OnMessageReceived event handler method create three message cases in addition to the default.
    1. case "UserOnline":
    2.     ...
    3.     break;
    4. case "ChatMessage":
    5.     ...
    6.     break;
    7. case "UserOffline":
    8.     ...
    9.     break;
  3. If the message is the UserOnline message, the client retrieves the string field, which is the username of the user that came online.
    1. string userOnline = msg.GetStringField("username");
  4. If the client's _messageText textbox is enabled then a line of text stating some user is online is added to the _chatLog textbox. This check is done so that the online message does not show in the _chatLog of a user who is not considered to be online.
    1. if (this._messageText.IsEnabled)
    2. {
    3.     this._chatLog.AppendText(userOnline + " is now online. \n");
    4. }
  5. If the message is the ChatMessage message, the client will be retrieving two sets of strings from two string fields. The first is the username and the second is the chat message text.
    1. string username = msg.GetStringField("username");
    2. string text = msg.GetStringField("text");
  6. Next the username and the chat message is added to the listbox in the client's UI which displays the messages that are exchanged in the chat. Again this is only allowed to happen if the client's _messageText textbox is enabled.
    1. if (this._messageText.IsEnabled)
    2. {
    3.     this._chatLog.AppendText(username + ": " + text + "\n");
    4. }
  7. If the message is the UserOffline message, the client retrieves a string field containing the username of the user that went offline.
    1. string userOffline = msg.GetStringField("username");
  8. Similarly, assuming that the user's _messageText textbox is enabled, a string text stating that a user went offline is added to the _chatLog.
    1. if (this._messageText.IsEnabled)
    2. {
    3.     this._chatLog.AppendText(userOffline + " went offline. \n");
    4. }
  9. A KeyUp event calls on the OnUsernameEntered event handler method which checks whether the key that had been pressed was the enter or return key.
    1. private void OnUsernameEntered(object sender, KeyEventArgs e)
    2. {
    3.     if (e.Key == Key.Return || e.Key == Key.Enter)
    4.     {...}
    5. }
  10. In the case that the aforementioned key is pressed then the text in the _usernameTextbox is assigned to be the client's username. The username prompts (both the label and the textbox) are then hidden. Add the following lines to the if statement [line 4, step 12]:
    1. this._username = this._usernameTextbox.Text;
    2. this._usernameTextbox.Visibility = Visibility.Hidden;
    3. this._instructionLabel.Visibility = Visibility.Hidden;
  11. Enable the _messageText textbox to grant the user access to the chat.
    1. this._messageText.IsEnabled = true;
  12. Add a KeyUp event listener for the _messageText textbox and a Click event listener.
    1. this._messageText.KeyUp += new KeyEventHandler(OnMessageEntered);
    2. this._sendButton.Click +=new RoutedEventHandler(OnSendButtonClick);
  13. Finally call a new message called UserOnline is created, containing the client's username, and is sent to the server.
    1. Message msg = new Message("UserOnline");
    2. msg.AddField("username", this._username);
    3. this._connection.SendMessage(msg);
  14. When the _sendButton is clicked the OnSendButtonClick event handler method is called, which in turn calls the SendMessage method.
    1. private void OnSendButtonClick(object sender, RoutedEventArgs e)
    2. {
    3.     SendMessage();
    4. }
  15. Similarly, the SendMessage method is also called in the OnMessageEntered event handler method' as long as the key that was pressed is either the enter or return'' key.
    1. private void OnMessageEntered(object sender, KeyEventArgs e)
    2. {
    3.     if (e.Key == Key.Return || e.Key == Key.Enter)
    4.     {
    5.         SendMessage();
    6.     }
    7. }
  16. In the event that a user closes their chat window the OnWindowClosing event handler method is called. Here a new message called UserOffline is made containing the client's username. This message is then sent to the server.
    1. private void OnWindowClosing(object sender, CancelEventArgs e)
    2. {
    3.     Message msg = new Message("UserOffline");
    4.     msg.AddField("username", this._username);
    5.  
    6.     this._connection.SendMessage(msg);
    7. }
  17. Now create the SendMessage method.
    1. private void SendMessage()
    2. {...}
  18. Add the first task this method executes [line 2, step 20], which is to get the text that the user has entered into the textbox .
    1. string messageToSend = this._messageText.Text;
  19. As long as the chat message is not null and it is not an empty string, a new message called ChatMessage will be created containing the username and the chat message text. It is then sent to the server and the _messageText textbox is cleared.
    1. if (messageToSend != null && messageToSend != "")
    2. {
    3.     Message message = new Message("ChatMessage");
    4.     message.AddField("username", this._username);
    5.     message.AddField("text", messageToSend);
    6.     this._connection.SendMessage(message);
    7.     this._messageText.Text = "";
back to top

Server

Open up the C# code file for the server.

Class Instance Variables Region
  1. Start off by creating another list. This list will be a collection of strings rather than connections. It will contain the usernames of all the connected clients/online users.
    private List<string> _onlineUsers;
Initialization Region
  1. In the InitializeServer method, change Name-of-Service to Chat. You are also free to change the port as long as the port number is larger than 1024 and does not exceed 65535.
    this._server = new Server("Chat", 12345);
Main Body Region

Note: This region is called Sending Chat Messages within the executable files provided during installation.

  1. Within the switch (msg.Name) portion of the OnMessageReceived event handler method create three message cases in addition to the default.
    1. case "UserOnline":
    2.     ...
    3.     break;
    4. case "ChatMessage":
    5.     ...
    6.     break;
    7. case "UserOffline":
    8.     ...
    9.     break;
  2. If the message is the UserOnline message, retrieve the string field containing the username of the user that came online.
    currentUser = msg.GetStringField("username");
  3. Next, if the list _onlineUsers does not contain the username that was retrieved from the message then it is added to that list.
    1. this._onlineUsers.Add(currentUser);
  4. Broadcast the message to all clients, including the client that just connected and the UpdateClientList method is called.
    1. this._server.BroadcastMessage(msg, (Connection)sender);
    2. UpdateClientList();
  5. Now, if the message is the ChatMessage message, simply broadcast to all clients.
    this._server.BroadcastMessage(msg);
  6. Lastly, if the message is the UserOffline message, the username is retrieved then the server checks if the username provided by the message is in the list. If it is, then the username is removed then the message is sent to all clients and the UpdateClientList method is called.
    1. currentUser = msg.GetStringField("username");
    2. if (this._onlineUsers.Contains(currentUser))
    3. {
    4.     this._onlineUsers.Remove(currentUser);
    5.     this._server.BroadcastMessage(msg, (Connection)sender);
    6.     UpdateClientList();
    7. }
  7. Create the UpdateClientList method. Inside this method, through the GUI thread, the listbox is, first, cleared off all the items it contains. Then each user that is in the online users list is added to the listbox.
    1. private void UpdateClientList()
    2. {
    3.     this.Dispatcher.Invoke(
    4.         new Action(
    5.             delegate()
    6.             {
    7.                 this._clientsList.Items.Clear();
    8.                 foreach (string user in this._onlineUsers)
    9.                 {
    10.                     this._clientsList.Items.Add(user);
    11.                 }
    12.             }
    13.     ));
    14. }
back to top

Result

Reminder: Ensure that the server is run before the client. Right click on ChatServer in Solution Explorer and select Set as StartUp Project. To run/add instances of the client simply right click on ChatClient in the Solution Explorer. Select Debug then Start a new instance.

You should get something like the following:

back to top