Click here to Skip to main content
Click here to Skip to main content

MC++ TCP server talking to MFC & MC++ TCP clients

By , 1 Jul 2002
 

Introduction

The original article proposed to demonstrate elementary network programming by designing a very simple SMTP server class. It had no support for sending attachments and was quite uncomplicated in nature. Since there are much better classes available within the BCL for sending emails, the exercise seemed to be quite futile. Therefore I have updated the article so that it provides a decent introduction to network programming. There are already several introductions to network programming on the web and I wanted this to be slightly different from all of them. Whether I was successful in that endeavor is up to the end-readers to judge.

Basically with the advent of .NET, remoting and web services are the preferred means of network communication. Both mechanisms are object oriented and allows the programmer to work from a high level of abstraction. But there are often situations, where you might want to communicate with native programs that were written prior to .NET, or for some reason you cannot expect all clients to be running .NET enabled programs. That's where we can use the slightly lower level network classes for our network-based programs. There is a Socket class available but I prefer to use the TcpListener and TcpClient classes which offer a higher level of abstraction.

In this article we'll design a multi-threaded TCP server using Managed C++. This server is very simple in functionality. It accepts a line of text and returns the reversed version of the same. We'll write an MFC dialog based application that will use the MFC CSocket class to chat with our Managed C++ TCP server. Since we have used basic TCP protocol, it does not matter what language the client program is written in. Just to complete the wheel, I also include a Managed C++ version of the TCP client, which also serves to demonstrate the TcpClient class.

TCP server - MC++

We write two classes here - SpookServer which basically starts our TCP server and listens for connections, and SpookClient which is a class for handling individual client connections. The SpookServer class itself spawns off a new thread in which it waits for connections. And for each connection, it instantiates a SpookClient object which immediately spawns off a new thread to handle the particular client connection. This system of one thread per client is quite acceptable in our case because each TCP chat involves so little process flow. It's just a matter of accepting a line of text, reversing it and sending it back.

The SpookServer class

__gc class SpookServer
{
private:
    TcpListener* lis;
    Thread* t;
    ManualResetEvent* mre;
public:
    SpookServer(int port)
    {   
        mre = new ManualResetEvent(true);
        lis = new TcpListener(port);
        t = new Thread(new ThreadStart(this,&SpookServer::ServerThread));
        t->Start();     
    }
    void CloseServer()
    {
        mre->Reset();
        lis->Stop();
    }
private:
    void ServerThread()
    {
        lis->Start();
        while(mre->WaitOne(100,false))
        {
            try
            {
                TcpClient* c = lis->AcceptTcpClient();
                SpookClient* sc = new SpookClient(c);
            }
            catch(Exception* e)
            {               
            }
                    
        }
    }
};

Well, the class is rather simple, but I'll still give a quick run-through of how it works. We use a ManualResetEvent object for synchronization. This is basically so that the calling class or method has a means of stopping the server anytime, in our case this is _tmain. As you can see, we spawn off a new thread where we listen for connections. This is so that we can return control to the calling class. We use the AcceptTcpClient method of the TcpListener class which returns a TcpClient object that is a reference to the connected client. Immediately we instantiate a new SpookClient object passing the returned TcpClient object to it's constructor. And we go back to AcceptTcpClient, thus allowing a seemingly innumerable number of clients to be able to connect at any one time.

The SpookClient class

__gc class SpookClient
{
private:
    TcpClient* m_c;
    Thread* t;
public:
    SpookClient(TcpClient* c)
    {
        m_c = c;
        t = new Thread(new ThreadStart(this,&SpookClient::StartChat));
        t->IsBackground = true;
        t->Start();
    }
private:
    void StartChat()
    {
        NetworkStream* ns = m_c->GetStream();
        unsigned char buffer __gc[] = new unsigned char __gc[128];
        String* str = "";
        while(true)
        {
            int d = ns->Read(buffer,0,127);
            str = String::Concat(str,Encoding::ASCII->GetString(buffer,0,d));
            if(str->LastIndexOf('\n') != -1)
                break;
        }

        str = str->Trim();
        str = String::Concat("\n",str);     
        buffer = Encoding::ASCII->GetBytes(str);
        Array::Reverse(buffer);
        ns->Write(buffer,0,buffer->Length);     
        ns->Flush();
        ns->Close();
        m_c->Close();
    }
};

