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
//---------------------------------------------------------------------------
// *******************************************************
//---------------------------------------------------------------------------