Розробка програмного забезпечення системи збору даних про хід та параметри технологічного процесу

ПОЯСНЮВАЛЬНА ЗАПИСКА

До курсової роботи за дисципліною

Мережі ЕОМ

на тему

Розробка програмного забезпечення системи збору даних про хід та параметри технологічного процесу


Реферат



Метою курсової роботи є розробка програмного забезпечення системи збору даних про хід та 	параметри технологічного процесу, яке складається з таких компонентів:

програмне забезпечення сервера, що приймає дані про хід технологічного процесу і завантажує їх у базу даних для подальшого аналізу, відсилає ці дані ,забезпечує синхронізацію своїх клієнтів;

програмне забезпечення КОМ для імітування ходу технологічного процесу;

програмне забезпечення робочої станції для відображення даних з бази даних.



СЕРВЕР, КЛІЕНТ, ТЕХНОЛОГІЧНИЙ ПРОЦЕС, МЕРЕЖА, БАЗА ДАНИХ, ПАКЕТ, ЗАПИТ, СИНХРОНІЗАЦІЯ


Зміст

	

		Вступ

		Структура та функції системи

		Алгоритми функціонування КОМ

		Алгоритми функціонування серверу

		Алгоритми функціонування робочих станцій

		Програмне забезпечення КОМ

		Програмне забезпечення серверу

		Програмне забезпечення робочих станцій

	Додаток А. Вихідний текст КОМ







1. Вступ



Мета курсового проекту складається в реалізації мережної взаємодії компонентів системи, побудованих за архітектурою «кліент - сервер». Ця архітектура, зокрема, використовується при побудові автоматизованих систем керування (АСК) технологічними процесами (ТП). У системах АСК ТП можна виділити три основних компоненти: керуючі обчислювальні машини (КОМ) або контролери, призначені для безпосереднього керування технологічним процесом; сервер бази даних (СБД), що здійснює зберігання інформації про хід ТП; і робочі станції (РС), що надають користувачам доступ до цієї інформації з метою спостереження за ходом технологічного процесу.

Програмне забезпечення розроблюється для операційної системи Mіcrosoft Wіndows, що реалізує функції вищезгаданих компонентів АСУ ТП. Обмін даними в розроблювальному ПО здійснюється за допомогою бібліотеки сокетов Wіndows (Wіndows Sockets, Wіnsock) з використанням стека протоколів TCP/ІP.


2. Структура та функції системи



Система складається з трьох різних компонентів, кожен з яких реалізує власну функцію і забезпечує вірну роботу системи взагалом. Далі приводиться описання цих компонентів, а на рисунку 1.1 можна побачити структуру системи взагалом. Перший компонент системи – це сервер, який працює з КОМ та робочими станціями. Він синхронізує час з іншими компонентами системи. Якщо сервер працює з КОМ, то приймає повідомлення про хід технологічного процесу від КОМ різних типів, та зберігає отриманні структури у відповідних файлах. Якщо сервер працює з робочою станцією, то відправляє структури даних.

Другий компонент – керуючі обчислювальні машини (КОМ). Це програмне забезпечення, яке імітує хід технологічного прцесу. Тобто десь відбувається технологічний процес (ТП), а КОМ має датчики (у додатку абстрактні) які під’єднані до конкретних контрольних точок і увесь час сповіщають про поточний стан процесів. Це перша функція КОМ. Друга – пересилання отриманих даних з датчиків серверу. Для цього встановлюється з’єднання з сервером і через встановлений у системі проміжок часу поточні дані посилаються серверу, який завантажує їх у структуру. Цей компонент синхронізує свою роботу з сервером. Для коректування часу відправлення даних, він періодично посилає запит серверу про поточний час у системі і якщо він відрізняється від власного годинника, то встановлюється нове, вірне, значення. Всього у системі чотири КОМ, які працюють у парі. Тобто вони об’єднуються у групи по дві машини. Кожна з КОМ в одній групі, отримує значення з однакових датчиків. Це зроблено для того щоб, якщо одна з машин вийде з ладу, друга продовжить відсилати дані серверу. Третій компонент – клієнт, який має засоби для під’єднання до серверу і отримання даних з його бази даних(спеціально створеної структури). Отже для того щоб подивитися про хід ТП треба під’єднатися клієнтом до серверу і запитати потрібну інформацію або про поточний стан процесів або про стан процесів, який був, наприклад, дві години тому по тому. Клієнт також синхронізує свою роботу з сервером.





Рисунок 1 – Структура системи збору інформації про хід технологічного процесу



На рисунку 1 можна бачити, що дві групи КОМ збирають дані і відсилають їх серверу. Сервер у системі, звичайно ж, один, а клієнтів може бути стільки скільки потрібно. Їх розташовано на різних робочіх станціях і можуть бути увімкнені у будь який час, також як і відключені.



3. Алгоритми функціонування КОМ



Як було зазначено у попередньому пункті КОМ об’єднані у групи по дві машини. Кожна з груп отримує дані з датчиків і відсилає їх до серевера. Одна з КОМ може бути відключена і тоді сервер буде приймати дані тільки з увімкненої машини. КОМ працюють абсолютно незалежно одна від одної і не знають про те, що роблять інші. Задача КОМ – отримати дані з датчиків і відіслати їх серверу, періодично синхронізуючись з сервером.

Алгоритм функціонування достатньо простий. Спочатку КОМ під’єднуєьтся до сервера і одразу ж посилає запит про поточне значення часу у системі. Після того як серевер відповість, КОМ коректує свій час і починає посилати серверу інформацію про стан технологічного процесу (дані з датчиків). Дані з датчиків отримуються і посилаються серверу по контрольних точках, які кратні періоду відсилки. У конкретному випадку перша група посилає дані кожні чотирнадцять секунд, а друга – кожні п’ять секунд. У ході роботи КОМ може статися так, що власний показчик часу зіб’ється і буде показувати невірне значення. Для того щоб після цього випадку КОМ не продовжувала роботу з невірним значенням часу, через кожні 10 хвилин відбувається запит у сервера про час у системі.

Схематично алгоритм функціонування зображено на рисунку 2.1.

Усі запити, посилання і отримання даних відбуваються з використанням стандартного формату пакетів, тобто у системі встановлено відомий усім компонетам протокол згідно з яким кожен з них «спілкується» з іншими. Пакет має просту структуру, яка зображена на рисунку 2.2, і включає дві частини: заголовок і безпосередньо дані. У деяких випадках частина даних пакета не потрібна і тоді пакет складається тільки із заголовку.

Якщо з’єднання встановлено, то КОМ продовжує свою роботу, інакше сервер недоступний або існують якісь неполадки у системі. Встановлене з’єднання є тільки мережним, тобто сервер має нового клієнта, а саме хто під’єднався йому ще невідомо. Оскільки КОМ це фактично теж клієнт, виконуючий спеціальну функцію, то серверу необхідно якось ідентифікувати тип клієнта. Для цього у протоколу є спеціальні повідомлення, завдяки яким сервер дізнається хто підєднується до нього. Тому після того як з’єднання встановлене КОМ посилає перший пакет, який є запитом на синхронізацію, де вказан тип відправника. Після цього можна вважати, що підключення повністю встановлене і сервер буде вірно оброблювати дані які вона йому відсилатиме. Далі через встановлений проміжок часу КОМ відсилає данні з датчиків. Пакет тепер складається із заголовку і даних. Код повідомлення говорить, що це нові данні які необхідно додати до бази даних, а поле даних містить чисельні показчики датчиків, номер КОМ з якої прийшов пакет і час у який були зняті дані з датчиків.





Рисунок 2.1 Алгоритм функціонування КОМ






Заголовок



						Обов’язкова частина пакета









Дані





	

	Рисунок 2.2 Структура пакетів

	

Тепер КОМ чекає коли пройде встановлений проміжок часу і знову відсилає нові дані з датчиків.



4. Алгоритми функціонування серверу



При старті серверу одразу ж починається прослуховування вхідних підключень і відбувається відкриття бази даних або якщо вона не існує, то створюється пуста база. Якщо хтось підключився – сервер чекає на перший від нього пакет який ідентифікує тип клієнта (КОМ або звичайний клієнт). Отримавши цю інформацію сервер відзначає це у внутрішньої структурі, що асоційована з поточним клієнтом і далі чекає на запити або дані від клієнтів або КОМ. Також сервер контролює час останнього запиту від клієнта. Якщо клієнт на протязі достатньо довгого часу нічого не відправляв, то клієнт закривається. Спочатку сервер отримує тільки заголовок і тільки потім, проаналізовавши його, отримує дані пакета, якщо потрібно. Отримавши пакет сервер, виходячі із коду повідомлення, що записаний у заголовку, робить відповідні дії.

Сервер робить наступне:

якщо прийшов запит на синхронизацію, то відправляється поточний час сервера;

якщо прийшли дані з КОМ, то сервер виділяє дані з пакету та записує їх у файл;

якщо пришов запит від клієнта, то сервер виконує пошук відповідних структур, та відсилає спочатку їхню кількість, а потім самі пакети.

Алгоритм роботи сервера зображено на рисунку 3.





Рисунок 3 – Алгоритм роботи серверу

	

Рисунок 3 – Алгоритм роботи серверу (продовження)



	

Рисунок 3 – Алгоритм роботи серверу (продовження)



Сервер виконує свої функції паралельно. Він одночасно очікує нових підключень, обробляє отримані дані, відсилає базу даних, контролює таймаути. З моменту старту сервер працює повністю автономно і не потребує втручання зі сторони користувача. База даних сервера складається з двох фалів які розташовано у тієї ж директорії, що і сервер. 


5. Алгоритми функціонування робочих станцій



Для того щоб отримати інформацію про хід технологічного процесу використовується клієнт. Клієнт – це програмне забезпечення, яке встановлюється на робочих станціях і забезпечую підключення до сервера і отримання даних з його бази даних. Клієнт працює виходячи з дій користувача. Спочатку треба ввести адрес серверу та підключитися до нього. При цьому автоматично буде виконано синхронізацію. При відправці пакету на синхронізацію, клієнт повідомить сервер про те, що це робоча станція. 

Після того як підключення встановлене клієнт чекає на подальші запити користувача. Користувач повинен вибрати тип даних, який він бажає отримати, початковий час та кінцевий час. Після цього сервер надішле йому відповідні структури, які відобразяться на екрані користувача. Треба зазначити, що клієнт може відключитись від серверу, ввести адресу іншого серверу та підключитись до нього. Загальний алгоритм роботи клієнта зображено на рисунунку 4.1.



Рисунок 4 – Алгоритм роботи клієнта

	

	6. Програмне забезпечення КОМ



Всього в системі має бути чотири КОМ, кожна з яких працює незалежно від інших. Задача КОМ – збирати інформацію з датчиків про хід технологічного процесу і періодично посилати їх серверу. Датчики виконані у вигляді структури змінних, кожен елемент якої представляє показчик цього датчику. При справжньому технологічному процесі ці датчики приймають якісь значення, у програмному забезпеченні КОМ необхідно імітувати такий процес. Але для можливості аналізу відправника даних, кожен КОМ відправляє фіксоване значення. Інтерфейс програмного забезпечення КОМ зображено на рисунку 5.1.





Рисунок 5.1 – Інтерфейс програмного забезпечення КОМ



Після запуску програми користувач повинен вибрати тип КОМ, адресу та порт серверу. Далі необхідно натиснути кнопку. Після цього кнопка стане неактивною. Програма не буде посилати ніяких повідомлень користувачу. Тому при введені невірної адреси програмне забезпечення буде постійно намагатися приєднатися до серверу. Для виправлення цієї ситуації необхідно перезапустити КОМ.

Програмне забезпечення КОМ виконане у середовищі Borland C++ Builder6. Використовувались блокуючі сокети.

Після ініціалізації даних, створення сокету програма входить у цикл, який завершується лише тоді, коли користувач закриє програмне забезпечення. У цьому циклі у разі будь-якої помилки при роботі з сокетами, сокет КОМ закриється та буде намагатися підключитися до серверу. 



if (bConnect)

{

shutdown(sUvm, SD_BOTH);

closesocket(sUvm);

sUvm = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

if (!(connect(sUvm, (struct sockaddr *)&server,

sizeof(server)) == SOCKET_ERROR))

{

bConnect = 0;

bTimeSync = 1;

}

}//if 

Одразу після підключення КОМ надішле запит на синхронізацію часу.

if ((!bConnect) && (bTimeSync))

{



nLeft = req.lengthPack;

idx = 0;



while(nLeft > 0)

{

ret = send(sUvm, ((char *)&req) + idx, nLeft, 0);

if (ret == SOCKET_ERROR)

{

bConnect = 1;

break;

}

nLeft -= ret;

idx += ret;

}

}//if



Та встновить цей час у системі.

if ((!bConnect) && (bTimeSync))

{

nLeft = 4;

idx = 0;

while(nLeft > 0)

{

ret = recv(sUvm, ((char *)&lastUpdateTime)+idx, nLeft, 0);

if ((ret == SOCKET_ERROR)||(ret == 0))

{

bConnect = 1;

break;

}

nLeft -= ret;

idx += ret;

}

bTimeSync = 0;

// установка времени в системе

//stime(&lastUpdateTime);

}//if

Також буде посилатися запит на синхронізацію через 10 хвилин від часу останнього запиту.

if (!bConnect)

{

if ((time (NULL) - lastUpdateTime) > 600)

bTimeSync = 1;

}//if



КОМ відсилають дані датчиків по контрольних точках. Тому час відправки КОМ однієї групи буде однаковим, навіть якщо один з них був запущеним пізніше, але в рамках однієї контрольної точки часу. 



if (!bConnect)

{

curTime = time(NULL);

if (lastSendTime == curTime)

timeToSleep = period * 1000;

else

timeToSleep = (period-(curTime % period))*1000;

WaitForSingleObject(hEvent, timeToSleep);

}



Після «прокинення» програмне забезпечення відішле поточний час та дані.

if (!bConnect)

{

if (iUVM < 3)

{

//считывание времени и установка параметров

curTime = time(NULL);

pack1.time = curTime;

nLeft = sizeof(pack1);

}

else

{

//считывание датчиков

//считывание времени и установка параметров

curTime = time(NULL);

pack2.time = curTime;

nLeft = sizeof(pack2);

}

lastSendTime = time(NULL);

idx = 0;

while(nLeft > 0)

{

ret = send(sUvm, pPack+idx, nLeft, 0);

if (ret == SOCKET_ERROR)

{

bConnect = 1;

break;

}

nLeft -= ret;

idx += ret;

}

}//if



Програма буде працювати так постійно, доки користувач не закриє форму програми. Ця подія перехопиться і цикл завершиться, також закриються сокети та поток програми.



7. Програмне забезпечення серверу



Інтерейс серверу зображено на рисунку 6.1.





Рисунок 6. 1 – Інтерфейс серверу



Спочатку користувач вводит адрес серверу, а потім запускає його. Після цього кнопка стане неактивованою.

Сервер також використовую блокуючи сокети. Це приводить до того, що необхідно створювати нові потоки для кожної блокуючої функції. Це з одного боку спрощує роботу з самими сонетами, але ускладнює задачу взаємодії потоків, коли вони використовують одні й ті ж змінні, коли один потік закриваює інший. Велика кількість потоків прискорює роботу серверу на багатопроцесорних системах, але загальмовує її на одно процесорних, бо операційній системі необхідно перемикатися між задачами.

Сервер має постійним потік прийому сокетів та контролю таймаутій. Також програма створює новий потік для кожного клієнта.

Алгоритм прийому клієнтів має наступний вигляд:



while (1)

{

sockAccept = accept(sListen, NULL, NULL);

if ((sockAccept == INVALID_SOCKET) && (WSAGetLastError() == WSAENOTSOCK))

break;

EnterCriticalSection (&cs);

if ((sockAccept != INVALID_SOCKET) && (iNumClients < MAX_CLIENT ))

{

sockInfo[iNumClients].sClient = sockAccept;

sockInfo[iNumClients].typeSender = 0;

sockInfo[iNumClients].time = time(NULL);

sockInfo[iNumClients].hClientThread =(HANDLE) _beginthreadex(NULL, 0,

ClientThread, (LPVOID)iNumClients, 0, &iThreadId);

sockInfo[iNumClients].time = time(NULL);

iNumClients ++ ;

LeaveCriticalSection (&cs);

}

else

{

LeaveCriticalSection (&cs);

shutdown(sockAccept, SD_BOTH);

closesocket(sockAccept);

}

}

return 0;



Алгоритм контролю часу клієнтів працює в залежності від типу клієнта. Якщо це КОМ, то timeout дорівнює 1 хвилині, інакше це 10 хвилин.



while (1)

{

dwWaitState = WaitForSingleObject(hEvent, timeToSleep);

if (dwWaitState == WAIT_OBJECT_0)

break;

EnterCriticalSection (&cs);

for (i = 0; i< iNumClients; i++)

{

if ((sockInfo[i].typeSender == 5) || (sockInfo[i].typeSender == 0))

{

if ((time(NULL) - sockInfo[i].time)>600)

{

DeleteSockInfo(i , 1);

}

}

else

{

if ((time(NULL) - sockInfo[i].time)>60)

{

DeleteSockInfo(i , 1);

}

} 

}//for

LeaveCriticalSection (&cs);

}//while

return 0;



Треба зазначити, що сокети клієнтів можуть закриватися при таймауті, при закритті серверу. Тому є спеціальний масив char bAlowCloseClient[MAX_CLIENT] , який указує потоку клієнта, що не потрібно видаляти сокет, бо це буде зроблено іншим модулем програми. В іншому разі, якщо виникла звичайна помилка, то потік клієнта самостійно закриє цей сокет. 

Модуль обробки клієнта приймає запити та відповідно отриманих даних виконує дії. Сервер приймає запит req у якому знаходиться тип запиту, ідентифікатор клієнту та довина запросу.



nLeft = sizeof(request);

idx = 0;

while(nLeft > 0)

{

ret = recv(sockInfo[nClient].sClient, ((char *) &req)+idx, nLeft, 0);

if ((ret == SOCKET_ERROR)||(ret == 0))

{

bError = 1;

break;

}

nLeft -= ret;

idx += ret;

}



Необхідно звернути увагу на те, як повідомлення закриття програми обробляється у сервері. Це важливо, бо необхідно зарити усі потоки, та видалити усі динамічні змінні.



void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)

{

int i;

if (!bServWorking)

return;

// Закрытие потока Control и события ожидания

SetEvent (hEvent);

WaitForSingleObject(hThreadControl, INFINITE);

CloseHandle (hThreadControl);

CloseHandle (hEvent);

//закрытие сокета сервера и потока Accept

shutdown(sListen, SD_BOTH);

closesocket(sListen);

WaitForSingleObject(hThreadAccept, INFINITE);

CloseHandle (hThreadAccept);



//закрытие сокетов клиентов -> закрытие потоков

EnterCriticalSection (&cs);

for (i = 0; i<iNumClients; i++)

{

bAlowCloseClient[i] = 1;

shutdown(sockInfo[i].sClient, SD_BOTH);

closesocket(sockInfo[i].sClient);

WaitForSingleObject(sockInfo[i].hClientThread, INFINITE);

CloseHandle (sockInfo[i].hClientThread);

}

LeaveCriticalSection (&cs);



fclose(f1);

fclose(f2);

DeleteCriticalSection (&cs);

DeleteCriticalSection (&csFile);

WSACleanup();

}



8. Програмне забезпечення робочих станцій



Інтерфейс робочої станції зображено на рисунку 7. 1.





Рисунок 7. 1 – Інтерфейс робочої станції



Спочатку користувач повинен ввести адресу та порт серверу та натиснути відповідну кнопку. При помилці з’явиться відповідне повідомлення та процедуру необхідно буде повторити. Також користувач сам може відсоєдинитись від сервера, натиснувши кнопку. Якщо помилка не з’явилась, то програма ще й синхронізує клієнта з сервером. Процес синхронізації такий ж самий як і в КОМ.



if (connect(sClient, (struct sockaddr *)&server,

sizeof(server)) == SOCKET_ERROR)

{

Application->MessageBoxA("Неудалось подключится к серверу", "Error!", MB_OK);

Form1->Button1->Enabled = TRUE;

return;

}



req.typeRequest = 0; //запрос на синхронизацию

req.typeSender = 5; //идентификатор рабочей станции

req.lengthPack = sizeof(request);

//отправка запроса на синхронизацию времени

nLeft = req.lengthPack;

idx = 0;

while(nLeft > 0)

{

ret = send(sClient, ((char *)&req) + idx, nLeft, 0);

if (ret == SOCKET_ERROR)

{

Application->MessageBoxA("Ошибка отправки запроса на синхронизацию", "Error!", MB_OK);

CloseClientSocket();

return;

}

nLeft -= ret;

idx += ret;

}

//приём времени сервера и установка его в системе

nLeft = sizeof(time_t);

idx = 0;

while(nLeft > 0)

{

ret = recv(sClient, ((char *)&UpdateTime)+idx, nLeft, 0);

if ((ret == SOCKET_ERROR)||(ret == 0))

{

Application->MessageBoxA("Ошибка приёма времени", "Error!", MB_OK);

CloseClientSocket();

return;

}

nLeft -= ret;

idx += ret;

}

// установка времени в системе

stime(&UpdateTime);



Далі програма очікує введення даних: типу КОМ, початкового та кінцевого часів. Після введення необхідно натиснути кнопку відправки запиту. Спочатку програма преобразує час. Якщо виникне якась помилка з часом, то з’явиться відповідне повідомлення.



//формирование структур времени

tmBegin.tm_sec = Form1->ComboBox3->ItemIndex;

tmBegin.tm_min = Form1->ComboBox2->ItemIndex;

tmBegin.tm_hour = Form1->ComboBox6->ItemIndex;

tmBegin.tm_mday = Form1->ComboBox8->ItemIndex+1;

tmBegin.tm_mon = Form1->ComboBox10->ItemIndex;

tmBegin.tm_year = Form1->ComboBox12->ItemIndex+70;

tmBegin.tm_isdst = 0;

time_tBegin = mktime(&tmBegin);

if (time_tBegin == -1)

{

Application->MessageBoxA("Ошибка преобразования начального времени", "Error!", MB_OK);

Form1->Button3->Enabled = TRUE;

return;

}



tmEnd.tm_sec = Form1->ComboBox5->ItemIndex;

tmEnd.tm_min = Form1->ComboBox4->ItemIndex;

tmEnd.tm_hour = Form1->ComboBox7->ItemIndex;

tmEnd.tm_mday = Form1->ComboBox9->ItemIndex+1;

tmEnd.tm_mon = Form1->ComboBox11->ItemIndex;

tmEnd.tm_year = Form1->ComboBox13->ItemIndex+70;

tmEnd.tm_isdst = 0;

time_tEnd = mktime(&tmEnd);

if (time_tBegin == -1)

{

Application->MessageBoxA("Ошибка преобразования конечного времени", "Error!", MB_OK);

Form1->Button3->Enabled = TRUE;

return;

}

if (time_tEnd < time_tBegin)

{

Application->MessageBoxA("Конечное время меньше начального", "Error!", MB_OK);

Form1->Button3->Enabled = TRUE;

return;

}

Далі програма відправить запит та надасть отримані дані або повідомить про відсутність даних у цьому діапазоні.

reqRange.typeRequest = 2;

reqRange.typeSender = 5;

reqRange.lengthPack = sizeof (requestRange);

reqRange.iTypeUVM = iTypeUVM;

reqRange.tBegin = time_tBegin;

reqRange.tEnd = time_tEnd;

nLeft = reqRange.lengthPack;

idx = 0;

while(nLeft > 0)

{

ret = send(sClient, ((char *)&reqRange) + idx, nLeft, 0);

if (ret == SOCKET_ERROR)

{

Application->MessageBoxA("Ошибка отправки запроса на приём данных", "Error!", MB_OK);

CloseClientSocket();

return;

}

nLeft -= ret;

idx += ret;

}

nLeft = sizeof(packageServer);

idx = 0;

while(nLeft > 0)

{

ret = recv(sClient, ((char *)&packServ)+idx, nLeft, 0);

if ((ret == SOCKET_ERROR)||(ret == 0))

{

Application->MessageBoxA("Ошибка приёма количества данных", "Error!", MB_OK);

CloseClientSocket();

return;

}

nLeft -= ret;

idx += ret;

}

if (packServ.AmountPacks == 0)

{

Application->MessageBoxA("Нет данных в заданном диапазоне времени", "Error!", MB_OK);

Form1->Button3->Enabled = TRUE;

return;

}

StringGrid1->RowCount = packServ.AmountPacks+1;

for (i = 0; i<packServ.AmountPacks; i++)

{

if (iTypeUVM == 1)

nLeft = sizeof(package1);

else

nLeft = sizeof(package2);

idx = 0;

while(nLeft > 0)

{

if (iTypeUVM == 1)

ret = recv(sClient, ((char *)&pack1)+idx, nLeft, 0);

else

ret = recv(sClient, ((char *)&pack2)+idx, nLeft, 0);

if ((ret == SOCKET_ERROR)||(ret == 0))

{

Application->MessageBoxA("Ошибка приёма структуры данных", "Error!", MB_OK);

CloseClientSocket();

return;

}

nLeft -= ret;

idx += ret;

}//while

Додаток А. Вихідний текст КОМ



//---------------------------------------------------------------------------

#include <time.h>

#include <vcl.h>

#pragma hdrstop

#pragma argsused

#include <winsock2.h>



#include "UVM.h"

//---------------------------------------------------------------------------

#pragma package(smart_init)

#pragma resource "*.dfm"

#include <process.h>



typedef unsigned char uc;

typedef unsigned short int ui;

#pragma pack (push)

#pragma pack (1)

typedef struct

{

uc typeRequest;

uc typeSender;

uc lengthPack;



time_t time;

uc speed1;

uc speed2;

ui temperature1;

ui temperature2;

ui temperature3;

ui weight1;

ui weight2;

ui weight3;

uc length1;

uc length2;

} package1;

typedef struct

{

uc typeRequest;

uc typeSender;

uc lengthPack;



time_t time;

uc speed1;

uc speed2;

uc speed3;

ui temperature1;

ui temperature2;

ui weight1;

ui weight2;

ui weight3;

ui weight4;

uc length;

} package2;

typedef struct

{

uc typeRequest; //0 - запрос на синхронизацию

//1 - данные от УВМ

//2 - запрос от клиента



uc typeSender; //1..4 номер УВМ

//5 клиент

uc lengthPack;

} request;



#pragma pack (pop)

TForm1 *Form1;





SOCKET sUvm;

HANDLE hEvent, hThreadUVM;

char bEndThread = 0;

//---------------------------------------------------------------------------

__fastcall TForm1::TForm1(TComponent* Owner)

: TForm(Owner)

{

}

//---------------------------------------------------------------------------

unsigned __stdcall UVMThread(LPVOID lpParam)

{

WSADATA wsd;

struct sockaddr_in server;

AnsiString sAddress, sPort, sNumUVM;

char * cAddress, * cPort, * cNumUVM;

char cBufTime[4], i;

unsigned int lenAdr, lenPort, iPort, iUVM,

idx, nLeft, ret;

uc bConnect, bTimeSync;

uc period;

request req;

package1 pack1 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

package2 pack2 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

char * pPack;

time_t curTime, lastUpdateTime, lastSendTime = 0;

DWORD timeToSleep;



Form1->Button1->Enabled = FALSE;

//создание события для временного прерывания работы программы

hEvent = CreateEvent( NULL, FALSE, FALSE, NULL);

// преобразование адреса

sAddress = Form1->Edit2->Text;

cAddress = sAddress.c_str();

lenAdr = sAddress.Length();

cAddress[lenAdr] = '\0';



//преобразование порта

sPort = Form1->Edit3->Text;

cPort = sPort.c_str();

lenPort = sPort.Length();

cPort[lenPort] = '\0';

iPort = atoi(cPort);



//преобразование номера УВМ

sNumUVM = Form1->ComboBox1->Text;

cNumUVM = sNumUVM.c_str();

cNumUVM[1] = '\0';

iUVM = atoi(cNumUVM);

//формирование постоянных значений структур

req.typeRequest = 0;

req.typeSender = iUVM;

req.lengthPack = sizeof(request);

if (iUVM == 2)

{

pack1.weight1 = 0xFFFF;

}

if (iUVM == 4)

{

pack2.weight1 = 0xFFFF;

}

if (iUVM <3)

{

period = 14;

pack1.typeRequest = 1;

pack1.typeSender = iUVM;

pack1.lengthPack = sizeof(pack1);

pPack = (char *)&pack1;

}

else

{

period = 5;

pack2.typeRequest = 1;

pack2.typeSender = iUVM;

pack2.lengthPack = sizeof(pack2);

pPack = (char *)&pack2;

}



if (WSAStartup(MAKEWORD(2,2), &wsd) != 0)

{

return 0;

}

//создание сокета

sUvm = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

if (sUvm == INVALID_SOCKET)

{

return 0;

}

server.sin_family = AF_INET;

server.sin_port = htons(iPort);

server.sin_addr.s_addr = inet_addr(cAddress);



if (server.sin_addr.s_addr == INADDR_NONE)

{

// Если не указан IP-адрес

return 0;

}

//подсоединение к серверу





if (connect(sUvm, (struct sockaddr *)&server,

sizeof(server)) == SOCKET_ERROR)

bConnect = 1;

else

bConnect = 0;



bTimeSync = 1;

while (!bEndThread)

{

//подключение к серверу

if (bConnect)

{

shutdown(sUvm, SD_BOTH);

closesocket(sUvm);

sUvm = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

if (!(connect(sUvm, (struct sockaddr *)&server,

sizeof(server)) == SOCKET_ERROR))

{

bConnect = 0;

bTimeSync = 1;

}

}//if

//отправка запроса на синхронизацию времени

if ((!bConnect) && (bTimeSync))

{



nLeft = req.lengthPack;

idx = 0;



while(nLeft > 0)

{

ret = send(sUvm, ((char *)&req) + idx, nLeft, 0);

if (ret == SOCKET_ERROR)

{

bConnect = 1;

break;

}

nLeft -= ret;

idx += ret;

}

}//if



//приём времени сервера и установка его в системе

if ((!bConnect) && (bTimeSync))

{

nLeft = 4;

idx = 0;

while(nLeft > 0)

{

ret = recv(sUvm, ((char *)&lastUpdateTime)+idx, nLeft, 0);

if ((ret == SOCKET_ERROR)||(ret == 0))

{

bConnect = 1;

break;

}

nLeft -= ret;

idx += ret;

}

bTimeSync = 0;

// установка времени в системе

//stime(&lastUpdateTime);

}//if



//остановка работы до контрольной точки отсылки

if (!bConnect)

{

curTime = time(NULL);

if (lastSendTime == curTime)

timeToSleep = period * 1000;

else

timeToSleep = (period-(curTime % period))*1000;

WaitForSingleObject(hEvent, timeToSleep);

}



//считывание датчиков и текущего времени, отправка пакета

if (!bConnect)

{

if (iUVM < 3)

{ 

//считывание времени и установка параметров

curTime = time(NULL);

pack1.time = curTime;

nLeft = sizeof(pack1);

}

else

{ 

//считывание времени и установка параметров

curTime = time(NULL);

pack2.time = curTime;

nLeft = sizeof(pack2);

}

lastSendTime = time(NULL);

idx = 0;

while(nLeft > 0)

{

ret = send(sUvm, pPack+idx, nLeft, 0);

if (ret == SOCKET_ERROR)

{

bConnect = 1;

break;

}

nLeft -= ret;

idx += ret;

}

}//if



//проверка необходимости на повторную синхронизацию

if (!bConnect)

{

if ((time (NULL) - lastUpdateTime) > 600)

bTimeSync = 1;

}//if

}//while

WSACleanup();

CloseHandle(hEvent);

// Form1->Button1->Enabled = TRUE;

return 0;

}

//---------------------------------------------------------------------------



void __fastcall TForm1::Button1Click(TObject *Sender)

{

UINT iThreadId;

int i = 2;



Form1->Button1->Enabled = FALSE;

hThreadUVM = (HANDLE)_beginthreadex(NULL, 0, UVMThread, (LPVOID)i, 0,

&iThreadId);

if (hThreadUVM == NULL)

{

return;

}

return;

}

//---------------------------------------------------------------------------

void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)

{

if (sUvm != NULL)

{

bEndThread = 1;

shutdown(sUvm, SD_BOTH);

closesocket(sUvm);

SetEvent (hEvent);

WaitForSingleObject(hThreadUVM, INFINITE);

CloseHandle (hThreadUVM);

}

}

//---------------------------------------------------------------------------


Додаток Б



Вихідний текст серверу



#include <vcl.h>

#pragma hdrstop



#include "Server.h"

#pragma package(smart_init)

#pragma resource "*.dfm"



#include <winsock2.h>

#include <process.h>

#include <time.h>

#include <fcntl.h>

#include <stdio.h>

#include <conio.h>



#define MAX_CLIENT 10

#define timeToSleep 10000

typedef unsigned char uc;

typedef unsigned short int ui;

#pragma pack (push)

#pragma pack (1)

typedef struct

{

time_t time;

uc speed1;

uc speed2;

ui temperature1;

ui temperature2;

ui temperature3;

ui weight1;

ui weight2;

ui weight3;

uc length1;

uc length2;

} package1;

typedef struct

{

time_t time;

uc speed1;

uc speed2;

uc speed3;

ui temperature1;

ui temperature2;

ui weight1;

ui weight2;

ui weight3;

ui weight4;

uc length;

} package2;

typedef struct

{

uc typeRequest; //0 - запрос на синхронизацию

//1 - данные от УВМ

//2 - запрос от клиента

//3 - данные от сервера

uc typeSender; //1..4 номер УВМ

//5 клиент

//6 сервер

uc lengthPack;

} request;

typedef struct

{

ui iTypeUVM;

time_t tBegin;

time_t tEnd;

} requestRange;

typedef struct

{

uc typeRequest;

uc typeSender;

uc lengthPack;

unsigned long AmountPacks;

} packageServer;



typedef struct

{

HANDLE hClientThread;

SOCKET sClient;

time_t time;

uc typeSender;

}SOCKET_INFORMATION;

#pragma pack (pop)

//---------------------------------------------------------------------------

//прототипы функций

void DeleteSockInfo(int, uc);

//---------------------------------------------------------------------------

//глобальные переменные

TForm1 *Form1;



WSADATA wsd;

SOCKET sListen;

HANDLE hThreadAccept, hThreadControl;

HANDLE hEvent;

char bAlowCloseClient[MAX_CLIENT];

BOOL bServWorking = FALSE;



CRITICAL_SECTION csFile;

FILE *f1;

FILE *f2;

time_t lastTimeGroup1 = 0, lastTimeGroup2 = 0;



CRITICAL_SECTION cs;

int iNumClients = 0;

SOCKET_INFORMATION sockInfo[MAX_CLIENT];

//---------------------------------------------------------------------------

unsigned __stdcall ClientThread(LPVOID lpParam)

{

int nClient=(int)lpParam;

unsigned int idx, nLeft, ret, iFound;

request req;

requestRange reqRange;

packageServer packServ;

uc bError = 0, TypeSender;

time_t curTime;

package1 pack1;

package1 * pack1Array;

package2 pack2;

package2 * pack2Array;

long int curPos, endPos, startPos;

unsigned int i, nStructs;



while (!bError)

{

//приём запроса клиента

nLeft = sizeof(request);

idx = 0;

while(nLeft > 0)

{

ret = recv(sockInfo[nClient].sClient, ((char *) &req)+idx, nLeft, 0);

if ((ret == SOCKET_ERROR)||(ret == 0))

{

bError = 1;

break;

}

nLeft -= ret;

idx += ret;

}

//установка типа клиента при получении первого запроса

if ((TypeSender == 0) && (!bError))

{

EnterCriticalSection (&cs);

sockInfo[nClient].typeSender = req.typeSender;

LeaveCriticalSection (&cs);

TypeSender = req.typeSender;

}

//запрос на синхронизацию?

if ((req.typeRequest == 0) && (!bError))

{

EnterCriticalSection (&cs);

sockInfo[nClient].time = time(NULL);

LeaveCriticalSection (&cs);



nLeft = 4;

idx = 0;

curTime = time(NULL);

while(nLeft > 0)

{

ret = send(sockInfo[nClient].sClient, ((char *) &curTime)+idx, nLeft, 0);

if (ret == SOCKET_ERROR)

{

bError = 1;

break;

}

nLeft -= ret;

idx += ret;

}

}//if синхронизация



//приём данных от УВМ

if ((req.typeRequest == 1) && (!bError))

{

EnterCriticalSection (&cs);

sockInfo[nClient].time = time(NULL);

LeaveCriticalSection (&cs);



if (TypeSender < 3)

nLeft = sizeof(package1);

else

nLeft = sizeof(package2);



idx = 0;

while(nLeft > 0)

{

if (TypeSender < 3)

ret = recv(sockInfo[nClient].sClient, ((char *)&pack1)+idx, nLeft, 0);

else

ret = recv(sockInfo[nClient].sClient, ((char *)&pack2)+idx, nLeft, 0);

if ((ret == SOCKET_ERROR)||(ret == 0))

{

bError = 1;

break;

}

nLeft -= ret;

idx += ret;

}//while

//запись полученных данных в файл

if (!bError)

if (TypeSender < 3)

{

EnterCriticalSection (&csFile);

if (pack1.time >= lastTimeGroup1)

{

lastTimeGroup1 = pack1.time;

fwrite((void *)&pack1,sizeof(package1) ,1,f1);

fflush(f1);

}

LeaveCriticalSection (&csFile);

}

else //TypeSender > 3

{

EnterCriticalSection (&csFile);

if (pack2.time >= lastTimeGroup2)

{

lastTimeGroup2 = pack2.time;

fwrite((void *)&pack2,sizeof(package2) ,1,f2);

fflush(f2); 

}

LeaveCriticalSection (&csFile);

}

}//if УВМ



//работа с рабочей станцией

if ((req.typeRequest == 2) && (!bError))

{

//сохранение времени последнего обращения

EnterCriticalSection (&cs);

sockInfo[nClient].time = time(NULL);

LeaveCriticalSection (&cs);

//приём диапазона и типа УВМ

nLeft = sizeof (requestRange);

idx = 0;

while(nLeft > 0)

{

ret = recv(sockInfo[nClient].sClient, ((char *)&reqRange)+idx, nLeft, 0);

if ((ret == SOCKET_ERROR)||(ret == 0))

{

bError = 1;

break;

}

nLeft -= ret;

idx += ret;

}//while

//поиск кол-ва файлов удовлетворяющих диапазону времени

packServ.typeRequest = 3; //данные от сервера

packServ.typeSender = 6; //сервер

packServ.lengthPack = sizeof (packageServer);

packServ.AmountPacks = 0;

EnterCriticalSection (&csFile);

if (reqRange.iTypeUVM == 1)

{

curPos = ftell(f1);

fseek(f1, 0, SEEK_END);

endPos = ftell(f1);

fseek(f1, 0, SEEK_SET);

nStructs = endPos / (sizeof(package1));

iFound = 0;

for (i = 0; i<nStructs; i++)

{

fread((void *)&pack1, sizeof(package1), 1, f1);

if ((pack1.time >=reqRange.tBegin) && ( pack1.time <= reqRange.tEnd))

{

packServ.AmountPacks++;

if (iFound == 0)

{

iFound = 1;

startPos = ftell(f1) - sizeof(package1);

}

}

}//for

fseek(f1, curPos, SEEK_SET);

}

else

{

curPos = ftell(f2);

fseek(f2, 0, SEEK_END);

endPos = ftell(f2);

fseek(f2, 0, SEEK_SET);

nStructs = endPos / (sizeof(package2));

iFound = 0;

for (i = 0; i<nStructs; i++)

{

fread((void *)&pack2, sizeof(package2), 1, f2);

if ((pack2.time >=reqRange.tBegin) && ( pack2.time <= reqRange.tEnd))

{

packServ.AmountPacks++;

if (iFound == 0)

{

iFound = 1;

startPos = ftell(f2) - sizeof(package2);

}

}

}//for

fseek(f2, curPos, SEEK_SET);

}

LeaveCriticalSection (&csFile);



//отсылка количества найденных структур

nLeft = packServ.lengthPack;

idx = 0;

while(nLeft > 0)

{

ret = send(sockInfo[nClient].sClient, ((char *) &packServ)+idx, nLeft, 0);

if (ret == SOCKET_ERROR)

{

bError = 1;

break;

}

nLeft -= ret;

idx += ret;

}

//считывание структур из файлов

if ((iFound != 0)&&(!bError))

{

EnterCriticalSection (&csFile);

if (reqRange.iTypeUVM == 1)

{

pack1Array = new package1[packServ.AmountPacks];

curPos = ftell(f1);

fseek(f1, startPos, SEEK_SET);

for (i = 0; i<packServ.AmountPacks; i++)

fread((void *)&pack1Array[i], sizeof(package1), 1, f1);

fseek(f1, curPos, SEEK_SET); 

}

else

{

pack2Array = new package2[packServ.AmountPacks];

curPos = ftell(f2);

fseek(f2, startPos, SEEK_SET);

for (i = 0; i<packServ.AmountPacks; i++)

fread((void *)&pack2Array[i], sizeof(package2), 1, f2);

fseek(f2, curPos, SEEK_SET);

}

LeaveCriticalSection (&csFile);



//отсылка структур

for (i = 0; i<packServ.AmountPacks; i++)

{

if (reqRange.iTypeUVM == 1)

nLeft = sizeof(package1);

else

nLeft = sizeof(package2);

idx = 0;

while(nLeft > 0)

{

if (reqRange.iTypeUVM == 1)

ret = send(sockInfo[nClient].sClient, ((char *) &pack1Array[i])+idx, nLeft, 0);

else

ret = send(sockInfo[nClient].sClient, ((char *) &pack2Array[i])+idx, nLeft, 0);

if (ret == SOCKET_ERROR)

{

bError = 1;

break;

}

nLeft -= ret;

idx += ret;

}//while

if (bError == 1)

{

idx = 5;

break;

}

}//for

if (reqRange.iTypeUVM == 1)

delete[] pack1Array;

else

delete[] pack2Array; 

}//if iFound

}//if PC

}//while (!bError)



//удаление записи о скруктуре текущего сокете

if (!bAlowCloseClient[nClient])

{

EnterCriticalSection (&cs);

DeleteSockInfo (nClient, 0);

LeaveCriticalSection (&cs);

}

return 0;

}//ClientThread

//---------------------------------------------------------------------------

unsigned __stdcall AcceptThread(LPVOID lpParam)

{

SOCKET sockAccept;



UINT iThreadId;



while (1)

{

sockAccept = accept(sListen, NULL, NULL);

if ((sockAccept == INVALID_SOCKET) && (WSAGetLastError() == WSAENOTSOCK))

break;

EnterCriticalSection (&cs);

if ((sockAccept != INVALID_SOCKET) && (iNumClients < MAX_CLIENT ))

{

sockInfo[iNumClients].sClient = sockAccept;

sockInfo[iNumClients].typeSender = 0;

sockInfo[iNumClients].time = time(NULL);

sockInfo[iNumClients].hClientThread =(HANDLE) _beginthreadex(NULL, 0,

ClientThread, (LPVOID)iNumClients, 0, &iThreadId);

sockInfo[iNumClients].time = time(NULL);

iNumClients ++ ;

LeaveCriticalSection (&cs);

}

else

{

LeaveCriticalSection (&cs);

shutdown(sockAccept, SD_BOTH);

closesocket(sockAccept);

}

}

return 0;

} // AcceptThread()

//---------------------------------------------------------------------------

unsigned __stdcall ControlThread(LPVOID lpParam)

{

int i;

DWORD dwWaitState;



while (1)

{

dwWaitState = WaitForSingleObject(hEvent, timeToSleep);

if (dwWaitState == WAIT_OBJECT_0)

break;

EnterCriticalSection (&cs);

for (i = 0; i< iNumClients; i++)

{

if ((sockInfo[i].typeSender == 5) || (sockInfo[i].typeSender == 0))

{

if ((time(NULL) - sockInfo[i].time)>600)

{

DeleteSockInfo(i , 1);

}

}

else

{

if ((time(NULL) - sockInfo[i].time)>60)

{

DeleteSockInfo(i , 1);

}

} 

}//for

LeaveCriticalSection (&cs);

}//while

return 0;

} // ControlThread()

//---------------------------------------------------------------------------

__fastcall TForm1::TForm1(TComponent* Owner)

: TForm(Owner)

{

}

//---------------------------------------------------------------------------

void __fastcall TForm1::Button1Click(TObject *Sender)

{

struct sockaddr_in server;

UINT iThreadId;

AnsiString sAddress, sPort;

char * cAddress, * cPort;

unsigned int lenAdr, lenPort, iPort;



Form1->Button1->Enabled = FALSE;

//создание или открытие файлов

hEvent = CreateEvent( NULL, FALSE, FALSE, NULL);



if ((f1=fopen("group1.txt","a+b"))==NULL)

{

Form1->Button1->Enabled = TRUE;

return;

}

fseek (f1, 0, SEEK_END);

if ((f2=fopen("group2.txt","a+b"))==NULL)

{

fclose(f1);

Form1->Button1->Enabled = TRUE;

return;

}

fseek (f2, 0, SEEK_END);



if (WSAStartup(MAKEWORD(2,2), &wsd) != 0)

{

fclose(f1);

fclose(f2);

Form1->Button1->Enabled = TRUE;

return;

}

//создание необходимых событий, секций

hEvent = CreateEvent( NULL, FALSE, FALSE, NULL);

InitializeCriticalSection (&cs);

InitializeCriticalSection (&csFile);



//создание сокета

sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

if (sListen == INVALID_SOCKET)

{

CloseHandle (hEvent);

fclose(f1);

fclose(f2);

DeleteCriticalSection (&cs);

DeleteCriticalSection (&csFile);

Form1->Button1->Enabled = TRUE;

return;

}

// преобразование адреса

sAddress = Form1->Edit2->Text;

cAddress = sAddress.c_str();

lenAdr = sAddress.Length();

cAddress[lenAdr] = '\0';

//преобразование порта

sPort = Form1->Edit3->Text;

cPort = sPort.c_str();

lenPort = sPort.Length();

cPort[lenPort] = '\0';

iPort = atoi(cPort);



server.sin_family = AF_INET;

server.sin_port = htons(iPort);

server.sin_addr.s_addr = inet_addr(cAddress);



if (server.sin_addr.s_addr == INADDR_NONE)

{

CloseHandle (hEvent);

shutdown(sListen, SD_BOTH);

closesocket(sListen);

fclose(f1);

fclose(f2);

DeleteCriticalSection (&cs);

DeleteCriticalSection (&csFile);

Form1->Button1->Enabled = TRUE;

return;

}



if (bind(sListen, (SOCKADDR*) &server, sizeof(server)) == SOCKET_ERROR)

{

CloseHandle (hEvent);

shutdown(sListen, SD_BOTH);

closesocket(sListen);

fclose(f1);

fclose(f2);

DeleteCriticalSection (&cs);

DeleteCriticalSection (&csFile);

Form1->Button1->Enabled = TRUE;

return;

}

if (listen(sListen, 8) == SOCKET_ERROR)

{

CloseHandle (hEvent);

shutdown(sListen, SD_BOTH);

closesocket(sListen);

fclose(f1);

fclose(f2);

DeleteCriticalSection (&cs);

DeleteCriticalSection (&csFile);

Form1->Button1->Enabled = TRUE;

return;

}

//создание потока accept

hThreadAccept = (HANDLE)_beginthreadex(NULL, 0, AcceptThread, (LPVOID)sListen, 0,

&iThreadId);

if (hThreadAccept == NULL)

{

CloseHandle (hEvent);

shutdown(sListen, SD_BOTH);

closesocket(sListen);

fclose(f1);

fclose(f2);

DeleteCriticalSection (&cs);

DeleteCriticalSection (&csFile);

Form1->Button1->Enabled = TRUE;

return;

}

//создание потока отслеживания времени последнего обращени клиентов

hThreadControl = (HANDLE)_beginthreadex(NULL, 0, ControlThread, (LPVOID)sListen, 0,

&iThreadId);

if (hThreadControl == NULL)

{

return;

}

bServWorking = TRUE;

return;



}

//---------------------------------------------------------------------------



void DeleteSockInfo(int num, uc bWaitThread)

{

int i;

bAlowCloseClient[num] = 1;

shutdown(sockInfo[num].sClient, SD_BOTH);

closesocket(sockInfo[num].sClient);



if (bWaitThread)

WaitForSingleObject(sockInfo[num].hClientThread, INFINITE);

CloseHandle (sockInfo[num].hClientThread);

bAlowCloseClient[num] = 0;

for (i = num; i<(iNumClients-1); i++)

sockInfo[i] = sockInfo[i+1];

iNumClients--;

}

//---------------------------------------------------------------------------



void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)

{

int i;

if (!bServWorking)

return;

// Закрытие потока Control и события ожидания

SetEvent (hEvent);

WaitForSingleObject(hThreadControl, INFINITE);

CloseHandle (hThreadControl);

CloseHandle (hEvent);

//закрытие сокета сервера и потока Accept

shutdown(sListen, SD_BOTH);

closesocket(sListen);

WaitForSingleObject(hThreadAccept, INFINITE);

CloseHandle (hThreadAccept);



//закрытие сокетов клиентов -> закрытие потоков

EnterCriticalSection (&cs);

for (i = 0; i<iNumClients; i++)

{

bAlowCloseClient[i] = 1;

shutdown(sockInfo[i].sClient, SD_BOTH);

closesocket(sockInfo[i].sClient);

WaitForSingleObject(sockInfo[i].hClientThread, INFINITE);

CloseHandle (sockInfo[i].hClientThread);

}

LeaveCriticalSection (&cs);



fclose(f1);

fclose(f2);

DeleteCriticalSection (&cs);

DeleteCriticalSection (&csFile);

WSACleanup();

}



Додаток В. Вихідний текст робочої станції



//---------------------------------------------------------------------------



#include <vcl.h>

#pragma hdrstop



#include "Client.h"



#include <time.h>

#include <winsock2.h>

//---------------------------------------------------------------------------

#pragma package(smart_init)

#pragma resource "*.dfm"

TForm1 *Form1;



typedef unsigned char uc;

typedef unsigned short int ui;

#pragma pack (push)

#pragma pack (1)

typedef struct

{

time_t time;

uc speed1;

uc speed2;

ui temperature1;

ui temperature2;

ui temperature3;

ui weight1;

ui weight2;

ui weight3;

uc length1;

uc length2;

} package1;

typedef struct

{

time_t time;

uc speed1;

uc speed2;

uc speed3;

ui temperature1;

ui temperature2;

ui weight1;

ui weight2;

ui weight3;

ui weight4;

uc length;

} package2;

typedef struct

{

uc typeRequest; //0 - запрос на синхронизацию

//1 - данные от УВМ

//2 - запрос от клиента

//3 - данные от севера

uc typeSender; //1..4 номер УВМ

//5 клиент

//6 сервер

uc lengthPack;

} request;

typedef struct

{

uc typeRequest;

uc typeSender;

uc lengthPack;



ui iTypeUVM;

time_t tBegin;

time_t tEnd;

} requestRange;

typedef struct

{

uc typeRequest;

uc typeSender;

uc lengthPack;

unsigned long AmountPacks;

} packageServer;



#pragma pack (pop)



SOCKET sClient;

WSADATA wsd;

//---------------------------------------------------------------------------

void CloseClientSocket()

{

Form1->Button2->Enabled = FALSE;

Form1->Button3->Enabled = FALSE;



shutdown(sClient, SD_BOTH);

closesocket(sClient);



Form1->Button1->Enabled = TRUE;

}

//---------------------------------------------------------------------------

__fastcall TForm1::TForm1(TComponent* Owner)

: TForm(Owner)

{

StringGrid1->RowCount = 2;

StringGrid1->Rows[1]->Clear();



StringGrid1->Cells[0][0] = "№";

StringGrid1->Cells[1][0] = "Год";

StringGrid1->Cells[2][0] = "Месяц";

StringGrid1->Cells[3][0] = "День месяца";

StringGrid1->Cells[4][0] = "Часы";

StringGrid1->Cells[5][0] = "Минуты";

StringGrid1->Cells[6][0] = "Секунды";

}

//---------------------------------------------------------------------------

void __fastcall TForm1::Button1Click(TObject *Sender)

{

struct sockaddr_in server;

AnsiString sAddress, sPort;

char * cAddress, * cPort;

unsigned int lenAdr, lenPort, iPort;

int nLeft, idx, ret;

request req;

time_t UpdateTime;



Form1->Button1->Enabled = FALSE;

// преобразование адреса

sAddress = Form1->Edit1->Text;

cAddress = sAddress.c_str();

lenAdr = sAddress.Length();

cAddress[lenAdr] = '\0';

//преобразование порта

sPort = Form1->Edit2->Text;

cPort = sPort.c_str();

lenPort = sPort.Length();

cPort[lenPort] = '\0';

iPort = atoi(cPort);



if (WSAStartup(MAKEWORD(2,2), &wsd) != 0)

{

Application->MessageBoxA("Ошибка WSAStartup!", "Error!", MB_OK);

Form1->Button1->Enabled = TRUE;

return;

}

//создание сокета

sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

if (sClient == INVALID_SOCKET)

{

Application->MessageBoxA("Невозможно создать сокет", "Error!", MB_OK);

Form1->Button1->Enabled = TRUE;

return;

}

server.sin_family = AF_INET;

server.sin_port = htons(iPort);

server.sin_addr.s_addr = inet_addr(cAddress);



if (server.sin_addr.s_addr == INADDR_NONE)

{

// Если не указан IP-адрес

Application->MessageBoxA("Напрвальный IP адрес", "Error!", MB_OK);

Form1->Button1->Enabled = TRUE;

return;

}

if (connect(sClient, (struct sockaddr *)&server,

sizeof(server)) == SOCKET_ERROR)

{

Application->MessageBoxA("Неудалось подключится к серверу", "Error!", MB_OK);

Form1->Button1->Enabled = TRUE;

return;

}



req.typeRequest = 0; //запрос на синхронизацию

req.typeSender = 5; //идентификатор рабочей станции

req.lengthPack = sizeof(request);

//отправка запроса на синхронизацию времени

nLeft = req.lengthPack;

idx = 0;

while(nLeft > 0)

{

ret = send(sClient, ((char *)&req) + idx, nLeft, 0);

if (ret == SOCKET_ERROR)

{

Application->MessageBoxA("Ошибка отправки запроса на синхронизацию", "Error!", MB_OK);

CloseClientSocket();

return;

}

nLeft -= ret;

idx += ret;

}

//приём времени сервера и установка его в системе

nLeft = sizeof(time_t);

idx = 0;

while(nLeft > 0)

{

ret = recv(sClient, ((char *)&UpdateTime)+idx, nLeft, 0);

if ((ret == SOCKET_ERROR)||(ret == 0))

{

Application->MessageBoxA("Ошибка приёма времени", "Error!", MB_OK);

CloseClientSocket();

return;

}

nLeft -= ret;

idx += ret;

}

// установка времени в системе

//stime(&UpdateTime);

Form1->Button2->Enabled = TRUE;

Form1->Button3->Enabled = TRUE;

}

//---------------------------------------------------------------------------



void __fastcall TForm1::Button2Click(TObject *Sender)

{

CloseClientSocket();

}

//---------------------------------------------------------------------------



void __fastcall TForm1::Button3Click(TObject *Sender)

{

int iTypeUVM, nLeft, idx, ret;

package1 pack1;

package2 pack2;

packageServer packServ;

requestRange reqRange;

tm tmBegin, tmEnd, *tmTime;

time_t time_tBegin, time_tEnd;

unsigned long i;



Form1->Button3->Enabled = FALSE;

StringGrid1->RowCount = 2;

StringGrid1->Rows[1]->Clear();

//формирование структур времени

tmBegin.tm_sec = Form1->ComboBox3->ItemIndex;

tmBegin.tm_min = Form1->ComboBox2->ItemIndex;

tmBegin.tm_hour = Form1->ComboBox6->ItemIndex;

tmBegin.tm_mday = Form1->ComboBox8->ItemIndex+1;

tmBegin.tm_mon = Form1->ComboBox10->ItemIndex;

tmBegin.tm_year = Form1->ComboBox12->ItemIndex+70;

tmBegin.tm_isdst = 0;

time_tBegin = mktime(&tmBegin);

if (time_tBegin == -1)

{

Application->MessageBoxA("Ошибка преобразования начального времени", "Error!", MB_OK);

Form1->Button3->Enabled = TRUE;

return;

}



tmEnd.tm_sec = Form1->ComboBox5->ItemIndex;

tmEnd.tm_min = Form1->ComboBox4->ItemIndex;

tmEnd.tm_hour = Form1->ComboBox7->ItemIndex;

tmEnd.tm_mday = Form1->ComboBox9->ItemIndex+1;

tmEnd.tm_mon = Form1->ComboBox11->ItemIndex;

tmEnd.tm_year = Form1->ComboBox13->ItemIndex+70;

tmEnd.tm_isdst = 0;

time_tEnd = mktime(&tmEnd);

if (time_tBegin == -1)

{

Application->MessageBoxA("Ошибка преобразования конечного времени", "Error!", MB_OK);

Form1->Button3->Enabled = TRUE;

return;

}

if (time_tEnd < time_tBegin)

{

Application->MessageBoxA("Конечное время меньше начального", "Error!", MB_OK);

Form1->Button3->Enabled = TRUE;

return;

}



iTypeUVM = Form1->ComboBox1->ItemIndex + 1;

//Формирование шапки таблицы

if (iTypeUVM == 1)

{

StringGrid1->Cells[7][0] = "Скорость 1";

StringGrid1->Cells[8][0] = "Скорость 2";

StringGrid1->Cells[9][0] = "Темпер. 1";

StringGrid1->Cells[10][0] = "Темпер. 2";

StringGrid1->Cells[11][0] = "Темпер. 3";

StringGrid1->Cells[12][0] = "Вес 1";

StringGrid1->Cells[13][0] = "Вес 2";

StringGrid1->Cells[14][0] = "Вес 3";

StringGrid1->Cells[15][0] = "Длина 1";

StringGrid1->Cells[16][0] = "Длина 2";

}

else

{

StringGrid1->Cells[7][0] = "Скорость 1";

StringGrid1->Cells[8][0] = "Скорость 2";

StringGrid1->Cells[9][0] = "Скорость 3";

StringGrid1->Cells[10][0] = "Темпер. 1";

StringGrid1->Cells[11][0] = "Темпер. 2";

StringGrid1->Cells[12][0] = "Вес 1";

StringGrid1->Cells[13][0] = "Вес 2";

StringGrid1->Cells[14][0] = "Вес 3";

StringGrid1->Cells[15][0] = "Вес 4";

StringGrid1->Cells[16][0] = "Длина";

}



//формирование и отсылка запроса на диапазон времён

reqRange.typeRequest = 2;

reqRange.typeSender = 5;

reqRange.lengthPack = sizeof (requestRange);

reqRange.iTypeUVM = iTypeUVM;

reqRange.tBegin = time_tBegin;

reqRange.tEnd = time_tEnd;



nLeft = reqRange.lengthPack;

idx = 0;

while(nLeft > 0)

{

ret = send(sClient, ((char *)&reqRange) + idx, nLeft, 0);

if (ret == SOCKET_ERROR)

{

Application->MessageBoxA("Ошибка отправки запроса на приём данных", "Error!", MB_OK);

CloseClientSocket();

return;

}

nLeft -= ret;

idx += ret;

}

//приём от сервера кол-ва записей входяих в диапазон

nLeft = sizeof(packageServer);

idx = 0;

while(nLeft > 0)

{

ret = recv(sClient, ((char *)&packServ)+idx, nLeft, 0);

if ((ret == SOCKET_ERROR)||(ret == 0))

{

Application->MessageBoxA("Ошибка приёма количества данных", "Error!", MB_OK);

CloseClientSocket();

return;

}

nLeft -= ret;

idx += ret;

}

if (packServ.AmountPacks == 0)

{

Application->MessageBoxA("Нет данных в заданном диапазоне времени", "Error!", MB_OK);

Form1->Button3->Enabled = TRUE;

return;

}

//приём в цикле записей

StringGrid1->RowCount = packServ.AmountPacks+1;

for (i = 0; i<packServ.AmountPacks; i++)

{

if (iTypeUVM == 1)

nLeft = sizeof(package1);

else

nLeft = sizeof(package2);

idx = 0;

while(nLeft > 0)

{

if (iTypeUVM == 1)

ret = recv(sClient, ((char *)&pack1)+idx, nLeft, 0);

else

ret = recv(sClient, ((char *)&pack2)+idx, nLeft, 0);

if ((ret == SOCKET_ERROR)||(ret == 0))

{

Application->MessageBoxA("Ошибка приёма структуры данных", "Error!", MB_OK);

CloseClientSocket();

return;

}

nLeft -= ret;

idx += ret;

}//while

//отображение полученной записи

StringGrid1->Cells[0][i+1] = i+1;

if (iTypeUVM == 1)

tmTime = localtime(&pack1.time);

else

tmTime = localtime(&pack2.time);

StringGrid1->Cells[1][i+1] = (*tmTime).tm_year+1900;

StringGrid1->Cells[2][i+1] = (*tmTime).tm_mon;

StringGrid1->Cells[3][i+1] = (*tmTime).tm_mday+1;

StringGrid1->Cells[4][i+1] = (*tmTime).tm_hour;

StringGrid1->Cells[5][i+1] = (*tmTime).tm_min;

StringGrid1->Cells[6][i+1] = (*tmTime).tm_sec;

if (iTypeUVM == 1)

{

StringGrid1->Cells[7][i+1] = pack1.speed1;

StringGrid1->Cells[8][i+1] = pack1.speed2;

StringGrid1->Cells[9][i+1] = pack1.temperature1;

StringGrid1->Cells[10][i+1] = pack1.temperature2;

StringGrid1->Cells[11][i+1] = pack1.temperature3;

StringGrid1->Cells[12][i+1] = pack1.weight1;

StringGrid1->Cells[13][i+1] = pack1.weight2;

StringGrid1->Cells[14][i+1] = pack1.weight3;

StringGrid1->Cells[15][i+1] = pack1.length2;

StringGrid1->Cells[16][i+1] = pack1.length2;

}

else

{

StringGrid1->Cells[7][i+1] = pack2.speed1;

StringGrid1->Cells[8][i+1] = pack2.speed2;

StringGrid1->Cells[9][i+1] = pack2.speed3;

StringGrid1->Cells[10][i+1] = pack2.temperature1;

StringGrid1->Cells[11][i+1] = pack2.temperature2;

StringGrid1->Cells[12][i+1] = pack2.weight1;

StringGrid1->Cells[13][i+1] = pack2.weight2;

StringGrid1->Cells[14][i+1] = pack2.weight3;

StringGrid1->Cells[15][i+1] = pack2.weight4;

StringGrid1->Cells[16][i+1] = pack2.length;

}

}//for



Form1->Button3->Enabled = TRUE;

}

//---------------------------------------------------------------------------