Click here to Skip to main content
15,867,895 members
Articles / Desktop Programming / Windows Forms

C# \ VB .NET Multi-user Communication Library (TCP)

Rate me:
Please Sign up or sign in to vote.
4.91/5 (54 votes)
15 Oct 2010CPOL13 min read 364.8K   33.5K   144   145
Allow multiple users to send and receive messages from one another on the same server

RobotBattleNetCommServer.jpg

An example of the NetComm library usage in my robot-battle school project,
this is a screenshot of the server, 4 clients are connected to it.

Introduction

The NetComm library allows you to connect multiple clients to the same server. This allows you to do the following:

  • Transfer personal messages to each other
  • Send a message to all of the users in the same server (public messages)
  • Communicate with each client using only ID (a unique string), without the need of knowing each other IP, the clients only need to know the host IP address.

The NetComm library handles all of the complicated work, it works in a separate thread from your application UI - the NetComm library will raise an event when a new message arrived.

Why is this useful? Well, this allows you to create a multi-user chat easily - or any other kind of application that needs to handle multiple users communication.

With the use of this library, you can create this kind of complicated communication easily.

This article is written with C# but I attached a VB.NET example for VB users (my favorite language). This library was built in VB.NET.

Background

I developed this library for my school project - I created a server that keeps track of 2 robots which are being remotely controlled by 2 remote PCs. This project simulates a robot-battle between 2 teams.

I connected the clients (4 clients) to the same server. The server handled all the game rules, each robot life points, bullets, starting ammo, weapons. The main role of the server is making both of the teams follow the same rules. The server is a place where game calculations were taken. It's pretty much the "center" of the game, viewers could look at the server UI and see the scoreboard, each team's robot life points, bullets remaining and more... The NetComm library made it a lot more easier for both of the teams to communicate.

The Concept (or, How It Works? For More Advanced Users)

I built this library with VB.NET. I used the TCPClient and TCPListener (System.Net.Sockets) which Microsoft created as a base for this library. The whole library relies on these 2 classes.

To let the UI stay responsive, I created the whole communication on another thread, when a new message arrived, I needed to raise an event to the UI thread to notify him about the new message. I used the SynchronizationContext class (System.Threading) to get the UI thread context and post the new messages to the UI thread message queue.

To be able to use IDs (so clients could transfer messages using strings, like names without the need knowing of IPs), I used the AsciiEncoding class to convert the bytes into strings for the unique IDs.

Read Before "Using the Code" (Important Information)

In this library, all clients connect to the same server which means the following:

  • All clients should use the same port as the server uses.
  • All clients must know the server IP address.
  • All clients can communicate between one another using IDs, an ID describes who the client is that the message was pointed to. It allows clients to communicate with one another. For example: an ID could be a name like "Jack", if a client with an ID "Jack" wants to send a message to "ElvisPresley" he simply uses his ID ("ElvisPresley") to send him the message. Those messages are being transferred via the host, which manages to analyze them and sends them to the recipient:
NetCommHowItWorks.jpg

