Написание TCP-сервера на WinSock на Borland C++ Builder 6 с возможностью видеть сообщения от клиентов и отправлять им

Гобелен - кликните для возврата на главную

Не прошло и месяца а Фурмановская ЦРБ в очередной раз попала в историю. На этот раз сотрудница клеветала на пациентов, а именно огласку получил случай когда сотрудница назвала пациента алкашём.

Ровно 3 года назад произошло событие, которое подарило жителям планеты Змеля новый чистый праздник 6 апреля - в этот замечательный день земля забрала гнить негодяя и СПАМера Жладимира Вольфовича Жириновского.

Как бы не обстояли дела в области культуры и IT-технологий, Самосвал писал статьи на связанные темы и планирует ещё написать.

Начал разбираться с информацией которая находится в HTTPS клиентском запросе рукопожатия.

Обратите внимание! Объект изображённый на гобилене может отличаться от общепринятого вида описанного объекта. Тут дело в том что художник видит именно так!

104 549 руб.

Описание товара

На форме находится Button1 - кнопка старта сервера, Button2 - кнопка остановки сервера, ListBox1 - список клиентов(которые в настоящий момент подключены к серверу), Memo1 - список сообщений(от клиентов и им), Edit1 - поле ввода сообщения для клиента, Button3 - кнопка отправки сообщения из Edit1 клиенту который выбран в ListBox1, Edit2 - поле для задания порта(применяется в момент старта сервера). TCP-сервер реализауется через WinSock.

Файл Unit1.h

C++
    
#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <winsock2.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:
    TButton *Button1;
    TButton *Button2;
    TListBox *ListBox1;
    TMemo *Memo1;
    TEdit *Edit1;
    TButton *Button3;
    TLabel *Label1;
    TEdit *Edit2;
    TLabel *Label2;
    
    void __fastcall Button1Click(TObject *Sender);
    void __fastcall Button2Click(TObject *Sender);
    void __fastcall Button3Click(TObject *Sender);
    void __fastcall FormCreate(TObject *Sender);
    void __fastcall FormClose(TObject *Sender, TCloseAction &Action);
    void __fastcall ListBox1Click(TObject *Sender);
      
private:
    SOCKET ListenSocket;
    HANDLE ServerThread;
    bool ServerRunning;
    TList* ClientSockets;
    CRITICAL_SECTION csClientList;
    int ServerPort;
    AnsiString LogMessage;
    TStringList* ClientListItems;
    bool EnableSendButton;
    
    void AddToLog(AnsiString msg);
    void UpdateClientList();
    void RemoveClient(SOCKET clientSocket);
    static DWORD WINAPI ServerThreadProc(LPVOID lpParam);
    static DWORD WINAPI ClientThreadProc(LPVOID lpParam);
    void SendToClient(SOCKET clientSocket, AnsiString message);
    bool StartServer(int port);
    void StopServer();
    void __fastcall AddLogToMemo();
    void __fastcall UpdateClientListBox();
    void __fastcall EnableSendButtonProc();
      
public:
    __fastcall TForm1(TComponent* Owner);
    __fastcall ~TForm1();
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif

  

Файл Unit1.cpp

C++
    
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#include <stdio.h>
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
    InitializeCriticalSection(&csClientList);
    ClientSockets = new TList;
    ListenSocket = INVALID_SOCKET;
    ServerThread = NULL;
    ServerRunning = false;
    ServerPort = 12345;
    ClientListItems = new TStringList;
}
//---------------------------------------------------------------------------
      
__fastcall TForm1::~TForm1()
{
    StopServer();
    DeleteCriticalSection(&csClientList);
    delete ClientSockets;
    delete ClientListItems;
}
//---------------------------------------------------------------------------
      
void __fastcall TForm1::FormCreate(TObject *Sender)
{
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    {
        ShowMessage("Ошибка инициализации Winsock");
        Button1->Enabled = false;
        return;
    }
      
    Edit2->Text = "12345";
    Button1->Enabled = true;
    Button2->Enabled = false;
    Button3->Enabled = false;
    AddToLog("Сервер готов к запуску. Введите порт и нажмите 'Старт сервера'");
}
//---------------------------------------------------------------------------
      
void __fastcall TForm1::AddLogToMemo()
{
    Memo1->Lines->Add(LogMessage);
}
//---------------------------------------------------------------------------
      
void TForm1::AddToLog(AnsiString msg)
{
    LogMessage = FormatDateTime("hh:nn:ss", Now()) + " - " + msg;
    TThread::Synchronize(NULL, AddLogToMemo);
}
//---------------------------------------------------------------------------
      
void __fastcall TForm1::UpdateClientListBox()
{
    ListBox1->Items->BeginUpdate();
    ListBox1->Items->Assign(ClientListItems);
    ListBox1->Items->EndUpdate();
}
//---------------------------------------------------------------------------
      
