Навигация:
<< >> Оглавление Указатель
Текст С++

Полный исходный текст на языке C++ для библиотеки (DLL) с моделями блоков, обменивающихся данными по сети, с дополнениями, призванными снизить нагрузку на сеть. Библиотека содержит три модели:

Уже описанные ранее модели Server и NetReceive включены сюда для того, чтобы все модели из рассматриваемого примера схемы находились в одной библиотеке.

Изменения относительно предыдущих версий моделей выделены цветом.

  // Передача данных по сети
  #include <windows.h>
  #include <RdsDef.h>
  // Подготовка описаний сервисных функций
  
  #include <RdsFunc.h>

  //==========  ==========
  int WINAPI ( /*hinst*/,
                           unsigned long reason,
                           void* /*lpReserved*/)
  { if(reason==DLL_PROCESS_ATTACH) // Загрузка DLL
      { // Получение доступа к функциям RDS
        if(!GetInterfaceFunctions())
           // Сообщение: старая версия RDS
      }
    return 1;
  }
  //========= Конец главной функции =========


  //=========================================
  // Модель блока, включающего сервер
  //=========================================
  extern "C" __declspec(dllexport)
    int  Server(int CallMode,
           BlockData,
           ExtParam)
  { int *pConnId;

    switch(CallMode)
      { // Инициализация
        case :
          // Отводим место под личную область данных размером в int
          BlockData->BlockData=pConnId=new int;
          // Запускаем сервер и соединяемся с ним
          *pConnId=(-1, // Порт по умолчанию
                                "ProgrammersGuide.Server", // Канал
                                FALSE); // Не принимаем данные
          break;

        // Очистка
        case :
          // Личная область данных – целое число
          pConnId=(int*)(BlockData->BlockData);
          // Разрываем связь с сервером (он завершится сам)
          (*pConnId);
          // Удаляем личную область
          delete pConnId;
          break;
      }
    return ;
  }
  //=========================================

  //=========================================
  // Личная область данных блоков приема
  // и передачи по сети
  //=========================================
  class TNetSendRcvData
  { public:
      int Mode; // Режим данного блока: прием или передача
        #define NETSRMODE_SENDER    0 // Передатчик
        #define NETSRMODE_RECEIVER  1 // Приемник
      char *ChannelName; // Имя канала
LimitSpeed; // Задан минимальный интервал передачи Delay; // Минимальный интервал в мс
int ConnId; // Идентификатор соединения // Переменные состояния блока-передатчика Connected; // Соединение установлено DataWaiting; // Передача данных отложена
Timer; // Таймер для отсчета интервала WaitingForTimer;// Таймер запущен - ждем LastSendTime; // Время последней отправки
// Функции класса void Connect(void); // Установить соединение void Disconnect(void); // Разорвать соединение void SendValue(double value); // Передать число в канал ReceiveValue( *rcv, // Реакция на double *pOut); // пришедшие данные
void SendArray(void *input); // Передать массив в канал ReceiveArray( *rcv, // Реакция на void *output); // пришедшие данные
void CreateTimer(void); // Создать таймер void DeleteTimer(void); // Удалить таймер CheckSendTimer(void); // Проверить, можно ли передавать, // и запустить таймер, если нельзя
int Setup(char *title); // Функция настройки блока void SaveText(void); // Сохранить параметры void LoadText(char *text); // Загрузить параметры // Конструктор класса TNetSendRcvData(int mode) { ConnId=-1; // Нет соединения Connected=DataWaiting=FALSE;
LimitSpeed=WaitingForTimer=FALSE;Timer=NULL;Delay=100;
ChannelName=NULL; Mode=mode; // Режим передается в параметре конструктора }; // Деструктор класса ~TNetSendRcvData() { Disconnect(); // Разорвать соединение
DeleteTimer(); // Удалить таймер
(ChannelName); // Освободить строку имени канала }; }; //========================================= // Установка соединения void TNetSendRcvData::Connect(void) { char *PrefixedName; // Полное имя канала // Если имя канала пустое, соединение невозможно if(ChannelName==NULL || (*ChannelName)==0) return; // Добавляем префикс к имени канала PrefixedName=("ProgrammersGuide.",ChannelName,FALSE); // Устанавливаем соединение с сервером ConnId=(NULL, // Сервер по умолчанию -1, // Порт по умолчанию PrefixedName,// Имя канала с префиксом Mode==NETSRMODE_RECEIVER); // Прием данных // Освобождаем динамически отведенную строку (PrefixedName);
// Создаем или уничтожаем таймер CreateTimer();
} //========================================= // Разорвать соединение void TNetSendRcvData::Disconnect(void) { if(ConnId!=-1) // Соединение было создано (ConnId); // Разорвать // Сбрасываем переменные состояния ConnId=-1; // Соединения больше нет Connected=FALSE; // Связи тоже больше нет } //=========================================
// Создать таймер void TNetSendRcvData::CreateTimer(void) { if(Mode!=NETSRMODE_SENDER || // Приемнику таймер не нужен (!LimitSpeed) ) // Интервал передачи не ограничивается { DeleteTimer(); // Удаляем таймер, если он создан return; } if(Timer) // Таймер уже создан return; // Создаем таймер Timer=( NULL, // Создается новый таймер 0, // Задержка задается при запуске | , // Однократный FALSE); // Создается остановленным } //=========================================
// Удалить таймер void TNetSendRcvData::DeleteTimer(void) { if(Timer) // Таймер есть { (Timer); Timer=NULL; } WaitingForTimer=FALSE; // Сбрасываем флаг ожидания } //=========================================
// Проверить, можно ли передать данные немедленно, и запустить // таймер, если нельзя BOOL TNetSendRcvData::CheckSendTimer(void) { interval; if(!Connected) // Нет связи с сервером return TRUE; // Разрешаем отправку, чтобы попытка отправки // была зафиксирована в функции SendValue if(!LimitSpeed) // Интервал не ограничивается return TRUE; if(WaitingForTimer) // Уже запустили таймер и ждем срабатывания return FALSE; // Интервал передачи ограничен, таймер сейчас выключен interval=GetTickCount()-LastSendTime; // Время с прошлой отправки if(interval>=Delay) // Прошло много времени – можно передавать return TRUE; // С прошлой отправки прошло менее Delay мс // Нужно подождать (Delay-interval) WaitingForTimer=TRUE; // Взводим флаг ожидания таймера (Timer,Delay-interval); // Запускаем таймер return FALSE; // Передавать сейчас нельзя – ждем таймера } //=========================================
// Передать данные void TNetSendRcvData::SendValue(double value) { if(!Connected) // Нет связи с сервером { // Взводим флаг наличия данных, ожидающих передачи DataWaiting=TRUE; return; } // Связь есть – передаем всем блокам канала (ConnId, // Соединение |, // Флаги 0,NULL, // Не передаем целое число и строку &value, // Указатель на данные sizeof(value)); // Размер данных // Сбрасываем флаг ожидания – мы только что передали данные DataWaiting=FALSE;
// Запоминаем время последней передачи LastSendTime=GetTickCount();
} //========================================= // Прием данных TNetSendRcvData::ReceiveValue( *rcv, // Указатель на структуру с данными double *pOut) // Указатель на выход блока { if(rcv==NULL || pOut==NULL) // Нет одного из указателей return FALSE; // Проверяем, есть ли среди принятых данных двоичные, // и равен ли размер этих данных размеру double if(rcv->Buffer==NULL || rcv->BufferSize!=sizeof(double)) return FALSE; // Нет данных или не совпал размер // Копируем принятое числов pOut memcpy(pOut,rcv->Buffer,sizeof(double)); return TRUE; // Приняты правильные данные } //=========================================
// Передать массив void TNetSendRcvData::SendArray(void *input) { int N; if(!Connected) // Нет связи с сервером { // Взводим флаг наличия данных, ожидающих передачи DataWaiting=TRUE; return; } // Связь есть – определяем размер массива input N=(input)? ((input)*(input)):0; if(N==0) // Массив пуст – передавать нечего return; // Передаем N чисел double всем блокам канала ( ConnId, // Соединение |, // Флаги N, // Целое число – размер массива NULL, // Строка не передается (input), // Начало данных массива N*sizeof(double)); // Размер массива в байтах // Сбрасываем флаг ожидания – мы только что передали данные DataWaiting=FALSE; // Запоминаем время передачи LastSendTime=GetTickCount(); } //=========================================
// Прием массива TNetSendRcvData::ReceiveArray( *rcv, // Указатель на структуру с данными void *output) // Указатель на выход блока (массив) { int N; if(rcv==NULL||output==NULL) // Нет одного из указателей return FALSE; // Проверяем, есть ли среди принятых данных двоичные, // и кратен ли размер блока данных размеру double if(rcv->Buffer==NULL || // Нет буфера с данными rcv->BufferSize%sizeof(double)!=0) // Размер не кратен 8 return FALSE; // Вычисляем число элементов в принятом массиве N=rcv->BufferSize/sizeof(double); // Вычисленное число элементов должно совпасть с переданным if(N!=rcv->Id) return FALSE; // Принято N чисел double – отводим массив под них if(!(output,1,N,FALSE,NULL)) return FALSE; // Копируем принятые данные в отведенный массив выхода memcpy((output),rcv->Buffer,rcv->BufferSize); return TRUE; } //=========================================
// Сохранение параметров блока void TNetSendRcvData::SaveText(void) { ini; // Вспомогательный объект // Создаем вспомогательный объект ini=(TRUE); // Создаем в объекте секцию "[General]" (ini,,0,"General"); // Записываем в эту секцию имя канала (ini,"Channel",ChannelName);
if(Mode==NETSRMODE_SENDER) // Передатчик { // Создаем новую секцию (ini,,0,"Timer"); // Записываем параметры (ini,"On",LimitSpeed); (ini,"Delay",Delay); }
// Сохраняем текст, сформированный объектом, как параметры блока (ini,); // Удаляем вспомогательный объект (ini); } //========================================= // Загрузка параметров блока void TNetSendRcvData::LoadText(char *text) { ini; // Вспомогательный объект char *str; // Создаем вспомогательный объект ini=(TRUE); // Записываем в объект полученный текст с параметрами блока (ini,,0,text); // Начинаем чтение секции "[General]", если она есть if((ini,"General")) // Секция есть { // Освобождаем старое имя канала (ChannelName); ChannelName=NULL; // Получаем у объекта указатель на строку с именем str=(ini,"Channel","",NULL); // Если такая строка есть в тексте, копируем ее в ChannelName if(str) ChannelName=(str); }
if(Mode==NETSRMODE_SENDER && // Передатчик (ini,"Timer")) // Есть секция "[Timer]" { LimitSpeed=(ini,"On",LimitSpeed)!=0; Delay=(ini,"Delay",Delay); }
// Удаляем вспомогательный объект (ini); // Поскольку имя канала могло измениться, соединяемся с // сервером заново Disconnect(); // Разрываем старое соединение Connect(); // Создаем новое } //=========================================
// Настройка параметров блока // // // int TNetSendRcvData::Setup(char *title) { win; // Идентификатор вспомогательного объекта ok; // Пользователь нажал "OK" // Создание окна win=(FALSE,-1,-1,title); // Поле ввода имени канала (win,0,1,, "Имя канала:",200); (win,1,,ChannelName); if(Mode==NETSRMODE_SENDER) { // Для передатчика – ввод интервала (win,0,2, | , "Интервал передачи, мс:",80); // Значение интервала (win,2,,Delay); // Разрешающий флаг поля (win,2,,LimitSpeed); } // Открытие окна ok=(win,NULL); if(ok) { // Пользователь нажал OK char *NewName=(win,1,); if(ChannelName==NULL || strcmp(NewName,ChannelName)!=0) { // Имя канала изменилось – запоминаем новое (ChannelName); ChannelName=(NewName); } // Флаг ограничения интервала и сам интервал LimitSpeed=(win,2,)!=0; Delay=(win,2,); // Устанавливаем соединение с сервером заново и создаем // или удаляем таймер, если необходимо Disconnect(); Connect(); } // Уничтожаем вспомогательный объект-окно (win); // Возвращаемое значение return ok?:; } //=========================================
//========================================= // Блок-передатчик числа //========================================= extern "C" __declspec(dllexport) int NetSend(int CallMode, BlockData, ExtParam) { // Указатель на личную область данных, приведенный к нужному типу TNetSendRcvData *data=(TNetSendRcvData*)(BlockData->BlockData); // #define pStart ((char *)(BlockData->VarTreeData)) #define Start (*((char *)(pStart))) #define Ready (*((char *)(pStart+RDS_VSZ_S))) #define x (*((double *)(pStart+2*RDS_VSZ_S))) switch(CallMode) { // Инициализация case : // Создаем объект класса TNetSendRcvData с // Mode==NETSRMODE_SENDER (передатчик) BlockData->BlockData=new TNetSendRcvData(NETSRMODE_SENDER); break; // Очистка case : delete data; break; // Проверка типов переменных case : return strcmp((char*)ExtParam,"{SSD}")? :; // Связь с сервером установлена case : // Взводим флаг наличия связи data->Connected=TRUE; // Если были данные, ожидающие отправки, // посылаем значение входа блока if(data->DataWaiting) data->SendValue(x); break; // Связь с сервером разорвана case : // Сбрасываем флаг наличия связи data->Connected=FALSE; break; // Запуск расчета case : // Если это – первый запуск после сброса, // передаем значение входа if((()ExtParam)->FirstStart) data->SendValue(x); break;
// Срабатывание таймера case : // Сбрасываем флаг ожидания таймера data->WaitingForTimer=FALSE; // Передаем значение входа блока data->SendValue(x); break;
// Такт расчета case : if(data->CheckSendTimer()) // Можно передавать немедленно data->SendValue(x); // Передаем значение входа break; // Вызов настройки // // // case : return data->Setup("Передача double"); // Сохранение параметров в текстовом виде case : data->SaveText(); break; // Загрузка параметров в текстовом виде case : data->LoadText((char*)ExtParam); break; } return ; // Отмена макроопределений #undef x #undef Ready #undef Start #undef pStart } //========================================= //========================================= // Блок-приемник числа //========================================= extern "C" __declspec(dllexport) int NetReceive(int CallMode, BlockData, ExtParam) { // Указатель на личную область данных, приведенный к нужному типу TNetSendRcvData *data=(TNetSendRcvData*)(BlockData->BlockData); // #define pStart ((char *)(BlockData->VarTreeData)) #define Start (*((char *)(pStart))) #define Ready (*((char *)(pStart+RDS_VSZ_S))) #define y (*((double *)(pStart+2*RDS_VSZ_S))) switch(CallMode) { // Инициализация case : // Создаем объект класса TNetSendRcvData с // Mode==NETSRMODE_RECEIVER (приемник) BlockData->BlockData= new TNetSendRcvData(NETSRMODE_RECEIVER); break; // Очистка case : delete data; break; // Проверка типов переменных case : return strcmp((char*)ExtParam,"{SSD}")? :; // По сети получены данные case : if(data->ReceiveValue((*)ExtParam,&y)) Ready=1; // Если данные верны, взводим флаг готовности // для передачи выхода по связям break; // Вызов настройки // // // case : return data->Setup("Прием double"); // Сохранение параметров в текстовом виде case : data->SaveText(); break; // Загрузка параметров в текстовом виде case : data->LoadText((char*)ExtParam); break; } return ; // Отмена макроопределений #undef y #undef Ready #undef Start #undef pStart } //========================================= //========================================= // Блок-передатчик массива //========================================= extern "C" __declspec(dllexport) int NetSendArray(int CallMode, BlockData, ExtParam) { // Указатель на личную область данных, приведенный к нужному типу TNetSendRcvData *data=(TNetSendRcvData*)(BlockData->BlockData); // #define pStart ((char *)(BlockData->VarTreeData)) #define Start (*((char *)(pStart))) #define Ready (*((char *)(pStart+RDS_VSZ_S))) #define pX ((void **)(pStart+2*RDS_VSZ_S)) switch(CallMode) { // Инициализация case : // Создаем объект класса TNetSendRcvData с // Mode==NETSRMODE_SENDER (передатчик) BlockData->BlockData=new TNetSendRcvData(NETSRMODE_SENDER); break; // Очистка case : delete data; break; // Проверка типов переменных case : return strcmp((char*)ExtParam,"{SSMD}")? :; // Связь с сервером установлена case : // Взводим флаг наличия связи data->Connected=TRUE; // Если были данные, ожидающие отправки, // посылаем значение входа блока if(data->DataWaiting) data->SendArray(pX); break; // Связь с сервером разорвана case : // Сбрасываем флаг наличия связи data->Connected=FALSE; break; // Запуск расчета case : // Если это – первый запуск после сброса, // передаем значение входа if((()ExtParam)->FirstStart) data->SendArray(pX); break; // Срабатывание таймера case : // Сбрасываем флаг ожидания таймера data->WaitingForTimer=FALSE; // Передаем значение входа блока data->SendArray(pX); break; // Такт расчета case : if(data->CheckSendTimer()) // Можно передавать немедленно data->SendArray(pX); // Передаем значение входа break; // Вызов настройки // // // case : return data->Setup("Передача массива"); // Сохранение параметров в текстовом виде case : data->SaveText(); break; // Загрузка параметров в текстовом виде case : data->LoadText((char*)ExtParam); break; } return ; // Отмена макроопределений #undef pX #undef Ready #undef Start #undef pStart } //========================================= //========================================= // Блок-приемник массива //========================================= extern "C" __declspec(dllexport) int NetReceiveArray(int CallMode, BlockData, ExtParam) { // Указатель на личную область данных, приведенный к нужному типу TNetSendRcvData *data=(TNetSendRcvData*)(BlockData->BlockData); // #define pStart ((char *)(BlockData->VarTreeData)) #define Start (*((char *)(pStart))) #define Ready (*((char *)(pStart+RDS_VSZ_S))) #define pY ((void **)(pStart+2*RDS_VSZ_S)) switch(CallMode) { // Инициализация case : // Создаем объект класса TNetSendRcvData с // Mode==NETSRMODE_RECEIVER (приемник) BlockData->BlockData= new TNetSendRcvData(NETSRMODE_RECEIVER); break; // Очистка case : delete data; break; // Проверка типов переменных case : return strcmp((char*)ExtParam,"{SSMD}")? :; // По сети получены данные case : if(data->ReceiveArray((*)ExtParam,pY)) Ready=1; // Если данные верны, взводим флаг готовности // для передачи выхода по связям break; // Вызов настройки // // // case : return data->Setup("Прием массива"); // Сохранение параметров в текстовом виде case : data->SaveText(); break; // Загрузка параметров в текстовом виде case : data->LoadText((char*)ExtParam); break; } return ; // Отмена макроопределений #undef pY #undef Ready #undef Start #undef pStart } //=========================================


<< >> Оглавление Указатель