Jack wants to send ElivsPresley a message, he uses his ID (ElivsPresley)
to send that message, the host receives the message,
analyze it and sends it over to ElvisPresley

  • The host and clients can send and receive messages. They can send private messages to each other. The host can broadcast messages (send a public message to all participating clients).
  • The host ID is always "" (null), to send a private message to the host we use the SendData method with a "" string. (If you still don't understand, you will get it in the end).
  • The NetComm library can send and receive an array of bytes. If you are planning to use this library for chat purposes, you can use the following function to convert bytes to string and string to bytes:
C#
string ConvertBytesToString(byte[] bytes)
{
    return ASCIIEncoding.ASCII.GetString(bytes);
}

byte[] ConvertStringToBytes(string str)
{
    return ASCIIEncoding.ASCII.GetBytes(str);
}

These 2 functions use the AsciiEncoding class, which uses the Ascii table to convert a char to its corresponding numeric value. (Each byte represents a character in a string).

NetCommChatExampleWithNames.jpg

The VB.NET example attached - a C# example is also attached

Using the Code

To start up, create your project and do the following:

  1. Open up your project.
  2. Right click on "References" and "Add Reference".
  3. Select the "Browse" tab and select the extracted "NetComm.dll".
  4. Accept all dialogs.

Creating the Host

HostScreenshot.jpg

To start our first step, we need to create a host that listens to any incoming client. The host himself can send and receive messages.

On your Form class global declaration, add the following declaration:

C#
NetComm.Host Server; //Creates the host variable object

On the Form_Load event handling method, add the following code:

C#
Server = new NetComm.Host(2020); 	//Initialize the Server object, 
				//connection will use the 2020 port number
Server.StartConnection(); 		//Starts listening for incoming clients

2020 is the port number, you can use whatever port number you want, but make sure the client uses the same one.

The 2nd line tells the Server object to start listening to any incoming client.

The Host Class Includes the Following Events

  • onConnection(string id)
    Every time a user connected, this event will raise to inform the application. The id argument includes the ID of the client connected.
  • lostConnection(string id)
    Every time a user disconnected, this event will raise to inform the application. The id argument includes the ID of the disconnected client.
  • errEncounter(Execption ex)
    Every time an error occurs, this event will raise to inform the application. The ex argument includes the exception object.
  • ConnectionLost()
    This event will raise when the server connection was closed (stopped listening).
  • DataReceived(string ID, byte[] Data)
    Every time a message arrived, this event will raise to inform the application. The ID argument holds the ID of the client who sent the message. The Data argument holds the array of bytes that client sent.
  • DataTransferred(string Sender, string Recipient, byte[] Data)
    Every time a message was transferred from client to client (using the Client.SendData method), this event will raise to inform the application. The Sender holds the ID of the client who sent the message. The Recipient holds the ID of the client who receives the message. The Data argument holds the data being transferred from client to client.

I created a textbox, and changed its name property to "Log", this textbox will log everything that occurred on the server.

Let's add the following lines to the Form_Load event handling method (below the previous lines):

C#
//Adding event handling methods, to handle the server messages
Server.onConnection += new NetComm.Host.onConnectionEventHandler(Server_onConnection);
Server.lostConnection += new NetComm.Host.lostConnectionEventHandler
			(Server_lostConnection);
Server.DataReceived += new NetComm.Host.DataReceivedEventHandler(Server_DataReceived);

We need to create some methods that will handle this events, for the onConnection event I have added the following method:

C#
void Server_onConnection(string id)
{
    Log.AppendText(id + " connected!" + 
	Environment.NewLine); //Updates the log textbox when new user joined
} 

Every time a user join the room, your application goes to this method, and will update the log when a user enters the server.

The log keeps being updated, to notify us about new users joining the room. If you noticed the onConnection method has the id argument, this argument includes the ID (name \ unique string) of the user who joined the server.

Now let's add the Server_lostConnection method:

C#
void Server_lostConnection(string id)
{
    Log.AppendText(id + " disconnected" + 
	Environment.NewLine); //Updates the log textbox when user leaves the room
}

Everytime a user leave the room, your application will go to this method, and will update the log when a user leaves the server.

Let's create the DataReceived method, to handle new messages:

C#
void Server_DataReceived(string ID, byte[] Data)
{
    Log.AppendText(ID + ": " + ConvertBytesToString(Data) + 
	Environment.NewLine); 	//Updates the log when a new message arrived, 
				//converting the Data bytes to a string
}

This is a little bit tricky. Everytime a new message was received, your application will go into this method. If Jack (Jack is the ID) sent us a message saying "Hello!" the Log textbox will get updated with the following text: "Jack: Hello!".

But why does the DataReceived method have the Data variable set to bytes? Why is it not a string? Well, as said, the NetComm class can transfer bytes from client to client, so we use the ConvertBytesToString function. This function is written at the top of this article.

As I mentioned earlier, the client can send messages and receive messages too, in order to send a message, we use the following method:

C#
Server.SendData("Jack", Data); 	//Jack is the ID of the client 
				//we want to send the data to

As mentioned, "Jack" is the ID of the client we want to send the data to. The Data variable is an array of bytes we want to transfer. If you want to transfer a string, you should use the AsciiEncoding class to convert the bytes into a string. Or just keep reading, we will build a function that translates a string into an array of bytes later on.

To send a message to all participating clients, we use the Brodcast method:

C#
Server.Brodcast(Data); //Sends the Data bytes to all participating clients

Again the Data variable is a set of bytes we want to send to each client.

To kick a specific client, we use the DisconnectUser method:

C#
Server.DisconnectUser("Jack"); //Kicks Jack out of the server

To get a list of all connected users, we use the User property:

C#
List<string> usersList = Server.Users; //Gets a list of all the connected clients

Every time you close the application, you should close the Server object connection. On your Form_Closing event handling method, add the following code:

C#
Server.CloseConnection(); //Closes all of the opened connections and stops listening

The Host class (or the Server object we created) has other properties and methods we have not discussed about, but you can always explore the Host class and find out for yourself.

Creating the Client

ClientScreenshot.jpg

The second step is to create the client, if you haven't read the "Creating the host" part of this article, you might consider going back - We will use some of the information we gathered earlier on the client side too.

Add the following variable to your client application global declarations:

C#
NetComm.Client client; //The client object used for the communication

The client variables holds all of the communication handling, events, and methods.

Add the following line to the Form_Load event handling method:

C#
client = new NetComm.Client(); //Initialize the client object

The Client Class Includes the Following Events

  • Connected()
    When the client connected successfully to the host, this event will raise to inform the application.
  • Disconnected()
    When the client got disconnected from the host, this event will raise to inform the application.
  • errEncounter(Execption ex)
    Every time an error occurs, this event will raise to inform the application. The ex argument includes the exception object.
  • DataReceived(byte[] Data, string ID)
    Every time a message arrived, this event will raise to inform the application. The ID argument holds the ID of the client who sent the message. The Data argument holds the array of bytes that client sent.

We need to handle the client object events (Connected, Disconnected, DataReceived, etc.). This allows us to respond to them and update our application according to them, put the following code on the Form_Load event handling method:

C#
//Adding event handling methods for the client
client.Connected += new NetComm.Client.ConnectedEventHandler(client_Connected);
client.Disconnected += new NetComm.Client.DisconnectedEventHandler(client_Disconnected);
client.DataReceived += new NetComm.Client.DataReceivedEventHandler(client_DataReceived);

Now we should add the methods which will respond to those events.

Let's create the Connected method:

C#
void client_Connected()
{
    Log.AppendText("Connected successfully!" + 
	Environment.NewLine); //Updates the log with the current connection state
} 

Every time the client connected successfully, your application will go into this method, then the log textbox will update its text saying "Connected successfully!"

Now we will add the Disconnected method:

C#
void client_Disconnected()
{
    Log.AppendText("Disconnected from host!" + 
	Environment.NewLine); //Updates the log with the current connection state
}

Every time the client got disconnected from the host, your application will get into this method, then the log textbox will update its text saying "Disconnected from host!".

Let's add the DataReceived method:

C#
void client_DataReceived(byte[] Data, string ID)
{
    Log.AppendText(ID + ": " + ConvertBytesToString(Data)  + 
	Environment.NewLine); //Updates the log with the current connection state
}

Every time the client will receive a new data, your application will go into this method, then the log textbox will update its text saying the ID of the client who sent the data and the string of the data. The ID argument holds the ID (unique string) of the client who sent the data. The Data argument is an array of bytes containing the data that client sent.

The ConvertBytesToString function is written at the top of this article.

We need to get the IP address of the host and the ID that the user wants, to do that we create a simple form to gather the information, but only for the purpose of this article, we will use the ID "Jack" and the IP address "localhost", which means the IP address of the computer we are using (we will run the example on the same computer).

Because this is an example, we will put up the communication setup code on the Form_Load event handling method, but in your project you should create a connection form that will gather this information.

Setting up the Connection (Connecting the Host)

C#
//Connecting to the host
client.Connect("localhost", 2020, "Jack"); //Connecting to the host 
			//(on the same machine) with port 2020 and ID "Jack"

If the connection was successfully made, your application should go to the Connected method we built earlier.

Sending Messages is the "main core" of the communication. This is why we needed to create a communication in the first place. Let's say we want to send a string to another client, to do this, we should add a textbox on our project, I named it "ChatMessage". Now create a button, I named it "SendButton". When the user finished writing the message at the "ChatMessage" textbox, he will click on the "SendButton" we created earlier. So we should write this code at the SendButton_Click event handling method:

C#
private void SendButton_Click(object sender, EventArgs e)
{
    client.SendData(ConvertStringToBytes(ChatMessage.Text), "Jack");
}

Every time we will click on the SendButton, we will send a private message to "Jack" with the text appears on the textbox. Remember we need to convert the string into an array of bytes, so we should use the following function:

C#
byte[] ConvertStringToBytes(string str)
{
    return ASCIIEncoding.ASCII.GetBytes(str);
}

Read the explanation at the top of this article to know more about this function.

To send a private message to the host, we simply use the SendData method:

C#
//Sending private message to the host
client.SendData(ConvertStringToBytes(ChatMessage.Text)); 

And finally, to close up the communication, we use the following method on our Form_Closing event handling method:

C#
if (client.isConnected) client.Disconnect(); //Disconnects if the 
			//client is connected, closing the communication thread

Speeding up the Communication

To speed up the communication, you can use the NoDelay, SendBufferSize and ReceiveBufferSize properties. Here is a short code that should speed up the communication:

C#
//Speeding up the connection
Server.SendBufferSize = 400;
Server.ReceiveBufferSize = 50;
Server.NoDelay = True;

Put this code on your Form_Load event handling method. Both host and client should use this code lines in order to make the connection faster.

Making Clients Broadcast Messages

I only now noticed that I haven't had any "Broadcast" method with the Client class. Here is a way of making clients broadcast messages which I haven't yet tested.

As you read, the host is the only one capable of broadcasting messages, in order to make a client broadcast a message we can send the message over to the host (a private message), and tell the host to broadcast that message to everyone. One way is just by adding the following code to the host DataReceived event handling method:

C#
void Server_DataReceived(string ID, byte[] Data)
{
    foreach (string clientID in Server.Users)
    {
        if (ID != clientID) Server.SendData(clientID, Data);
    }
}

In other words (not code "words"), we simulate the host Broadcast method, but this time we ignore the client who sent the message, so he won't receive the message he sent.

The client uses the SendData method to send the message to the host:

C#
client.SendData(Data); 	//This message will be pointed to the host, 
			//then the host will broadcast it

An ID Issue - The "D" Character

When using names like "David" or "Dan", the NetComm library does not work well for some reason. For now, the only solution is to use other IDs that do not start with the "D" character.

Points of Interest

This library was targeted for my school project (a robot battle), but I decided to share it with everyone.
Writing it was fun, and challenging (getting to know the TCP protocol for the first time). I wrote this library to create a communication between 5 computers, one of them is the server which manages the game rules, life points, and more... Other 2 computers control the 2 robots from another room, all of the messages are being transmitted through the NetComm library. Use this library for whatever you want!

History

  • 15.10.2010 - Added the ID issue information, added images
  • 16.10.2010 - Fixed wrong information, added demonstration picture, removed extra information, added a reference link for each class

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer Nemex Studios
Israel Israel
My name is Shay and I'm 21 years old.
At age 16 I created Nemex Studios (www.byshynet.com), an application development "company" (not official).
I'm the developer of "Mouse Recorder Pro", "Mouse Shaker" and many other applications, developed to help the community.

Comments and Discussions

 
GeneralRe: Getting warning mesages about Host.ConvertFromAscii Pin
Ken Gunther20-Aug-12 14:52
Ken Gunther20-Aug-12 14:52 
Questionmy vote of 5 Pin
mohamad yousef4-Jun-12 22:19
mohamad yousef4-Jun-12 22:19 
QuestionProblem Pin
James225529-May-12 5:20
James225529-May-12 5:20 
AnswerRe: Problem Pin
shynet31-May-12 6:32
shynet31-May-12 6:32 
QuestionWorks Great! Pin
Lon Weekly6-Sep-11 2:47
Lon Weekly6-Sep-11 2:47 
AnswerRe: Works Great! Pin
shynet8-Sep-11 3:14
shynet8-Sep-11 3:14 
GeneralMy vote of 5 Pin
Lon Weekly6-Sep-11 2:46
Lon Weekly6-Sep-11 2:46 
QuestionMax sent data size Pin
Member 43110571-Sep-11 8:39
Member 43110571-Sep-11 8:39 
Hi everyone,first of all let me thanks the author for this library Wink | ;)