The SpookClient class has a TcpClient member and we assign the TcpClient object passed to the constructor to this member object. And we start off a new thread. Inside the thread, we first call TcpClient.GetStream to get the underlying NetworkStream object. Once we have the NetworkStream object we can use Write and Read on it to send and receive data, respectively. We simply accept a line of text, by looping till a \n is encountered and then we reverse it and send it back. Remember to Flush and Close the NetworkStream object and then call Close on the TcpClient object as well.

TCP client - MFC

void CTcpClientMFCDlg::OnBnClickedButton1()
{
    UpdateData(TRUE);
    m_text += "\n";
    CSocket s;
    s.Create();
    if(s.Connect("127.0.0.1",20248))
    {
        s.Send(m_text,m_text.GetLength());
                
        m_text = "";
        char buff[17];
        
        int i;
        
        while(m_text.Find('\n') == -1)
        {
            i=s.Receive(buff,16);
            buff[i]=0;
            m_text += buff;         
        }
        
        m_text.Trim();      
        
        UpdateData(false);      
    }
    s.Close();
}

Well, we don't do anything spectacular here. We simply send the text using CSocket::Send() and receive back the reversed line of text using CSocket::Receive. You'd think that with using network streams and all, the data would have been slightly personalized for .NET purposes, but apparently the raw data is preserved. This indicates that you can use the TcpListener and TcpClient classes for any kind of Tcp programming including popular protocols like HTTP, SMTP, POP3 etc.

TCP client - MC++

__gc class SpookClient
{
public:
    String* Reverse(String* s)
    {
        TcpClient* tl = new TcpClient("127.0.0.1",20248);
        NetworkStream* nw = tl->GetStream();
        unsigned char buffer __gc[] = new unsigned char __gc[128];
        buffer = Encoding::ASCII->GetBytes(s);
        nw->Write(buffer,0,buffer->Length);
        unsigned char retbuffer __gc[] = new unsigned char __gc[128];
        String* str = "";
        while(true)
        {
            int d = nw->Read(retbuffer,0,127);
            str = String::Concat(str,Encoding::ASCII->GetString(retbuffer,0,d));
            if(str->LastIndexOf('\n') != -1)
                break;
        }
        nw->Close();
        tl->Close();
        return str;
    }
};

I have written another class here called SpookClient, not to be confused with the other class in the server program that has the same name. This uses a TcpClient object to connect to the server, gets the underlying NetworkStream using GetStream and uses Write and Read for sending and receiving data, to and from the server. This code snippet would look very similar to our client handler class in the server program because they both demonstrate the use of the NetworkStream class.

License

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

About the Author

Nish Sivakumar
United States United States
Member
Nish is a real nice guy who has been writing code since 1990 when he first got his hands on an 8088 with 640 KB RAM. Originally from sunny Trivandrum in India, he has been living in various places over the past few years and often thinks it’s time he settled down somewhere.
 
Nish has been a Microsoft Visual C++ MVP since October, 2002 - awfully nice of Microsoft, he thinks. He maintains an MVP tips and tricks web site - www.voidnish.com where you can find a consolidated list of his articles, writings and ideas on VC++, MFC, .NET and C++/CLI. Oh, and you might want to check out his blog on C++/CLI, MFC, .NET and a lot of other stuff - blog.voidnish.com.
 
Nish loves reading Science Fiction, P G Wodehouse and Agatha Christie, and also fancies himself to be a decent writer of sorts. He has authored a romantic comedy Summer Love and Some more Cricket as well as a programming book – Extending MFC applications with the .NET Framework.
 
Nish's latest book C++/CLI in Action published by Manning Publications is now available for purchase. You can read more about the book on his blog.
 
Despite his wife's attempts to get him into cooking, his best effort so far has been a badly done omelette. Some day, he hopes to be a good cook, and to cook a tasty dinner for his wife.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralCode Converted From /clr:oldSyntax to /ClrmemberDragynMaster10 Feb '07 - 12:44 
Hi, I took the liberty I hope i had and converted the code for the MC++ server and client so one did not have to use the /clr:oldSyntax (insteed using /Clr). I also made it so the server listend on port 2302 (I just did it because it was the port my program was using atm. :smile: ). I also made the client able to choose the IP to connect to. I think thats all the major stuff i changed at least Big Grin | :-D .
 