void __fastcall TForm1::EnableSendButtonProc()
{
    Button3->Enabled = EnableSendButton;
}
//---------------------------------------------------------------------------
      
void TForm1::UpdateClientList()
{
    EnterCriticalSection(&csClientList);
    try
    {
        ClientListItems->Clear();
        EnableSendButton = false;
        
        for (int i = 0; i < ClientSockets->Count; i++)
        {
            SOCKET s = (SOCKET)ClientSockets->Items[i];
            sockaddr_in clientAddr;
            int addrLen = sizeof(clientAddr);
            if (getpeername(s, (sockaddr*)&clientAddr, &addrLen) == 0)
            {
                AnsiString clientInfo = inet_ntoa(clientAddr.sin_addr) + AnsiString(":") + IntToStr(ntohs(clientAddr.sin_port));
                ClientListItems->Add(clientInfo);
            }
        }
        
        EnableSendButton = (ClientSockets->Count > 0 && ListBox1->ItemIndex >= 0);
    }
    __finally
    {
        LeaveCriticalSection(&csClientList);
    }
    
    TThread::Synchronize(NULL, UpdateClientListBox);
    TThread::Synchronize(NULL, EnableSendButtonProc);
}
//---------------------------------------------------------------------------
      
bool TForm1::StartServer(int port)
{
    ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (ListenSocket == INVALID_SOCKET)
    {
        AddToLog("Ошибка создания сокета: " + IntToStr(WSAGetLastError()));
        return false;
    }
      
    sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = INADDR_ANY;
    serverAddr.sin_port = htons(port);
      
    if (bind(ListenSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
    {
        AddToLog("Ошибка bind: " + IntToStr(WSAGetLastError()));
        closesocket(ListenSocket);
        ListenSocket = INVALID_SOCKET;
        return false;
    }
      
    if (listen(ListenSocket, SOMAXCONN) == SOCKET_ERROR)
    {
        AddToLog("Ошибка listen: " + IntToStr(WSAGetLastError()));
        closesocket(ListenSocket);
        ListenSocket = INVALID_SOCKET;
        return false;
    }
      
    ServerRunning = true;
    ServerThread = CreateThread(NULL, 0, ServerThreadProc, this, 0, NULL);
    return true;
}
//---------------------------------------------------------------------------
      
void TForm1::StopServer()
{
    if (!ServerRunning) return;
      
    ServerRunning = false;
    
    if (ListenSocket != INVALID_SOCKET)
    {
        closesocket(ListenSocket);
        ListenSocket = INVALID_SOCKET;
    }
      
    if (ServerThread)
    {
        WaitForSingleObject(ServerThread, INFINITE);
        CloseHandle(ServerThread);
        ServerThread = NULL;
    }
      
    EnterCriticalSection(&csClientList);
    for (int i = 0; i < ClientSockets->Count; i++)
    {
        SOCKET s = (SOCKET)ClientSockets->Items[i];
        closesocket(s);
    }
    ClientSockets->Clear();
    LeaveCriticalSection(&csClientList);
}
//---------------------------------------------------------------------------
      
DWORD WINAPI TForm1::ServerThreadProc(LPVOID lpParam)
{
    TForm1* form = (TForm1*)lpParam;
    SOCKET listenSocket = form->ListenSocket;
      
    while (form->ServerRunning)
    {
        sockaddr_in clientAddr;
        int addrLen = sizeof(clientAddr);
        SOCKET clientSocket = accept(listenSocket, (sockaddr*)&clientAddr, &addrLen);
        
        if (clientSocket == INVALID_SOCKET)
        {
            if (form->ServerRunning)
                form->AddToLog("Ошибка accept: " + IntToStr(WSAGetLastError()));
            continue;
        }
      
        EnterCriticalSection(&form->csClientList);
        form->ClientSockets->Add((void*)clientSocket);
        LeaveCriticalSection(&form->csClientList);
      
        AnsiString clientInfo = inet_ntoa(clientAddr.sin_addr) + AnsiString(":") + IntToStr(ntohs(clientAddr.sin_port));
        form->AddToLog("Клиент подключен: " + clientInfo);
        form->UpdateClientList();
      
        form->SendToClient(clientSocket, "Добро пожаловать на сервер! Ваш ID: " + clientInfo);
      
        HANDLE hThread = CreateThread(NULL, 0, ClientThreadProc, (LPVOID)new std::pair<TForm1*, SOCKET>(form, clientSocket), 0, NULL);
        CloseHandle(hThread);
    }
      
    return 0;
}
//---------------------------------------------------------------------------
      
DWORD WINAPI TForm1::ClientThreadProc(LPVOID lpParam)
{
    auto params = (std::pair<TForm1*, SOCKET>*)lpParam;
    TForm1* form = params->first;
    SOCKET clientSocket = params->second;
    delete params;
      
    char buffer[1024];
    int bytesReceived;
      
    while (form->ServerRunning)
    {
        bytesReceived = recv(clientSocket, buffer, sizeof(buffer), 0);
        
        if (bytesReceived <= 0)
        {
            if (bytesReceived == SOCKET_ERROR)
                form->AddToLog("Ошибка чтения от клиента: " + IntToStr(WSAGetLastError()));
            break;
        }
      
        buffer[bytesReceived] = '\0';
        AnsiString message(buffer);
      
        sockaddr_in clientAddr;
        int addrLen = sizeof(clientAddr);
        getpeername(clientSocket, (sockaddr*)&clientAddr, &addrLen);
        AnsiString clientInfo = inet_ntoa(clientAddr.sin_addr) + AnsiString(":") + IntToStr(ntohs(clientAddr.sin_port));
      
        form->AddToLog("[" + clientInfo + "]: " + message);
      
        if (message.LowerCase() == "exit")
        {
            form->SendToClient(clientSocket, "До свидания!");
            break;
        }
      
        form->SendToClient(clientSocket, "ECHO: " + message);
    }
      
    closesocket(clientSocket);
    form->RemoveClient(clientSocket);
      
    form->AddToLog("Клиент отключен");
    form->UpdateClientList();
      
    return 0;
}
//---------------------------------------------------------------------------
      
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    int port = StrToIntDef(Edit2->Text, 0);
    if (port <= 0 || port > 65535)
    {
        AddToLog("Неверный номер порта. Допустимый диапазон: 1-65535");
        return;
    }
      
    ServerPort = port;
    
    if (StartServer(ServerPort))
    {
        Button1->Enabled = false;
        Button2->Enabled = true;
        Edit2->Enabled = false;
        AddToLog("Сервер запущен на порту " + IntToStr(ServerPort));
    }
    else
    {
        AddToLog("Не удалось запустить сервер на порту " + IntToStr(ServerPort));
    }
}
//---------------------------------------------------------------------------
      
void __fastcall TForm1::Button2Click(TObject *Sender)
{
    StopServer();
    
    Button1->Enabled = true;
    Button2->Enabled = false;
    Button3->Enabled = false;
    Edit2->Enabled = true;
    ListBox1->Clear();
    AddToLog("Сервер остановлен");
}
//---------------------------------------------------------------------------
      
void __fastcall TForm1::Button3Click(TObject *Sender)
{
    if (ListBox1->ItemIndex < 0) return;
    
    AnsiString message = Edit1->Text;
    if (message.IsEmpty()) return;
      
    EnterCriticalSection(&csClientList);
    try
    {
        if (ListBox1->ItemIndex >= 0 && ListBox1->ItemIndex < ClientSockets->Count)
        {
            SOCKET clientSocket = (SOCKET)ClientSockets->Items[ListBox1->ItemIndex];
            SendToClient(clientSocket, message);
            
            sockaddr_in clientAddr;
            int addrLen = sizeof(clientAddr);
            getpeername(clientSocket, (sockaddr*)&clientAddr, &addrLen);
            AnsiString clientInfo = inet_ntoa(clientAddr.sin_addr) + AnsiString(":") + IntToStr(ntohs(clientAddr.sin_port));
            
            AddToLog("Сервер -> [" + clientInfo + "]: " + message);
            Edit1->Clear();
        }
    }
    __finally
    {
        LeaveCriticalSection(&csClientList);
    }
}
//---------------------------------------------------------------------------
      
void TForm1::SendToClient(SOCKET clientSocket, AnsiString message)
{
    if (send(clientSocket, message.c_str(), message.Length(), 0) == SOCKET_ERROR)
    {
        AddToLog("Ошибка отправки сообщения: " + IntToStr(WSAGetLastError()));
        RemoveClient(clientSocket);
        UpdateClientList();
    }
}
//---------------------------------------------------------------------------
      
void TForm1::RemoveClient(SOCKET clientSocket)
{
    EnterCriticalSection(&csClientList);
    try
    {
        int index = ClientSockets->IndexOf((void*)clientSocket);
        if (index >= 0)
        {
            ClientSockets->Delete(index);
        }
    }
    __finally
    {
        LeaveCriticalSection(&csClientList);
    }
}
//---------------------------------------------------------------------------
      
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
    StopServer();
    WSACleanup();
}
//---------------------------------------------------------------------------
      
void __fastcall TForm1::ListBox1Click(TObject *Sender)
{
    Button3->Enabled = (ListBox1->ItemIndex >= 0);
}
//---------------------------------------------------------------------------

  

Другие товары

Вы так же можете прочитать следующие статьи: