Introduction
Multithreaded N-N Chat Application is LAN based application. where any number of users can login and take part in chating. This is not a general chat room where a message sent, gets displayed to each person currently loged in. You can send message to only the intended receipent where you are assured that nobody other will get the message, except the main server where you will see all the messages sent through this application.

Background
Here I have tried to show the use of threading and socket programming. If the user has some basic idea of threading and socket programming independant of implementation details it will be helpful to understand the logical sequance.
How to use it?
You will find two folders in the .zip file. One is client and the other is server. Copy this project on the computer which you want to work as server. Copy the client project on all other computers which you want to work as clients. Keep in mind that you can have both server and any number of clients on the same computer too. Now first start the server by pressing the "StartServer" button. Now start the client application. Provide the IP address of the server in the text box named "server". Provide the user name with which you want to login in "Name" textbox. Then press the connect button. You will see your listbox getting filled wait for it to be completed. Once it is over, select any name from the list, write message in the "WriteMessage" box and press send button. At last when you want to get out from the chat click "logout" button rather than directly closing the application by clicking the "close" button.
Using the code
Basicaly the application has some predefined codes which are being passed as header information with each message and are used to identify the type of the message and based on the type, it will be processed differently.
You will be able to see all the code at server as all the messages are being dispalyed at server along with the header information. This codes will be useful for understanding the program flow.
Next, I have one thread continously running and listing at port - 70 for new connection requests and as the client is accepted, one another thread is created dynamicaly at runtime private for that client which will handle all the interaction to that client. That thread will continously read data from that client and send to the server. So, at server we have one main thread accepting the clients and N number of client represantative threads dealing with its client only.
Server maintains the Collection object storing each client at any moment it is little tricky to maintain this list when some client logs out.
At the client application, once the connection is established, one thread is created which continously reads data from the server and processes the data according to the header information. Normaly execution at client will be in this thread only.
This is the main reading thread running at server. Check differnt codes being used and processng according to the codes used.
static void readThrd()
{
int thrdno;
thrdno = curclno;
NetworkStream *readstream = cl->GetStream();
while(1)
{
Byte buff [] = new Byte[100];
int len=0;
try {
len = readstream->
Read(buff,0,buff->Length);
}
catch(Exception *e) {
System::Windows::Forms::MessageBox::Show(e->Message);
}
richTextBox1->Text = String::Concat(richTextBox1->Text,listBox1->Items->get_Item(thrdnos[thrdno]/*thrdno*/)->ToString());
richTextBox1->Text = String::Concat(richTextBox1->Text,S" : "); richTextBox1->Text = String::Concat(richTextBox1->Text,System::Text::Encoding::ASCII->GetString(buff,0,buff->Length));
String *mesg;
String *tmpsub;
tmpsub = S"";
mesg=S"";
mesg = System::Text::Encoding::ASCII->GetString(buff,0,len);
try {
tmpsub = mesg->Substring(0,5);
}
catch(Exception *e) {
System::Windows::Forms::MessageBox::Show(e->Message);
}
if(!String::Compare(tmpsub,S"%%%01")) //Normal Message for other client
{
int index,listboxind;
int length;
index = mesg->IndexOf('$');
String *sub2 = "";
sub2= mesg->Substring(index+1,(mesg->Length-index-1));
try {
if(listBox1->Items->Contains(mesg->Substring(index+1,(mesg->Length-index-1))->ToString()))
listboxind = listBox1->Items->IndexOf(mesg->Substring(index+1,(mesg->Length-index-1))->ToString());
length=0;
length=sub2->Length;
}
catch(Exception *e)
{
continue;
}
IEnumerator *en = sockList->GetEnumerator();
en->MoveNext();
for(int i=0;iMoveNext();
}
NetworkStream *sndthrd = ((TcpClient *)en->Current)->GetStream();
sub2=S"";
sub2 = mesg->Substring(5,(index-5));
String *sndstr = S"";
sndstr = String::Concat(sndstr,listBox1->Items->get_Item(thrdnos[thrdno]/*thrdno*/)->ToString());
sndstr = String::Concat(sndstr,S" : ");
sndstr=String::Concat(sndstr,sub2);
sndthrd->Write(System::Text::Encoding::ASCII->GetBytes(sndstr),0,sndstr->Length);
}
else if(!String::Compare(tmpsub,S"###03")) //Logout request from Client
{
IEnumerator *enmrtr = sockList->GetEnumerator();
NetworkStream *strm;
while (enmrtr->MoveNext())
{
strm= ((TcpClient *) enmrtr->Current)->GetStream();
strm->Write(System::Text::Encoding::ASCII->GetBytes(S"###02"),0,5);
Thread::Sleep(500);
for(int i=0;i < listBox1->Items->Count;i++)
{
if(i == thrdnos[thrdno]/*thrdno*/)
{
//tmpsttr->Finalize();
Thread::Sleep(500);
continue;
}
String *tmpsttr=S"";
tmpsttr = String::Concat(S"###01",listBox1->Items->get_Item(i)->ToString());
strm->Write(System::Text::Encoding::ASCII->GetBytes(tmpsttr),0,tmpsttr->Length);
tmpsttr=S""; tmpsttr->Finalize(); Thread::Sleep(500);
}
}
Items->RemoveAt(thrdnos[thrdno]);
RemoveAt(thrdnos[thrdno]/*thrdno*/);
strmList->RemoveAt(thrdnos[thrdno]/*thrdno*/);
for(int slot= (thrdno+1);slot <= listBox1->Items->Count;slot++)
{
thrdnos[slot] = thrdnos[slot]-1;
}
readstream->Write(System::Text::Encoding::ASCII->GetBytes(S"###09"),0,5);
readstream->Close();
return;
//break;
}
/////////////////////////////
}
}
Server maintains the list of all clients like this...
cl = server->AcceptTcpClient(); strm = cl->GetStream();
sockList->Add(cl);
At client side, This is the main thread....
static void readingthrdcl()
{
while(1)
{
Byte buff[] = new Byte[100];
int len;
try {
len=readingStream->Read(buff,0,buff->Length);
}
catch(Exception *e)
{
System::Windows::Forms::MessageBox::Show(e->Message);
}
String *str;
str = S"";
str = System::Text::Encoding::ASCII->GetString(buff,0,len);
len = str->Length;
String *substr;
substr = System::Text::Encoding::ASCII->GetString(buff,0,5);
int cur;
if(!String::Compare(substr,S"###01"))
{
String *namestr;
namestr = System::Text::Encoding::ASCII->GetString(buff,0,len);
namestr=namestr->Remove(0,5);
listBox1->Items->Add(namestr);
nameList->Add(namestr);
namestr->Finalize();
}
else if(!String::Compare(substr,S"###02"))
{
listBox1->Items->Clear();
}
else if(!String::Compare(substr,S"###09"))
{
readingStream->Close();
//Thread::Abort();
return;
}
richTextBox3->Text = String::Concat(richTextBox3->Text,System::Text::Encoding::ASCII->GetString(buff,0, buff->Length));
buff->Finalize();
}
}
Points of Interest
Here I have used different codes to interprit the type of message like login request, logout request, message sending request. This code's are passed as a header information with each of the message.
Next, when I was writing the application, I was able to create threads, connect to the server, and even synchronize the threads but still I was not able to send message to any person in the login list. Later I found that when you receive a message from the socket in the buffer, it returns the value of number of characters received which I was not storing. So during the time of filling the login list, the generated string of name of loged in person was differing from the name of that person at the server. Because the string was generated from the buffer and as I was not storing the returened value of the read() function of the socket, the generated string's length was just equal to the size of buffer and so differed from the corrosponding string at server and server was not able to pass the sent message to the intended user.