MC++ Server
<code>
#include "stdafx.h"
 
#using &lt;mscorlib.dll&gt;
#using &lt;System.dll&gt;
 
#include &lt;tchar.h&gt;
#include &lt;conio.h&gt;
 
using namespace System;
using namespace System::Net::Sockets;
using namespace System::Threading;
using namespace System::Text;
 
ref class SpookClient
{
private:
     TcpClient^ m_c;
     Thread^ t;
public:
     SpookClient(TcpClient^ c)
     {
          m_c = c;
          t = gcnew Thread(gcnew ThreadStart(this,&SpookClient::StartChat));
          t->IsBackground = true;
          t->Start();
     }
private:
     void StartChat()
     {
          NetworkStream^ ns = m_c->GetStream();
          cli::array<unsigned char>^ buffer = gcnew cli::array<unsigned char>(128);
          String^ str = "";
          while(true)
          {
               int d = ns->Read(buffer,0,127);
               str = String::Concat(str,Encoding::ASCII->GetString(buffer,0,d));
               if(str->LastIndexOf('\n') != -1)
                    break;
          }
 
          str = str->Trim();
          str = String::Concat("\n",str);         
          buffer = Encoding::ASCII->GetBytes(str);
          Array::Reverse(buffer);
          ns->Write(buffer,0,buffer->Length);         
          ns->Flush();
          ns->Close();
          m_c->Close();
     }
};
 
ref class SpookServer
{
private:
     TcpListener^ lis;
     Thread^ t;
     ManualResetEvent^ mre;
public:
     SpookServer(int port)
     {    
          mre = gcnew ManualResetEvent(true);
          lis = gcnew TcpListener(port);
          t = gcnew Thread(gcnew ThreadStart(this,&SpookServer::ServerThread));
          t->Start();
     }
     void CloseServer()
     {
          mre->Reset();
          lis->Stop();
     }
private:
     void ServerThread()
     {
          lis->Start();
          while(mre->WaitOne(100,false))
          {
               try
               {
                    TcpClient^ c = lis->AcceptTcpClient();
                    SpookClient^ sc = gcnew SpookClient(c);
               }
               catch(Exception^ e)
               {                   
               }
                        
          }
     }
};
 

int _tmain(void)
{
     SpookServer^ s = gcnew SpookServer(2302);
     Console::WriteLine("Press Any Key to Exit...");
     _getch();
     s->CloseServer();
      return 0;
}
</code>
 
MC++ Client
<code>
#include "stdafx.h"
 
#using &lt;mscorlib.dll&gt;
#using &lt;System.dll&gt;
 
#include &lt;tchar.h&gt;
#include &lt;conio.h&gt;
 
using namespace System;
using namespace System::Net::Sockets;
using namespace System::Text;
 
ref class SpookClient
{
public:
     String^ Reverse(String^ s, String^ IP, int port)
     {
           try {
          TcpClient^ tl = gcnew TcpClient(IP, port);
          NetworkStream^ nw = tl->GetStream();
          cli::array<unsigned char>^ buffer = gcnew cli::array&lt;unsigned char>(128);
          buffer = Encoding::ASCII->GetBytes(s);
          nw->Write(buffer,0,buffer->Length);
          cli::array<unsigned char>^ retbuffer = gcnew cli::array&lt;unsigned char>(128);
          String^ str = "";
          while(true)
          {
               int d = nw->Read(retbuffer,0,127);
               str = String::Concat(str,Encoding::ASCII->GetString(retbuffer,0,d));
               if(str->LastIndexOf('\n') != -1)
                    break;
          }
          nw->Close();
          tl->Close();
          return str;
          }
          catch(System::Net::Sockets::SocketException^ se) {
               return se->Message;
          }
     }
};
 
int _tmain(void)
{
     Console::Write("IP Address: ");
     String^ ip = Console::ReadLine();
     Console::WriteLine("Port: 2302");
     Console::WriteLine("Message: ");
      String^ msg = Console::ReadLine();
     SpookClient^ sc = gcnew SpookClient();
     Console::WriteLine(sc->Reverse ((msg + "\n"), ip, 2302));
     Console::ReadKey(true);
      return 0;
}
</code>
 
