Tutorial for creating a threaded socket server and client application

By: Christopher Moeller

Abstract: Deriving from TServerClientThread to create a threaded socket server application

Tutorial:  Creating a threaded socket server and client application

This tutorial provides source code and explanations of how to create a threaded
socket server (and a complimentary client application).  The server creates threads
upon the Clients request, and will wait a specified duration during the thread execution
for the Client to respond.  Threads for server connections are descendants of
TServerClientThread and because of this, you cant use the New Thread object dialog.
Instead, you must declare your thread manually as by descending from TServerClientThread.
 

//---------------------------------------------------------------------------
// *****************         ServerMain.cpp        *****************
//---------------------------------------------------------------------------
//  Requires:   TServerSocket,   TMemo
#include <vcl.h>
#pragma hdrstop
#include "ServerMain.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
// Wait 60 seconds for client
const int CLIENTWAITTIME = 60000;
// Size of buffer for reading/writing text across socket connection
const int BUFFERSIZE = 32;
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
   : TForm(Owner)
{
}
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// Instead of using a client socket component that you place in your application
// from the Component palette, the server client thread must use the TServerClientWinSocket
// object that is created when the listening server socket accepts a client connection.
// This is available as the public ClientSocket property. In addition, you can use the
// protected HandleException method rather than writing your own thread-safe exception
// handling.
void __fastcall TMyServerThread::ClientExecute(void)
{
   // make sure connection is active
   while (!Terminated && ClientSocket->Connected)
   {
      try
      {
         // Now, use TWinSocketStream to read or write information
         // over a blocking socket connection
         TWinSocketStream *pStream = new TWinSocketStream(ClientSocket, CLIENTWAITTIME);

         try
         {
            char buffer[BUFFERSIZE];
            memset( buffer, 0, sizeof(buffer) );

            // give the client 60 seconds to start writing
            if (pStream->WaitForData(CLIENTWAITTIME))
            {
               if (pStream->Read(buffer, sizeof(buffer)) == 0)
                  // (if can't read in 60 seconds) than close the connection
                  ClientSocket->Close();
               else
               {
                  // Client to Server test text
                  Form1->Memo1->Lines->Add(AnsiString("(Client) ") +AnsiString(buffer) );

                  // Back again to Client
                  pStream->Write( buffer, sizeof(buffer));
               }

               // ...
               // Process requests here.
               // ...

            }
            else
               ClientSocket->Close();
         }
         __finally
         {
            delete pStream;
         }
      }
      catch (...)
      {
         HandleException();
      }
   }
}
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
void __fastcall TForm1::ServerSocket1GetThread(TObject *Sender,
      TServerClientWinSocket *ClientSocket, TServerClientThread *&SocketThread)
{
   SocketThread = new TMyServerThread(false, ClientSocket);
}
//---------------------------------------------------------------------------
// *******************************************************
//---------------------------------------------------------------------------
 
 

//---------------------------------------------------------------------------
// ******************         ServerMain.h        ******************
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
#ifndef ServerMainH
#define ServerMainH
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <ScktComp.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
   __published: // IDE-managed Components
      TServerSocket *ServerSocket1;
   TMemo *Memo1;
      void __fastcall ServerSocket1GetThread(TObject *Sender,
         TServerClientWinSocket *ClientSocket, TServerClientThread *&SocketThread);
   private:       // User declarations
   public:        // User declarations
      __fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// Threads for server connections are descendants of TServerClientThread.
// Thus, you may not use the New Thread object dialog.
// Instead, declare your thread manually as follows:
class PACKAGE TMyServerThread : public Scktcomp::TServerClientThread
{
   public:
      // if true, FreeOnTerminate is set to false before the thread terminates,
      // and the thread is left in the thread cache. When KeepInCache is false,
      // the thread is freed when execution terminates.
      __fastcall TMyServerThread(bool CreateSuspended, TServerClientWinSocket* ASocket)
         : Scktcomp::TServerClientThread(CreateSuspended, ASocket)
         { CreateSuspended = false; KeepInCache=true; FreeOnTerminate=false; };

      // To implement this thread, you override the ClientExecute method instead of the Execute method.
      void __fastcall ClientExecute(void);
};
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
// *******************************************************
//---------------------------------------------------------------------------
 
 

//---------------------------------------------------------------------------
// *****************         ClientMain.cpp        *****************
//---------------------------------------------------------------------------
//  Requires:  2 TButtons, TMemo, TClientSocket
#include <vcl.h>
#pragma hdrstop
#include "ClientMain.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm2 *Form2;
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
__fastcall TForm2::TForm2(TComponent* Owner)
   : TForm(Owner)
{
}
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
void __fastcall TForm2::Button1Click(TObject *Sender)
{
   if (ClientSocket1->Active)
      ClientSocket1->Active = false;
   if (InputQuery("Computer to connect to", "Address Name:", Server))
   {
      if (Server.Length() > 0)
      {
         ClientSocket1->Host      =  Server;
         ClientSocket1->Active    =  true;
      }
   }
}
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
void __fastcall TForm2::Button2Click(TObject *Sender)
{
   ClientSocket1->Active = false;
}
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
void __fastcall TForm2::ClientSocket1Read(TObject *Sender, TCustomWinSocket *Socket)
{
   if (ClientSocket1->Active == true)
      if (Socket->Connected == true)
         Memo1->Lines->Add( AnsiString("(Server) ") +Socket->ReceiveText() );
}
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
void __fastcall TForm2::ClientSocket1Write(TObject *Sender, TCustomWinSocket *Socket)
{
   if (ClientSocket1->Active == true)
      if (Socket->Connected == true)
         Socket->SendText("This text is passed to and fro");
}
//---------------------------------------------------------------------------
// *******************************************************
//---------------------------------------------------------------------------
 
 

//---------------------------------------------------------------------------
// ******************         ClientMain.h        ******************
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
#ifndef ClientMainH
#define ClientMainH
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <ScktComp.hpp>
//---------------------------------------------------------------------------
class TForm2 : public TForm
{
__published: // IDE-managed Components
   TClientSocket *ClientSocket1;
   TButton *Button1;
   TButton *Button2;
   TMemo *Memo1;
   void __fastcall Button1Click(TObject *Sender);
   void __fastcall Button2Click(TObject *Sender);
   void __fastcall ClientSocket1Read(TObject *Sender,
          TCustomWinSocket *Socket);
   void __fastcall ClientSocket1Write(TObject *Sender,
          TCustomWinSocket *Socket);
private: // User declarations
   AnsiString Server;
public:  // User declarations
   __fastcall TForm2(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm2 *Form2;
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
// *******************************************************
//---------------------------------------------------------------------------


Server Response from: ETNASC04