0% found this document useful (0 votes)
70 views

Message System: Carbonum Labs

In this ebook we see how to build a basic messaging system using UDP ports. The main focus will be placed in the following topics: • Creation of UDP ports both server and client. • Handling of the event OnStateChanged. • Handling of the event OnReceiveMessage. • Broadcasting messages to all active connections. • Keeping track of user state for each connection. • Dropping connections that are no longer needed.
Copyright
© © All Rights Reserved
Available Formats
Download as PDF or read online on Scribd
0% found this document useful (0 votes)
70 views

Message System: Carbonum Labs

In this ebook we see how to build a basic messaging system using UDP ports. The main focus will be placed in the following topics: • Creation of UDP ports both server and client. • Handling of the event OnStateChanged. • Handling of the event OnReceiveMessage. • Broadcasting messages to all active connections. • Keeping track of user state for each connection. • Dropping connections that are no longer needed.
Copyright
© © All Rights Reserved
Available Formats
Download as PDF or read online on Scribd
You are on page 1/ 11

2010

Carbonum Labs
www.carbonum.com
Copyright (c) 2009-2014

[MESSAGE SYSTEM]
Creating a Message System with Port Manager 2010.
Message system

Message System
In this example we build a basic messaging system using UDP ports. The main focus will be placed
in the following topics:

• Creation of UDP ports both server and client.


• Handling of the event OnStateChanged.
• Handling of the event OnReceiveMessage.
• Broadcasting messages to all active connections.
• Keeping track of user state for each connection.
• Dropping connections that are no longer needed.

Remember UDP is a connectionless protocol, however the PortManager still handles UDP end
points as if they where connections. UDP server ports have an IDLE timer (connection timeout) if
the UDP server detects that a given end point has been silent for more than the specified timeout,
it will consider the end point as “disconnected”; at this moment it will issue the OnStateChanged
event and release all associated resources.

The Protocol
We will have a simple text based protocol to communicate our client and server applications. UDP
gives no guaranties about packet delivery, order or integrity, however in order to simplify this
example we will assume no packets can be lost in the network so there will be no need to handle
acknowledges nor retries for ourselves. If this cannot be guaranteed in your environment, switch
to TCP ports or design and implement a robust protocol that is capable of solving these problems
to some extent.

To begin with, any client connected to the server will have to provide a nickname before sending
any messages. We will keep track of this nickname using the UserState property found in each
active connection. After that, each client will simply send any message the user types to the
server, and also show on the screen the messages the server will send to it.

All messages in the protocol start with the delimiter "$m[" and ends with "]\r\n". The general
structure of a message is as follows:

$m[COMMAND,ARG1,ARG2,...,ARGN]\r\n

Where:

• COMMAND is the name of the command being issued to the server or client.
• And ARG1, ARG2, and so on, are arguments required by the command in order to be
executed. These arguments should be scaped to avoid potential parsing problems using
the following scape sequences:

Page 2
Message system

Original Character Scaped Character


, \x01
] \x02
Note: Since the protocol is meant to transmit plain text, we are not providing any scaping symbols
for characters \x01 and \x02.

The protocol itself is composed of the following commands:

Registration Command

This command allows a client to register its nickname within the server. This should be the first
command sent by any client before sending any other command.

$m[REGISTER,Nickname]\r\n

Please note that to avoid unnecessary complexity we are not authenticating users, nor validating if
they are already registered in a different end point (or active connection).

Send message command

This command allows the client to send a message to the server.

$m[SEND,mensaje]\r\n

Whenever a message is sent to the server, the server will append the name of the user that is
sending it, and broadcast to all connected users using the same Send command (this includes the
original sender).

Disconnect command

Since UDP is a connectionless protocol, before any client decides to go away it’s a good idea to tell
the server we are no longer interested in receive any more massages. The disconnect command
enables a client to notify the server it should dispose any resources associated to its connection.

$m[DISCONNECT]\r\n

Server application
Create a new solution and add a new windows application project to it, name it
“MessageSystemServer”. This project will be our server application. The main form in this project
should have the layout shown in figure 1. Notice we have already added the Port Manager
component in the tray area of the form designer.

Page 3
Message system

Right click the Port Manager component and select the Port configuration option from the
contextual menu that shows up. Once there, add a new server port using the UdpServer interface
(if you wish you can use the TcpServer interface instead since we are not really taking any
advantage of the udp protocol here). Make sure to set the timeout property to zero (disabled).
Also add the message delimiter (“$m[“, “]\r\n”) as shown in figure 2.

