Создание многопотокового клиент-серверного
приложения в 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
//---------------------------------------------------------------------------
// *******************************************************
//---------------------------------------------------------------------------
|