Introduction
Microsoft has created a set of technologies for creating peer-to-peer applications. These technologies represent low level building blocks that can be used to build many types of applications. Adrian Moore has explained these technologies in several CodeProject Articles. He has also brought these technologies to the managed programming world.
For our senior project at Michigan State University, we were asked by Microsoft to create a set of classes that can be used to quickly create peer-to-peer applications. Since there had already been work done by Adrian to do this, we decided to create an extension of his library that would focus on creating chatting style applications easily. This article shows how we created it.
Thinking About Chatting
To begin our library, we looked at several types of chatting applications. It seemed that everywhere we looked, there was chatting going on. Whether it's through your favorite instant messaging client, or in a channel on IRC, or even on a blog website that several authors contribute to, all of these types of interactions could be thought of as chatting, just with a slight skew on naming and presenting.
What we concluded was that all of these styles of chat applications could be considered a "chatroom", and different people exchange "messages" there that persisted for varying lengths of time.
Using Graphs and Records
When we looked at the peer technologies offered by Microsoft and Adrian, we were especially drawn to the graphing technology. Graphs can be thought of as a database of records, which is similar to how you can consider lots of chatting applications: as a chatroom of messages.
So we created a class that modeled this basic relationship, and called it Chatroom
. In our model, the Chatroom
handles all of the networking code needed to set up a graph and connect to peers in the same chatroom. Below is a simplified version of the Connect()
method.
_graph = new Peer.Graph.PeerGraph(this.Name);
_chatID = Guid.NewGuid();
ChatID = _chatID.ToString();
Peer.Graph.PeerGraph.Create(this.Name, ChatID, DatabaseName);
_graph.Open(DatabaseName, ChatID );
As you can see, we first create a graph (which will fail if someone has already created one) and then open it for connections to other peers. The Chatroom
also handles converting messages into records. Below is a simplified version of the Send(Message)
method:
BinaryFormatter formatter = new BinaryFormatter();
MemoryStream memory = new MemoryStream();
Peer.Graph.PeerRecord record;
formatter.Serialize(memory, message);
record = _graph.CreatePeerRecord(CHAT_MESSAGE_RECORD_TYPE,
message.Lifetime);
record.DataAsStream = memory;
_graph.AddRecord(record);
As you can see, we actually serialize the entire object as the data of a record. Using this method makes the data reliable as it cannot be deserialized unless the entire messages is received. We used this method for transmitting all of the data that needs to be synchronized in a chatroom.
Events
Like most Windows APIs, the peer-to-peer technology publishes events that your program can listen to. We are able to publish events specifically for Users and Messages in our library, by subscribing to peer events. When a record gets added to the database, we determine what type of record it is and fire the correct event. Below is a simplified version of how we determine which event to fire based on a graphing event.
Peer.Graph.PeerRecord record;
BinaryFormatter formatter = new BinaryFormatter();
switch (e.Action)
{
case Peer.Graph.PeerRecordAction.Added:
if (e.RecordType == USER_RECORD_TYPE)
{
record = this._graph.GetRecord(e.RecordId);
PeerChat.User user =
(PeerChat.User)formatter.Deserialize(record.DataAsStream);
if (this.UserJoined != null)
{
UserEventArgs args = new UserEventArgs(user);
UserJoined(this, args);
}
...
As you can see, we only have to retrieve the record out of the database and instantiate it as an object that we can then work with.
UI Controls
After creating the classes needed for our infrastructure, we wanted to make it easy for developers to make a Windows Forms application. To do this, we created custom controls that have a Chatroom
associated with them. We used the events published by our Chatroom
to control how they behave. Take this example of the UserList
control which provides a visual representation of the presence in a chatroom.
m_chat_room.UserJoined += new
PeerChat.Chatroom.UserEventHandler(m_chat_room_UserJoined);
...
void m_chat_room_UserJoined(object sender, PeerChat.UserEventArgs e)
{
AddUserToList(e.User);
}
public void AddUserToList(PeerChat.User the_user)
{
if (this.InvokeRequired)
{
UserCallback methodToCall = new UserCallback(AddUserToList);
Invoke(methodToCall, new object[] { the_user });
}
else
{
this.Items.Add(the_user.Name);
}
}
The InvokeRequired
flag is used because updating a Windows Forms control from a different thread (the peer thread) is not a safe practice. In the example above, the name of the user who had joined the Chatroom
is added to the Items
of a list box.
Conclusion
While there are many other aspects of the peer-to-peer technologies, we did not cover them in our project, we hope that our venture into this world will save some time for developers using this type of technology. There are also many aspects of the PeerChat library that we have not discussed in this article, but if you would like to learn more, you can download the source code to PeerChat on the top of this page. You can also learn more about graphing by viewing Adrian Moore's CodeProject articles.
About The Project
PeerChat was developed by Mike Fazio, Dan Lash, Carly Szekely, and Tom Vollman at Michigan State University. The website for the project is located here.