forked from ahyahy/OneScriptMultithreadedTCPServer
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhowworks.html
More file actions
298 lines (273 loc) · 20.5 KB
/
howworks.html
File metadata and controls
298 lines (273 loc) · 20.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
<!DOCTYPE html>
<html lang="ru">
<head>
<title>Какэтоработает</title>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="styleos.css">
<script defer src="mobil.js"></script>
</head>
<body>
<header>
<section>
<div>
<div id="logo"><big><big><big>Многопоточный TCP сервер <br><small><small>для сценарного языка</small></small> OneScript</big></big></big></div>
</div>
<div style="direction: rtl; overflow:hidden; padding: 8px 0px 0px 0px; margin-top:-7px;">
<div id=div22>
<input type="checkbox" id="toggle">
<label id="label1" class="hamburger-icon" onclick="label1click()"><div></div><div></div><div></div></label>
</div>
<nav>
<a onclick="navclick(event)" href="index.html">Главная</a>
<a onclick="navclick(event)" href="screenshot.html">Скриншоты</a>
<a onclick="navclick(event)" href="howworks.html">Как это работает</a>
<a onclick="navclick(event)" href="doc.html">Документация</a>
<a onclick="navclick(event)" href="down.html">Скачать</a>
<a onclick="navclick(event)" href="kont.html">Контакты</a>
<input type="checkbox" id="toggle2">
<a id="mobil" onclick="mobilClick()">`</a>
</nav>
</div>
</section>
</header>
<section class="content">
<p>Рекомендую ознакомиться со статьей на ресурсе infostart.ru - <b>Как решать повседневные задачи с помощью OneScript</b> <a href="https://infostart.ru/public/327581/index.php?ID=327581%3Flogout%3Dyes" target="_blank">здесь</a></p>
<p>Скачайте и установите <b>1Script - язык сценариев для специалистов по 1С</b> (сайт) <a href="http://oscript.io/" target="_blank">здесь</a></p>
<p>
Кроме TCP сервера в библиотеке есть TCP клиент. Он тоже спроектирован под особенности кода односкрипта. Оба могут быть запущены
различными способами, описанными в параграфах
<a href="Mtcps/MtcpsServerVariant.html" target="_blank">Варианты запуска сервера.</a>
и
<a href="Mtcps/MtcpsClientVariant.html" target="_blank">Варианты запуска клиента.</a>
Это дает гибкость при построении клиент-серверного приложения.
</p>
<div class=marginleft40>
<details>
<summary>В этом примере создается эхо-сервер:</summary>
<pre class=code>
<button id="copy1" type="button" style="font-size:100%;">Копировать</button>
<hr style="border-color: lightgray;"><div id="cont1">
// Этот сервер можно запускать для тестирования клиентов.
// В ответ на сообщения клиентов сервер будет пересылать сообщение
// как самому клиенту, так и всем подключенным клиентам.
Перем МС, Сервер;
Процедура ПриПодключенииКлиента() Экспорт
АргументыСобытия = МС.АргументыСобытия;
Сообщить("Клиент подключился: " + АргументыСобытия.ИдентификаторКлиента);
Сообщить("Активных клиентов: " + Сервер.КоличествоАктивныхКлиентов);
Сообщить("========================");
КонецПроцедуры
Процедура ВывестиСообщение(Данные, ИдентификаторКлиента)
Попытка
РазмерДанных = Данные.ПрочитатьЦелое32(0);
ДанныеСообщения = Данные.Прочитать(4, РазмерДанных);
ДД = ПолучитьДвоичныеДанныеИзБуфераДвоичныхДанных(ДанныеСообщения);
ОтдельноеСообщение = ПолучитьСтрокуИзДвоичныхДанных(ДД, КодировкаТекста.UTF8);
Сообщить("Сообщение от " + ИдентификаторКлиента + " (" + РазмерДанных + " байт):" + Символы.ПС + ОтдельноеСообщение);
Если (Данные.Размер - 4 - РазмерДанных) > 0 Тогда
ОстатокДанных = Данные.Прочитать(4 + РазмерДанных, Данные.Размер - 4 - РазмерДанных);
ВывестиСообщение(ОстатокДанных, ИдентификаторКлиента);
КонецЕсли;
Исключение
ДанныеСообщения = Данные.Прочитать(0, Данные.Размер);
ДД = ПолучитьДвоичныеДанныеИзБуфераДвоичныхДанных(ДанныеСообщения);
ОтдельноеСообщение = ПолучитьСтрокуИзДвоичныхДанных(ДД, КодировкаТекста.UTF8);
Сообщить("Сообщение от " + ИдентификаторКлиента + " (" + Данные.Размер + " байт):" + Символы.ПС + ОтдельноеСообщение);
КонецПопытки;
КонецПроцедуры
Процедура СерверПолучилДанные() Экспорт
АргументыСобытия = МС.АргументыСобытия;
Данные = АргументыСобытия.Данные;
// Данные, полученные сервером, доступны нам как тип БуферДвоичныхДанных (BinaryDataBuffer).
// Если с сервером будет взаимодействовать клиент, который формирует сообщения по оговоренным заранее правилам,
// появляется возможность делить данные на отдельные сообщения и кроме того знать тип данных отдельного сообщения.
// Как частный случай можно на стороне клиента отправлять в сетевой поток сначала размер сообщения (четыре байта)
// а затем само сообщение. В этом примере так и сделано.
// Разобъем полученные сервером данные на отдельные сообщения.
ВывестиСообщение(Данные, АргументыСобытия.ИдентификаторКлиента);
// Отправим всем строку.
Сервер.ОтправитьВсем("От сервера всем: " + Символы.ПС + ПолучитьСтрокуИзБуфераДвоичныхДанных(Данные));
// Отправим клиенту.
Сервер.ОтправитьКлиенту(АргументыСобытия.ИдентификаторКлиента, "От сервера клиенту " + АргументыСобытия.ИдентификаторКлиента + ": " + Символы.ПС + ПолучитьСтрокуИзБуфераДвоичныхДанных(Данные));
Сообщить("========================");
КонецПроцедуры
Процедура ПриОтключенииКлиента() Экспорт
АргументыСобытия = МС.АргументыСобытия;
Сообщить("Клиент отключился: " + АргументыСобытия.ИдентификаторКлиента);
Сообщить("Активных клиентов: " + Сервер.КоличествоАктивныхКлиентов);
Сообщить("========================");
КонецПроцедуры
Процедура ПриОшибкеСервера() Экспорт
АргументыСобытия = МС.АргументыСобытия;
Сообщить("ОшибкаСервера: " + АргументыСобытия.ОшибкаСервера);
Сообщить("========================");
КонецПроцедуры
ПодключитьВнешнююКомпоненту("C:\444\111\MultithreadedTCPServer\MultithreadedTCPServer\bin\Debug\MultithreadedTCPServer.dll");
МС = Новый МногопоточныйTCPСерверДляОдноСкрипта();
Сервер = МС.МногопоточныйСервер(8080);
Сервер.ПриОшибкеСервера = МС.Действие(ЭтотОбъект, "ПриОшибкеСервера");
Сервер.ПриОтключенииКлиента = МС.Действие(ЭтотОбъект, "ПриОтключенииКлиента");
Сервер.СерверПолучилДанные = МС.Действие(ЭтотОбъект, "СерверПолучилДанные");
Сервер.ПриПодключенииКлиента = МС.Действие(ЭтотОбъект, "ПриПодключенииКлиента");
// // Для визуализации активности клиентов можно включить это свойство.
// Сервер.ПроверятьАктивностьКлиента = Истина;
Сервер.Начать();
Пока МС.Продолжать Цикл
МС.ПолучитьСобытие().Выполнить();
КонецЦикла;
</div>
</pre>
</details>
</div>
<div class=marginleft40>Сохраните этот код в файл с расширением ".os" и исполняйте в окне терминала.</div>
<p>
Ограничений на способ реализации TCP клиента нет. Сервер можно настроить на протокол общения с любым клиентом.
Проще всего использовать клиент из этой же библиотеки.
</p>
<div class=marginleft40>
<details>
<summary>В этом примере создается клиент:</summary>
<pre class=code>
<button id="copy2" type="button" style="font-size:100%;">Копировать</button>
<hr style="border-color: lightgray;"><div id="cont2">
Перем МС;
Процедура ВывестиСообщение(Данные)
Попытка
// В случае передачи в потоке перед самим сообщением размера сообщения используем этот вариант.
РазмерДанных = Данные.ПрочитатьЦелое32(0);
ДанныеСообщения = Данные.Прочитать(4, РазмерДанных);
ДД = ПолучитьДвоичныеДанныеИзБуфераДвоичныхДанных(ДанныеСообщения);
ОтдельноеСообщение = ПолучитьСтрокуИзДвоичныхДанных(ДД, КодировкаТекста.UTF8);
Сообщить("Получено сообщение: " + " (" + РазмерДанных + " байт):" + Символы.ПС + ОтдельноеСообщение);
Если (Данные.Размер - 4 - РазмерДанных) > 0 Тогда
ОстатокДанных = Данные.Прочитать(4 + РазмерДанных, Данные.Размер - 4 - РазмерДанных);
// Продолжаем делить данные на отдельные сообщения.
ВывестиСообщение(ОстатокДанных);
КонецЕсли;
Исключение
// В случае передачи в потоке никак не разделенных байтов используем этот вариант.
ДанныеСообщения = Данные.Прочитать(0, Данные.Размер);
ДД = ПолучитьДвоичныеДанныеИзБуфераДвоичныхДанных(ДанныеСообщения);
ОтдельноеСообщение = ПолучитьСтрокуИзДвоичныхДанных(ДД, КодировкаТекста.UTF8);
Сообщить("Получено сообщение: " + " (" + Данные.Размер + " байт):" + Символы.ПС + ОтдельноеСообщение);
КонецПопытки;
// Можно предусмотреть другие способы разделения потока байтов на отдельные сообщения, например посылка символа перевода строки.
КонецПроцедуры
Процедура КлиентПолучилДанные() Экспорт
АргументыСобытия = МС.АргументыСобытия;
Данные = АргументыСобытия.Данные;
// Данные, полученные клиентом, доступны нам как тип БуферДвоичныхДанных (BinaryDataBuffer).
// Если с клиентом будет взаимодействовать сервер, который формирует сообщения по оговоренным заранее правилам,
// появляется возможность делить данные на отдельные сообщения и кроме того знать тип данных отдельного сообщения.
// Как частный случай можно на стороне сервера отправлять в сетевой поток сначала размер сообщения (четыре байта)
// а затем само сообщение.
ВывестиСообщение(Данные);
Сообщить("========================");
КонецПроцедуры
ПодключитьВнешнююКомпоненту("C:\444\111\MultithreadedTCPServer\MultithreadedTCPServer\bin\Debug\MultithreadedTCPServer.dll");
МС = Новый МногопоточныйTCPСерверДляОдноСкрипта();
// Запустим клиента фоновым заданием.
TCPПодключение1 = МС.ФоновыйTCPКлиент("127.0.0.1", 8080);
// Назначим обработчик события.
TCPПодключение1.КлиентПолучилДанные = МС.Действие(ЭтотОбъект, "КлиентПолучилДанные");
// Подождем пока клиент подключится.
Пока Не TCPПодключение1.Подключен Цикл
Приостановить(100);
КонецЦикла;
Сообщить("Подключен");
Сообщить("Печатайте и нажмите ENTER");
// // Можно отправлять данные.
// БуферОтправки = ПолучитьБуферДвоичныхДанныхИзСтроки("Серверу от клиента ПРИВЕТ!");
// TCPПодключение1.Отправить(БуферОтправки);
// Можно далее дополнять алгоритм сценария, фоновый TCP клиент не блокирует выполнение сценария.
// Мы же не закроем подключение и остановимся в цикле ввода сообщений.
Результат = Неопределено;
Пока ВвестиСтроку(Результат, , , ) Цикл
TCPПодключение1.Отправить(ПолучитьБуферДвоичныхДанныхИзСтроки(Результат));
КонецЦикла;
</div>
</pre>
</details>
</div>
<div class=marginleft40>Сохраните этот код в файл с расширением ".os" и исполняйте в окне терминала.</div>
<p>
Если потребуется организовать безопасный ввод пароля можно воспользоваться библиотекой
<a href="https://infostart.ru/1c/articles/2527054/" target="_blank">Текстовый пользовательский интерфейс (TUI) для сценарного языка OneScript</a>.
</p>
<div class=marginleft40>
<details>
<summary>В этом примере показан безопасный ввод пароля:</summary>
<pre class=code>
<button id="copy3" type="button" style="font-size:100%;">Копировать</button>
<hr style="border-color: lightgray;"><div id="cont3">
Перем Стр;
Процедура ЗапросПароля(параметр1) Экспорт
Контекст = Новый Структура();
Контекст.Вставить("МояСтруктура", параметр1);
ЗагрузитьСценарийИзСтроки(Стр, Контекст);
КонецПроцедуры
Стр = "
|Перем ТФ, ПолеВвода1;
|
|Процедура ПолеВвода1_КлавишаНажата() Экспорт
| Если ТФ.АргументыСобытия.Клавиша = ТФ.Клавиши.Enter Тогда
| МояСтруктура.Пароль = ПолеВвода1.Текст;
| ТФ.Завершить();
| КонецЕсли;
|КонецПроцедуры
|
|ПодключитьВнешнююКомпоненту(""C:\444\111\OneScriptTerminalGui\OneScriptTerminalGui\bin\Debug\OneScriptTerminalGui.dll"");
|ТФ = Новый ТерминалФормыДляОдноСкрипта();
|
|ПолеВвода1 = ТФ.Верхний.Добавить(ТФ.ПолеВвода("""", , , 30));
|ПолеВвода1.Центр();
|ПолеВвода1.Секрет = Истина;
|ПолеВвода1.КлавишаНажата = ТФ.Действие(ЭтотОбъект, ""ПолеВвода1_КлавишаНажата"", ПолеВвода1);
|
|ТФ.Запуск();
|";
МояСтруктура = Новый Структура("Пароль", "");
МассивПараметров = Новый Массив(1);
МассивПараметров[0] = МояСтруктура;
Задание = ФоновыеЗадания.Выполнить(ЭтотОбъект, "ЗапросПароля", МассивПараметров);
Задание.ОжидатьЗавершения();
Сообщить("Пароль = " + МояСтруктура.Пароль);
// Ожидаем пароль.
Пока МояСтруктура.Пароль = "" Цикл
Приостановить(1000);
КонецЦикла;
// // Если нужно не завершаем сценарий.
// Пока Истина Цикл
// Приостановить(1000);
// КонецЦикла;
</div>
</pre>
</details>
</div>
<p>
В библиотеке есть методы создания самоподписанного сертификата сроком на один год. Для защищенного соединения на стороне сервера
нужен будет файл сертификата <b>.pfx</b>. На стороне клиента нужен будет файл сертификата <b>.crt</b>.
</p>
<p>
Файл сертификата <b>.pfx</b> генерируется методом <b>МногопоточныйTCPСерверДляОдноСкрипта.СоздатьСамоподписанныйСертификат (OneScriptMultithreadedTCPServer.CreateSelfSignedCertificate)</b>.
</p>
<p>
Файл сертификата <b>.crt</b> извлекается их файла <b>.pfx</b> методом <b>МногопоточныйTCPСерверДляОдноСкрипта.ИзвлечьCrtИзPfx (OneScriptMultithreadedTCPServer.ExtractCrtFromPfx)</b>.
</p>
<div class=marginleft40>
<details>
<summary>Пример работы с сертификатами:</summary>
<pre class=code>
<button id="copy3" type="button" style="font-size:100%;">Копировать</button>
<hr style="border-color: lightgray;"><div id="cont3">
ПодключитьВнешнююКомпоненту("C:\444\111\MultithreadedTCPServer\MultithreadedTCPServer\bin\Debug\MultithreadedTCPServer.dll");
МС = Новый МногопоточныйTCPСерверДляОдноСкрипта();
МС.СоздатьСамоподписанныйСертификат("C:\111\certik.pfx", "OneScript", "123456");
МС.ИзвлечьCrtИзPfx("C:\111\certik.pfx", "123456", "C:\111\certik.crt");
</div>
</pre>
</details>
</div>
</section>
</body>
</html>