Исходные коды программ и игр

Программирование - работа и хобби

Отправка классов и структур по сети

Язык программирования C++

Обмен объектами классов и структур по сети

При разработке приложений и игр часто возникает необходимость отправки объектов классов и структур по сети. По своей сути классы и структуры С++ представляют из себя практически одно и тоже. Принципы отправки по сети объектов классов и структур полностью идентичны. Поэтому ниже в основном будет речь идти о сетевой работе с объектами классов.

Сам способ отправки по сети объектов классов, а не отдельных переменных, очень удобен. При этом в составе отправляемого объекта могут быть даже объекты других классов и структур. Значения и структура передаваемой информации хранится всего в одном объекте. Сетевой обмен самодостаточными объектами классов унифицирует и сокращает в объеме программный код.

Возможные проблемы передачи объектов по сети

Если в составе класса переменные фиксированного размера сетевой обмен строится просто. Функция sizeof() даёт точное количество байтов занимаемое в памяти классом с переменными-членами фиксированного размера. Сложности возникают при отправках переменных с изменяемыми размерами, например: сообщение произвольной длины. Если в составе отправляемого класса есть объект CString со строкой произвольной длины, то функция sizeof() не даст необходимого результата и при отправке класса по сети возникнут исключительные ошибки.

Указатели на объекты, на массивы содержащие адрес на реальные данные отправлять в составе класса нельзя. Нулевые указатели можно. Если отправить в сеть в составе класса указатель на существующие данные, то неизбежно эти данные будут потеряны. Объекту отправляемого класса принадлежит только указатель, но не данные, на которые ссылается этот указатель.

Приложение для отправки объектов класса по сети

Приложение отправки классов и структур по сетиИсходник содержит программный код приложения отправки объектов класса и структуры по сети. Приложение демонстрирует один из способов отправки классов по сети. Программа имеет оконный интерфейс с элементами редактирования отправляемой информации.

Для обмена информацией по сети приложение использует объект класса CExchange с различными типами переменных и вложенным объектом структуры CAdd. Сетевое подключение построено на классе CMySocket, наследнике класса асинхронных сокетов CAsyncSocket. Полученные по сети переменные-члены объекта CExchange и переменные вложенной структуры выводятся в элементы управления интерфейса, которые позволяют редактировать отправляемые в сеть данные.

Класс обмена данными CExchange

Класс CExchange основной класс для передачи данных по сети. Пересылаемый класс в своем составе имеет члены-переменные различного типа: int, BOOL, Char, объект структуры CAdd.

class CExchange и struct CAdd имеют по одному методу GetSizeText(), вычисляющий размер отправляемого текста в байтах. Методы не влияют на размеры объектов класса и их наличие не вносит никаких особенностей в сетевую работу при передаче данных. Поскольку класс и структура представляются небольшим программным кодом, они объявлены и определены только в заголовочном файле CSend.h

Листинг заголовочного файла:
#pragma once
#include "pch.h"
#include "framework.h"

struct CAdd
{
public:
    static const  int m_lenText = 256;

public:
    TCHAR m_Text[CAdd::m_lenText] = _T("");
    int m_ArrayInts[5] = {};
    BOOL m_Boolean = FALSE;

    int GetSizeText()
    {
        return m_lenText * sizeof(TCHAR);
    }
};


// Класс для обмена данными по сети
class CExchange
{
public:
    static const  int m_lenText = 256;

public:
    TCHAR m_Text[CExchange::m_lenText] = _T("");
    int m_ArrayInts[5] = {};
    BOOL m_Boolean = FALSE;
    CAdd m_objectClass;

    int GetSizeText()
    {
        return m_lenText * sizeof(TCHAR);
    }
};

Класс сокетов CMySocket : public CAsyncSocket

Класс CMySocket имеет минимальное количество программного кода. В данном классе используется принцип замещения базовых функций. Замещаются функции извещения о сетевых событиях: приёма запросов на подключение, соединение клиента с сервером, сообщение о данных для извлечения в буфере, событие отключения сокета.

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

Вырезки программного кода в качестве примера:
// Класс CMySocket

// Переменная указатель для "связи" с родительским окном.
CMFCClassNetworkDlg* m_pParent;
...
// Функция замещающая базовый метод
void CMySocket::OnConnect(int nErrorCode)
{
    // Данные в родительское окно для индикации процесса соединения.
    nErrorCode == 0 ? m_pParent->OnConnect(FALSE) : m_pParent->OnConnect(TRUE);
	
    CAsyncSocket::OnConnect(nErrorCode);
}
...
// Главное окно приложения
...
// Событие подключения вызываемое в главном окне.
void CMFCClassNetworkDlg::OnConnect(BOOL error)
{
    if (error == TRUE)
    {
        MessageBox(L"Попытка подключения отвергнута!");
    }
    else
    {
        SetWindowText(L"Клиент: сеть работает!");
    }	
}
...

