![]() |
General Programming »
Internet / Network »
Client/Server Development
Intermediate
License: The Code Project Open License (CPOL)
Multithreaded Chat ServerBy SidzoneThis is a simple multithreaded chat server, intended for people to learn Socket programming and Threads in C#. |
C# 2.0, Windows, .NET 2.0VS2005, Dev
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||

I started writing this application to learn Socket programming, Threads, and UI controls in C#. But in the process, I learned more than that. We would go step by step and will try to cover all important aspects of this application. But before we begin, I'd like to set some expectations about the application. This application demonstrates how multiple clients can connect to a single server and communicate to the server. However, at this point, clients cannot communicate with each other. That would be the next step for this application.
What do you need to get started with a multithreaded chat application? Threads and Sockets.
Following is how the application starts and how it works:
Now, let's look at what each thread, that corresponds to a client connect, does:
BeginReceive method. A callback method is passed a parameter. This callback method is called when any data is received on that socket.SocketException is raised, then the connection is closed, as this means that the client has asked to close the connection.Huh! Looks pretty simple. Now, let's look at some concepts in C# that are required to understand the implementation.
.NET framework's Socket class provides a BeginReceive method to receive data asynchronously, i.e., in a non-blocking manner. The BeginReceive method has the following signature:
public IAsyncResult BeginReceive( byte[] buffer, int offset, int size,
SocketFlags socketFlags, AsyncCallback callback, object state );
The way the BeginReceive function works is that, you pass the function a buffer and a callback function (delegate) which will be called whenever data arrives. The callback method is called by the system when data is received on the given socket. This method is called using a separate thread (internally spawned by the system). Hence this operation is asynchronous and non-blocking.
Now, where exactly will be the received data? It will be in the buffer that was passed in the BeginReceive method. But before you read the data, you should know the number of bytes that has been received. This is achieved by calling the EndReceive method of the Socket class.
The BeginReceive call is completed only after the EndReceive method of the Socket. The following code will clear what has been explained in the above paragraphs:
// Create the state object.
StateObject state = new StateObject();
state.workSocket = connectedClient.Client;
//Call Asynchronous Receive Function
connectedClient.Client.BeginReceive(state.buffer, 0,
StateObject.BufferSize, 0,new AsyncCallback(OnReceive), state);
public void OnReceive(IAsyncResult ar)
{
String content = String.Empty;
// Retrieve the state object and the handler socket
// from the asynchronous state object.
StateObject state = (StateObject)ar.AsyncState;
Socket handler = state.workSocket;
int bytesRead;
if (handler.Connected)
{
// Read data from the client socket.
try
{
bytesRead = handler.EndReceive(ar);
if (bytesRead > 0)
{
// There might be more data, so store the data received so far.
state.sb.Remove(0, state.sb.Length);
state.sb.Append(Encoding.ASCII.GetString(
state.buffer, 0, bytesRead));
// Display Text in Rich Text Box
content = state.sb.ToString();
SetText(content);
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize,
0,new AsyncCallback(OnReceive), state);
}
}
catch (SocketException socketException)
{
//WSAECONNRESET, the other side closed impolitely
if (socketException.ErrorCode == 10054 ||
((socketException.ErrorCode != 10004) &&
(socketException.ErrorCode != 10053)))
{
// Complete the disconnect request.
String remoteIP =
((IPEndPoint)handler.RemoteEndPoint).Address.ToString();
String remotePort =
((IPEndPoint)handler.RemoteEndPoint).Port.ToString();
this.owner.DisconnectClient(remoteIP, remotePort);
handler.Close();
handler = null;
}
}
// Eat up exception....Hmmmm I'm loving eat!!!
catch (Exception exception)
{
MessageBox.Show(exception.Message + "\n" + exception.StackTrace);
}
}
}
Answer is, use Delegates.
If you look at the application, The rich text box that displays the chat message is created by the thread that creates the chat dialog box. Now, the chat data in the rich text box is updated by a thread that calls the callback function, OnReceive. This is a system spawned thread!
In order to access it, create a delegate as:
public delegate void SetTextCallback(string s);
Now, create a function to update the rich text box as:
private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.rtbChat.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.rtbChat.SelectionColor = Color.Blue;
this.rtbChat.SelectedText = "\nFriend: "+text;
}
}
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 31 Jan 2007 Editor: Smitha Vijayan |
Copyright 2006 by Sidzone Everything else Copyright © CodeProject, 1999-2009 Web22 | Advertise on the Code Project |