Figure 1. Layout of the server application form.

As we mentioned before we are going to keep track of state information for each connected client
using the UserState property of each active connection found in the server port we just created. In
this example the user state object will be used simply to remember the nickname of the user that
is connected at the end point. We will store this information in a separate class of type
“MsgSrvUserState” shown below:

class MsgSrvUserState
{
private string nickname;

public MsgSrvUserState()
{
}

public string Nickname


{
get { return nickname; }
set { nickname = value; }
}
}

Page 4
Message system

Figure 2. Add a server port using the UdpServer interface.

Open the port configuration screen again, select the server port and then click on the
OnStateChanged button (highlighted in figure 2) to create a new event handler for this event. Add
the following code to it:

private void MessageServer_OnStateChanged(IComPort sender, PortConnectionEventArgs e)


{
if (e.State == ConnectionState.Connected)
{
//Create a new user state object and add a row to the ListView
e.Connection.UserState = new MsgSrvUserState();
ListViewItem item = new ListViewItem(e.Connection.ID.ToString());
item.SubItems.Add(e.Connection.HostName + ":" +
e.Connection.Port.ToString());
item.SubItems.Add("Anonymous");
list.Items.Add(item);
}
else if (e.State == ConnectionState.Disconnected)
{
for (int i = 0; i < list.Items.Count; i++)
{
if (list.Items[i].Text == e.Connection.ID.ToString())

Page 5
Message system

{
list.Items.RemoveAt(i); //drop endpoint from the listview
break;
}
}
}
}
Each active connection can have one of three possible states: Connecting, Connected or
Disconnected. Whenever this state changes, the port will issue the OnStateChanged event. So
basically, when we get an event with the connected state means we have just established a new
connection, in this case we create a new MsgSrvUserState object and assigning it to the UserState
property of the connection which state is being reported. We also add a new item to the ListView
to reflect the new connection.

In the other hand, when we get an event with the disconnected state means a connection we had
previously established is being closed and discarded. In this case, we simply erase the
corresponding row in the ListView. There is no need to do anything about the user state we
previously created since our user state object does not need any cleanup and will be collected; this
however might not always be the case, so you can use this opportunity to do the necessary
cleanup.

Next we need to write a handler for the OnReceiveMessage event. Open the port configuration
again, select the server port and click on the OnReceiveMessage button to create the event
handler. Fill with the code shown below:

private void MessageServer_OnReceiveMessage(IComPort sender, PortMessageEventArgs e)


{
MsgSrvUserState st = (MsgSrvUserState)e.Connection.UserState;
//Parse received message
string[] msgParts = e.Message.Split(new char[] { ',', ']' },
StringSplitOptions.RemoveEmptyEntries);
if (msgParts.Length >= 2)
{
switch (msgParts[0].ToLower())
{
case "$m[register":
//Update the nickname in the UserState object
st.Nickname = unscape(msgParts[1]);
//Update the listview too
for (int i = 0; i < list.Items.Count; i++)
{
if (list.Items[i].Text == e.Connection.ID.ToString())
{
list.Items[i].SubItems[2].Text = st.Nickname;
break;
}
}
break;
case "$m[send":
if (st != null)
{
string message = st.Nickname + " says:\r\n" + msgParts[1];
//Broadcast the received message to all active connections
sender.Broadcast("$m[SEND," + scape(message) + "]\r\n");
}
break;
case "$m[disconnect":

Page 6
Message system

//Symply close the connection so that the server port


//reports the OnStateChange event and drop its resources.
e.Connection.Disconnect();
break;
}
}
}

Here we parse the received message and process accordingly. If we receive a REGISTER command,
we update the UserState object (and the listview), if we receive a SEND command, we generate a
new message appending the nickname of the user that is sending it and broadcast to all active
connections. Finally if we receive a DISCONNECT command, we simply drop the connection calling
it’s Disconnect method.

Notice we are using two methods named scape and unscape; we previously said we need to do a
scape operation to avoid potential parsing errors, those functions are show below:

private string scape(string str)


{
return str.Replace(',', '\x01').Replace(']', '\x02');
}

private string unscape(string str)


{
return str.Replace('\x01', ',').Replace('\x02', ']');
}

The final step would be to write the click event handlers for the start/stop buttons in the form:

private void btnStart_Click(object sender, EventArgs e)