Передача по сети переменных фиксированных размеров

Отправка переменных фиксированного размера осуществляется в составе объекта класса или структуры. Для этого при отправке в метод передается указатель на объект с отправляемыми данными и размер объекта измеренный функцией sizeof(). Таким способом безопасно можно отправлять объекты только когда в их составе переменные с фиксированными значениями, например: int, char, BOOL, массив[const int], объекты классов с подобными переменными.

Метод оправки объекта класса в сеть

Переменные, предназначенные для отправки, заполняются значениями из окон редактирования пользовательского интерфейса приложения. При помощи механизма обмена данными DDX, значения из элементов управления CEdit, посредством переменных типа int BOOL CString копируются в отправляемый объект CExchange.

Листинг метода отправки:
void CMFCClassNetworkDlg::OnBnClickedButtonSend()
{
    // При вызове этой функции TRUE данные из оконных элементов диалога 
    // копируются в связанные с ними переменными.
    UpdateData(TRUE);

    CMySocket* pSockSend = NULL;
    if (m_IsServer == TRUE) pSockSend = &m_AcceptSocket;
    if (m_IsClient == TRUE) pSockSend = &m_MainSocket;

    if (pSockSend != NULL)
    {
        CExchange stateSend;
        int sizeSend = sizeof(CExchange);

        // --- Из элементов управления в переменные класса обмена данными по сети ---

        // -- CExchange --
        _tcsncpy_s(stateSend.m_Text, CExchange::m_lenText, m_valueText, _TRUNCATE);

        stateSend.m_ArrayInts[0] = m_valueArrayInts0;
        ...
        stateSend.m_ArrayInts[4] = m_valueArrayInts4;

        stateSend.m_Boolean = m_valueBoolean;

        // -- CExchange::CAdd --
        _tcsncpy_s(stateSend.m_objectClass.m_Text, CAdd::m_lenText, m_valueText2, _TRUNCATE);

        stateSend.m_objectClass.m_ArrayInts[0] = m_valueArrayInts0_2;
        ...
        stateSend.m_objectClass.m_ArrayInts[4] = m_valueArrayInts4_2;

        stateSend.m_objectClass.m_Boolean = m_valueBoolean2;

        // Отправляем объект класса.
        pSockSend->Send(&stateSend, sizeSend);
    }
}

Метод извлечения данных из сетевого буфера

Полученная по сети информация наглядно выводится на интерфейс приложения-приёмника. Можно сравнить отправленные и полученные данные. При корректно написанном программном коде сетевого обмена информация будет передаваться абсолютно точно и без ошибок.

Листинг метода извлечения данных:
// Метод-эхо метода класса CMySocket
void CMFCClassNetworkDlg::OnReceive(BOOL error)
{
    CMySocket* pSockReceive = NULL;
    if (m_IsServer == TRUE) pSockReceive = &m_AcceptSocket;
    if (m_IsClient == TRUE) pSockReceive = &m_MainSocket;

    if (pSockReceive != NULL)
    {
        CExchange stateReceive;
        int sizeReceive = sizeof(CExchange);

        pSockReceive->Receive(&stateReceive, sizeReceive);

        // --- Вывод полученных из сети данных на интерфейс ---

        // -- CExchange --
        m_valueText = stateReceive.m_Text;

        m_valueArrayInts0 = stateReceive.m_ArrayInts[0];
        ...
        m_valueArrayInts4 = stateReceive.m_ArrayInts[4];

        m_valueBoolean = stateReceive.m_Boolean;

        // -- CExchange::CAdd ---
        m_valueText2 = stateReceive.m_objectClass.m_Text;

        m_valueArrayInts0_2 = stateReceive.m_objectClass.m_ArrayInts[0];
        ...
        m_valueArrayInts4_2 = stateReceive.m_objectClass.m_ArrayInts[4];

        m_valueBoolean2 = stateReceive.m_objectClass.m_Boolean;

        // При вызове этой функции с FALSE значения переменных
        // копируются в связанные с ними окнами редактирования.
        UpdateData(FALSE);
    }
}

Исходник приложения

Исходный код приложения написан на языке С++ в среде библиотеки MFC. Исходник представляет собой оконное сетевое приложение для Windows. Сервер и клиента можно тестировать на одном компьютере и по локальной сети на разных устройствах с операционной системой Windows. Инструмент программирования - MS Visual Studio 2019.
Файл: MFCClassNetwork_vs16.zip
Размер: 1981 Кбайт
Загрузки: 103