15 мая 2023 года "Исходники.РУ" отмечают своё 23-летие!
Поздравляем всех причастных и неравнодушных с этим событием!
И огромное спасибо всем, кто был и остаётся с нами все эти годы!

Главная Форум Журнал Wiki DRKB Discuz!ML Помощь проекту


Создание многопотокового клиент-серверного приложения в CBuilder.

Компилятор: C++ Builder 5.x

В этой статье приводится исходный код и описывается процесс создания многопотокового сервера и клиентской программы.  По запросам клиента сервер создаёт потоки и ожидает определённое время, ответа клиента.

Потоки создаваемые сервером, это потомки TServerClientThread, поэтому мы не сможем использовать объект New Thread. Вместо этого будем объявлять поток вручную, наследуя его от TServerClientThread.
 

//---------------------------------------------------------------------------
// *****************         ServerMain.cpp         *****************
//---------------------------------------------------------------------------
//  Requires:   TServerSocket,   TMemo
#include <vcl.h>
#pragma hdrstop
#include "ServerMain.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
// Будем ждать клиента 60
const int CLIENTWAITTIME = 60000;
// Размер буфера для чтения/записи через сокетное соединение
const int BUFFERSIZE = 32;
//---------------------------------------------------------------------------

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

//---------------------------------------------------------------------------
// Вместо использования компоненты клиентского сокета, который помещается на форму
// из Палитры компонентов, поток сервер-клиент должен использовать объект
// TServerClientWinSocket, который создаётся когда ожидающий сокет сервера
// аксептит клиентское соединение.
// Для обработки исключительных ситуаций можно воспользоваться методом HandleException.
void __fastcall TMyServerThread::ClientExecute(void)
{
   // убеждаемся, что соединение активно
   while (!Terminated && ClientSocket->Connected)
   {
      try
      {
         // Теперь ипользуем TWinSocketStream для чтения или записи инфомации
         // через блокирующий сокет
         TWinSocketStream *pStream = new TWinSocketStream(ClientSocket, CLIENTWAITTIME);

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

            // даём клиенту 60 секунд для начала записи
            if (pStream->WaitForData(CLIENTWAITTIME))
            {
               if (pStream->Read(buffer, sizeof(buffer)) == 0)
                  // если не удаётся прочитать через 60 секунд, то закрываем соединение
                  ClientSocket->Close();
               else
               {
                  // Клиент Серверу тестовый текст
                  Form1->Memo1->Lines->Add(AnsiString("(Client) ") +AnsiString(buffer) );

                  // Возвращаем обратно клиенту
                  pStream->Write( buffer, sizeof(buffer));
               }

               // ...
               // Обработка запросов.
               // ...

            }
            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-компоненты
      TServerSocket *ServerSocket1;
   TMemo *Memo1;
      void __fastcall ServerSocket1GetThread(TObject *Sender,
         TServerClientWinSocket *ClientSocket, TServerClientThread *&SocketThread);
   private:       // Пользовательские объявления
   public:        // Пользовательские объявления
      __fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// Потоки в сервере наследуются от TServerClientThread.
// Мы не сможем использовать объект New Thread.
// Вместо этого будем объявлять поток вручную следующим образом:
class PACKAGE TMyServerThread : public Scktcomp::TServerClientThread
{
   public:
      // если true, то перед тем как завершить поток, устанавливаем FreeOnTerminate в false,
      // и поток останется в кэше. А при установке KeepInCache в false,
      // после завершения выполнения потока, он будет удалён.
      __fastcall TMyServerThread(bool CreateSuspended, TServerClientWinSocket* ASocket)
         : Scktcomp::TServerClientThread(CreateSuspended, ASocket)
         { CreateSuspended = false; KeepInCache=true; FreeOnTerminate=false; };

      // Чтобы включить этот поток, переопределяем метод ClientExecute вместо Execute.
      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
//---------------------------------------------------------------------------
// *******************************************************
//---------------------------------------------------------------------------