{
btnStart.Enabled = false;
portManager.Start();
btnStop.Enabled = true;
lStatus.Text = "Started...";
}

private void btnStop_Click(object sender, EventArgs e)


{
btnStop.Enabled = false;
portManager.Stop();
btnStart.Enabled = true;
lStatus.Text = "Stoped...";
}

Also remember to set the SyncControl property of the Port Manager component to avoid cross-
thread exceptions (figure 3).

Page 7
Message system

Figure 3. Set the SyncControl property to avoid cross-thread exceptions.

However mind the fact that handling all events in the GUI thread might cause the user interface to
hang or appear non-responsive. It’s the same problem you would face if you start doing expensive
processing in the click event handler of a button for instance.

Client application
For our client application, create a new windows application project named
“MessageSystemClient”, and add some components to the main form to match the layout shown
in figure 4.

Page 8
Message system

Figure 4. Layout of the client form.

After that, open the port configuration screen and create a new client port using the Udp
interface. Name this port “MsgSrvClient” and make sure you add the message delimiter as show in
figure 5.

Figure 5. Add a client port using the udp interface.

After saving the new port, click on the OnStateChanged button to write the corresponding event
handler. Fill it with the following code:

private void MsgSrvClient_OnStateChanged(IComPort sender, PortConnectionEventArgs e)


{
if (e.State == ConnectionState.Connected)
{
btnConnect.Enabled = false;
btnDisconnect.Enabled = true;
sender.SendMessage(e.Connection,
"$m[REGISTER," + scape(txtNickname.Text) + "]\r\n");
}
else if (e.State == ConnectionState.Disconnected)
{
btnConnect.Enabled = true;
btnDisconnect.Enabled = false;
}

Page 9
Message system

}
In this event handler we manage the state of the connect and disconnect buttons, also when an
event reports a Connected state, we send the REGISTER command to the server.

Next we write the click event handler for the Send button as follows:

private void btnSend_Click(object sender, EventArgs e)


{
portManager.Ports["MsgSrvClient"].SendMessage("$m[SEND," +
scape(txtSend.Text) + "]\r\n");
txtSend.Text = "";
}
Here we simply issue the SEND command to the server and clear the input control.

Go back to the Port configuration screen (by right clicking in the Port Manager component and
selecting the Port configuration option from the contextual menu), select the client port and click
on the OnReceiveMessage button to write the corresponding event handler:

private void MsgSrvClient_OnReceiveMessage(IComPort sender, PortMessageEventArgs e)


{
//Parse received message
string[] msgParts = e.Message.Split(new char[] { ',', ']' },
StringSplitOptions.RemoveEmptyEntries);
if (msgParts.Length >= 2 && msgParts[0].ToLower() == "$m[send")
{
txtRecv.AppendText(unscape(msgParts[1]));
txtRecv.AppendText("\r\n");
}
}
Here we parse the message the server is sending us, if it is a SEND command, we display the
message on the screen. Note that the scape and unscape functions are the same seen before.

Finally we need to write the event handlers for the connect and disconnect buttons. It is important
to note that we cannot know the IP/Port of the server at design time, this information needs to be
provided by the user. When we click the connect button, we need to update the Hostname and
Port properties of the client port before calling the Start method:

private void btnConnect_Click(object sender, EventArgs e)


{
if (txtNickname.Text.Length == 0)
MessageBox.Show("Enter a nickname first");
else
{
string hostname = txtHostname.Text;
int port = Convert.ToInt32(txtPort.Text);
portManager.Ports["MsgSrvClient"].HostName = hostname;
portManager.Ports["MsgSrvClient"].Port = port;
portManager.Start();
}
}

On the other hand, when we click the disconnect button, we first send the DISCONNECT command
to the server, and then call the Stop method on the Port Manager:

Page 10
Message system

private void btnDisconnect_Click(object sender, EventArgs e)


{
portManager.Ports["MsgSrvClient"].SendMessage("$m[DISCONNECT]\r\n");
portManager.Stop();
}

We also need to set the SyncControl property of the Port Manager control to avoid cross-thread
exceptions here.

Build and run the solution (start both applications), make sure you click the start button in the
server application before you start doing anything with the client. Also note that you can run
several instances of the client and see how a conversation would look.

Note: The complete Visual Studio solution for this example can be found in your installation route
under the examples folder, look for the MessageSystem example.

Page 11

You might also like