Then i've a problem,the client/server work fine with small amount of sent data,but with large byte array like 1000 and over , the client/server always receive a package of around 615 byte and lose the rest.
Is there a way to increase the byte package size? Or is necessary to add a custom method to handle it like sending small amount of data and then join them together?

Thanks.
AnswerRe: Max sent data size Pin
shynet1-Sep-11 9:14
shynet1-Sep-11 9:14 
GeneralRe: Max sent data size Pin
Member 43110572-Sep-11 4:38
Member 43110572-Sep-11 4:38 
GeneralRe: Max sent data size Pin
shynet2-Sep-11 5:02
shynet2-Sep-11 5:02 
GeneralRe: Max sent data size Pin
Member 43110575-Sep-11 1:49
Member 43110575-Sep-11 1:49 
GeneralRe: Max sent data size Pin
shynet8-Sep-11 3:14
shynet8-Sep-11 3:14 
SuggestionIt Works Pin
Steven B. Tuttle7-Aug-11 20:13
Steven B. Tuttle7-Aug-11 20:13 
GeneralRe: It Works Pin
shynet11-Aug-11 3:57
shynet11-Aug-11 3:57 
GeneralSending Image Byte causes disconnection Pin
JaxoVB10-Jun-11 6:02
JaxoVB10-Jun-11 6:02 
GeneralRe: Sending Image Byte causes disconnection Pin
shynet11-Jun-11 2:04
shynet11-Jun-11 2:04 
GeneralMy vote of 5 Pin
Karsten Otto24-Apr-11 8:47
Karsten Otto24-Apr-11 8:47 
GeneralNetComm + byte[] data Pin
Member 778531115-Apr-11 13:17
Member 778531115-Apr-11 13:17 
GeneralRe: NetComm + byte[] data Pin
shynet16-Apr-11 22:37
shynet16-Apr-11 22:37 
GeneralRe: NetComm + byte[] data Pin
Member 778531117-Apr-11 6:03
Member 778531117-Apr-11 6:03 
GeneralRe: NetComm + byte[] data Pin
shynet17-Apr-11 22:08
shynet17-Apr-11 22:08 
GeneralRe: NetComm + byte[] data Pin
Member 778531118-Apr-11 3:56
Member 778531118-Apr-11 3:56 
GeneralRe: NetComm + byte[] data Pin
shynet18-Apr-11 21:58
shynet18-Apr-11 21:58 
GeneralConnect to PC in remote LAN Pin
Haryusa12-Apr-11 10:03
Haryusa12-Apr-11 10:03 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.