As you can probably see i used Visual Studio (2005)...
I have the project file for this but i cant figure out a way to link to them!
So just replace the code in TcpServerDemo and TcpClientDemo from the download on the page with the above code!Big Grin | :-D ALSO: Remember to remove the /clr:oldSyntax and replace it with /Clr!
 
I'm Sorry, I MIGHT write an artical and put in the project files, until then do the above... it should work.
 
-DragynMaster
 
Remember, if everything is okay, something is wrong.
*Salute*
GeneralCSocket Receivememberfahad_iub17 Nov '04 - 0:09 
Cry | :(( Cry | :(( Dear,
 
Actually i am facing a problem with my socket client that its running fine on my development machine , but when I have transfer it to some other machine on the same network, client connects fine but when I send request for the data CSOcket->RECEIVE function returns -1.
 
Any body have idea about this.
fahad_iub@yahoo.com
 
Regards,
 
small
GeneralRe: CSocket ReceivesussAnonymous20 Sep '05 - 2:08 
Dear there are restriction on the server for the specific IP address.
 
Please ask ur server admin to add the ip for that machine
 
Regards
GeneralPOP3 Servermemberdo van sung7 Nov '04 - 3:27 
ban nao co source code cua chuong trinh POP3 Server va client khong?
Hay giup minh voi
 
shyman
QuestionHow about creating a Java ClientmemberSeanV22 Jul '03 - 8:04 
How about creating a Java Client for the MS Chatter Example
AnswerRe: How about creating a Java ClienteditorNishant S22 Jul '03 - 8:20 
SeanV wrote:
How about creating a Java Client for the MS Chatter Example
 
For mostly whimsical reasons I have always stayed away from Java and it has only done me good so far. Therefore I am wholly unsuitable for writing anything in Java. Perhaps someone else can do this (it shouldn't be too difficult I guess)
 
Nish
 

"I'm a bit bored at the moment so I'm thinking about writing a new programming language" - Colin Davies
 
My book :- Summer Love and Some more Cricket [New Win]
Review by Shog9 Click here for review[NW]

GeneralC++/MFCmemberSid Kraft27 Jul '02 - 9:32 
I have a MFC activity which "loads" data through several "edit boxes" generated by the MFC "Wizard" to create a screen for input. Each time I execute the application, I can input my data, using an Updatedata(TRUE) statement after each input to get the data transfered. However, when I call a C++ program from the Dialog application with the input values to get the equation for the curve that "goes" through all of the points, I then want to return to the original screen and "click" on another button on the screen to plot the output. The problem is, when the return from the C++ subroutine is made, the system stops execution. What do I have to add to the dialog program to continue to plot(in another window?)? Please let me know at s.w.kraft@netzero.net. Thanks in advance, Sid Kraft.
GeneralThanks!memberMatt Newman2 Jul '02 - 5:57 
Now I have a reason to actually think about learning MC++. Awesome article!
 
-Suspicious | :suss: Matt Newman / Windows XP ActivistSuspicious | :suss:
-Sonork ID: 100.11179

"You can't seriously believe that you could get away with suing someone over quoting text from a message posted in a public forum, can you?" - John Simmons
GeneralRe: Thanks!subeditorNishant S2 Jul '02 - 6:41 
Matt Newman wrote:
Awesome article!
 
Thanks Matt! You really do read all new articles, don't you! And I also remember you saying that you have VC++ .NET standard!
 
Nish
 

Author of the romantic comedy

Summer Love and Some more Cricket [New Win]

Review by Shog9
Click here for review[NW]

GeneralRe: Thanks!memberMatt Newman2 Jul '02 - 11:17 
Nishant S wrote:
You really do read all new articles, don't you
 
I just read the ones that catch my eye. Your articles are always good so that helps Smile | :)
 
-Suspicious | :suss: Matt Newman / Windows XP ActivistSuspicious | :suss:
-Sonork ID: 100.11179

"You can't seriously believe that you could get away with suing someone over quoting text from a message posted in a public forum, can you?" - John Simmons

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

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 2 Jul 2002
Article Copyright 2001 by Nish Sivakumar
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid