Описание объекта JCJSTcpServer плагина PluginNetwork

Объект JCJSTcpServer это полнофункциональный многопоточный асинхронный TCP сервер с поддержкой защищенного соединения

В этом разделе перечислены свойства и методы объекта JCJSTcpServer плагина PluginNetwork

В JCJSTcpServer реализован протокол обмена jCjS, гарантирующий неизменную доставку JS объектов между удаленными постами jCjS.
Для совместимости со сторонним ПО, предусмотрен режим raw.


Пример использования находится в каталоге stuff/tests/pluginNetwork/TCP_Srv


Свойства (property)


Объект JCJSTcpServer имеет общие свойства указанные в описательной части протокола обмена jCjS

Пример использования:

// Загрузить плагин и Установить в JS конструторы объектов
post.loadPlugin('PluginNetwork').install(scriptEngine);

// Создать объект, к свойствам и
// методам которого можно обращаться в стиле Qt
var tcpServer = new JCJSTcpServer();
//свойство параметра чтение/запись:
// Чтение
var addr = tcpServer.listen_addr;
// Запись
var addr = '127.0.0.1';
tcpServer.listen_addr = addr;

String tcpServer.listen_addr Чтение/запись
Установить прослушиваемый ip адрес
Внимание!!! запись возможна только перед tcpServer.serverStart() или после tcpServer.serverStop()
При установке автоматически устанавливается параметр tcpVer

var addr = tcpServer.listen_addr;     // по умолчанию '0.0.0.0' (на всех интерфейсах)
tcpServer.listen_addr = '127.0.0.1';  // установить прослушивание только на localhost


int tcpServer.listen_port Чтение/запись
Установить прослушиваемый порт
Внимание!!! запись возможна только перед tcpServer.serverStart() или после tcpServer.serverStop()

var port = tcpServer.listen_port;    // по умолчанию '0' (система выделит порт автоматически)
tcpServer.listen_port = 8888;        // установить прослушивающий порт


String Array() tcpServer.allAddresses только чтение
Запрос всех доступных в системе ip-адресов (всех интерфесов)

var allAddresses = tcpServer.allAddresses;    // Массив строк ['127.0.0.1', '192.168.1.24', '10.7.12.25']


bool tcpServer.isWork только чтение
возвращает текущее состояние сервера

if(tcpServer.isWork) {
    tcpServer.serverStop();
}


VariantList tcpServer.clientDescriptors только чтение

возвращает дескрипторы соединений

var clientDescriptors = tcpServer.clientDescriptors;

int tcpServer.clientCount только чтение

возвращает количество подключенных клиентов

var clientsCount = tcpServer.clientCount;

int tcpServer.maxClients Чтение/запись
Максимальное количество клиентов

var maxClients = tcpServer.maxClients;  // по умолчанию 30
tcpServer.maxClients = 1;               // ограничить одним соединением


StringList tcpServer.clientAddrs только чтение

возвращает адреса подключенных клиентов

var clientAddrs = tcpServer.clientAddrs;

int tcpServer.intrval чтение/запись
Интервал чтения из сокета


bool tcpServer.raw чтение/запись
Включение raw режима (по умолчанию выключен)


quint8 tcpServer.stopChar чтение/запись
В режиме raw: Символ конца приемной посылки (по умолчанию 0 - выключен)


String tcpServer.stopWord чтение/запись
В режиме raw: Слово конца приемной посылки (по умолчанию '' - выключен)
stopChar более приоритетнее чем stopWord


Параметры, относящиеся к конфигурированию защищенного соединения

bool tcpServer.ssl только чтение
возвращает текущий режим

var ssl = tcpServer.ssl;

Если сервер подключен в режиме без шифрования, а клиент пытается подключиться в режиме SSL, то в логе клиента вы встретите уведомление

2015-02-06-11:11:29.182 +1 TCP_Cli_00: error=13: Error during SSL handshake: unknown protocol

В этом случае необходимо переключить сервер в режим защищенного соединения или отключить шифрование на стороне клинета.

Если сервер подключен в режиме шифрования, а клиент пытается подключиться в обычном режиме, то в логе клиента вы найдете сообщение о дисконнекте

2015-02-06-11:18:37.468 +1 TCP_Cli_00: error=1: The remote host closed the connection

В этом случае необходимо перевести клинета на защищенное соединение или переключить сервер в режим без шифрования

Если сервер использует самоподписанные сертификаты, на стороне клиента в логе можно встретить следующюю запись

2015-02-06-11:18:37.468 +1 error=13: The host name did not match any of the valid hosts for this certificate

В этом случае на стороне клиента нужно включить режим игнорирования ошибок ignoreSelfSignSsl


String tcpServer.mode только чтение
возвращает текущий режим

var mode = tcpServer.mode;
/* Возвратит варианты:
   "без шифрования"
   "SSL сервер"
*/


String tcpServer.verifyMode Чтение/запись
Верификация удаленного клиента


int tcpServer.tcpVer Чтение/запись
Установить необходимую версию TcpIp (по умолчанию 0 автоматичекий выбор)
Внимание!!!
Автоматически устанавливается перед пуском сервера или во время установки listen_addr или сертификата
Установка необходимой версии имеет смысл только если:


String tcpServer.sslProto чтение/запись
установить необходимый протокол шифрования (по умолчанию SecureProtocols - максимально безопасный)
Внимание!!! Устанавливать необходимй протокол шифрования следует перед serverStart()
Для просмотра протокола шифрования, используемого клиентом см. getSslProto()


String tcpServer.sslKeyPass Чтение/запись

Установить пароль к приватному ключу
Внимание!!! Параметр устанавливается когда сервер остановлен

var keyPass = tcpServer.sslKeyPass;

keyPass = '9045271001'

tcpServer.sslKeyPass = keyPass;

String tcpServer.sslDir Чтение/запись

Указать каталог, в котором лежат ключ и сертификат
String tcpServer.sslCertName Чтение/запись

Указать имя сертификата (каталог поиска sslDir по умолчанию ../etc/ssl относительно каталога с исполняемым файлом jCjS)
Необходимое условие - файл приватного ключа .key и сертификата .crt должны имет одно имя
Внимание!!! Параметр устанавливается когда сервер остановлен
При установке сертификата определяется кому выдан сертификат, и если это ip-адрес или localhost то автоматически устанавливается параметр listen_addr и tcpVer

if(post.ssl.enable == 'true')
{
    // сначало указать пароль для приватного ключа, если он зашифрован
    tcpServer.sslKeyPass = '9045271001'

    // будет произведен поиск localhost.crt и его ключа в tcpServer.sslDir или '../etc/ssl' если tcpServer.sslDir === ''
    tcpServer.sslCertName = 'localhost'

    // или указать каталог с ключами, но выбор имени используемого сертификата будет отсутствовать
    tcpServer.sslDir = server.getAbsPath('../etc/ssl') // будет найдена первая пара .key  .crt с одинаковым именем

    // sslDir и sslCertName это два разных подхода к установке используемого сертификата
}

При подключении к серверу, использующего SSL соединение, в логе клиента можно найти такую запись

2015-02-05-19:58:40.991 +0 JCJSTcpClient: 127.0.0.1:54207 <-> 127.0.0.1:8001 используют защищенное соединение



Генерация ключей

Если необходимо сменить ключ и сертификат, воспользуйтес сценарием etc/ssl/create-keys.[cmd|sh]
Необходимое условие - OpenSSL должен быть установлен
Скачать OpenSSL можно здесь https://www.openssl.org/related/binaries.html

if(post.ssl.enable == 'true')
{
    tcpServer.sslDir = server.getAbsPath('../etc/ssl')
    tcpServer.sslKeyPass = '9045271001'
}

При подключении к серверу, использующего SSL соединение, в логе клиента можно найти такую запись

2015-02-05-19:58:40.991 +0 JCJSTcpClient: 127.0.0.1:54207 <-> 127.0.0.1:8001 используют защищенное соединение



Сигналы

void tcpServer.readyRead(Variant message, int socketDescriptor)

генерируется объектом JCJSTcpServer при успешном приеме сообщения

// подсоединить к сигналу readyRead функцию обработчик
tcpServer.readyRead.connect(function messageRead(mess, socketDesc){
    post.log("i", 'Клиент прислал сообщение: ' + JSON.stringify(mess) + '  дескриптор соединения=' + socketDesc)
});
// эквивалентно следующей конструкции
function messageRead(mess, socketDesc){
    post.log("i", 'Клиент прислал сообщение: ' + JSON.stringify(mess) + '  дескриптор соединения=' + socketDesc);
}
tcpServer.readyRead.connect(messageRead);


// =====================================
// следующее два тривиального примера: автоматически отправлять эхо-ответ
// 1
tcpServer.readyRead.connect(tcpServer.sendClient);

// 2
//чтобы передать объект this в функцию обработчик
tcpServer.readyRead.connect(tcpServer,  function(mess, descrptr){
    //    здесь   (this === tcpServer) =  true
    this.sendClient(mess, descrptr);
});


void tcpServer.connected(String addr, int port, int socketDescriptor) Сигнал

генерируется объектом JCJSTcpServer при успешном подсоединении клиента к серверу

// подсоединить к сигналу connected функцию обработчик
tcpServer.connected.connect(function clientConnected(addr, port, socketDesc){
    post.log("i", 'Есть новое соединение: ip-addr=' + addr + '  port=' + port + '  дескриптор соединения=' + socketDesc)
});

void tcpServer.disconnected(String addr, int port, int socketDescriptor) Сигнал

генерируется объектом JCJSTcpServer при разъединении соединения с клиентом

// подсоединить к сигналу connected функцию обработчик
tcpServer.disconnected.connect(function clientDisconnected(addr, port, socketDesc){
    post.log("i", 'Клиент отключился: ip-addr=' + addr + '  port=' + port + '  дескриптор соединения=' + socketDesc)
});

void tcpServer.error(String error, int socketDescriptor) Сигнал

генерируется объектом JCJSTcpServer при ошибках в сокете

// подсоединить к сигналу connected функцию обработчик
tcpServer.error.connect(function error(err, socketDesc){
    post.log("i", 'Ошибка: err=' + err + '  дескриптор соединения=' + socketDesc)
});

void tcpServer.protoSelfChange(int socketDescriptor, newVer, oldVer) Сигнал

генерируется объектом JCJSTcpServer если в процессе обмена протокол был изменен

tcpServer.protoSelfChange.connect(function protoSelfChange(d, newVer, oldVer){
    if(newVer === 0 && oldVer > 0) {
       post.log('w',  'было понижение версии. Встроенное шифрование crypt отключено');

       post.log('w', 'cli ' + d + ': '+JSON.stringify(tcpServer.clientInfo(d)) );
    }
});


Методы

Date tcpServer.serverWorkTime() Функция

Время работы сервера

var serverWorkTime = tcpServer.serverWorkTime();

bool tcpServer.serverStart() Функция

Запустить сервер

tcpServer.serverStart();

bool tcpServer.serverStop() Функция

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

tcpServer.serverStop();

bool tcpServer.serverStartStop() Функция

поменять статус сервера на противоположный

tcpServer.serverStartStop();

void tcpServer.removeAllCliens() Функция

отключает всех клиентов
(у каждого подключенного клиента сгенерируется сигнал разрыва соединения)

tcpServer.removeAllCliens();

void tcpServer.removeClient(descriptor) Функция

отключает клиента по дискриптору
(у подключенного клиента сгенерируется сигнал разрыва соединения)

for(var i = 0; i < tcpServer.clientDescriptors.lenght; ++i)
{
    tcpServer.removeClient(tcpServer.clientDescriptors[i]);
}
// этот код равноценен команде tcpServer.removeAllCliens()

void tcpServer.getSslProto(descriptor) Функция
Используемый клиентом протокол шифрования

for(var i = 0; i < tcpServer.clientDescriptors.lenght; ++i)
{
    post.log('w', 'cli number ' + i + ': '+tcpServer.getSslProto(tcpServer.clientDescriptors[i]));
}

// --------------- ИЛИ ---------------
tcpServer.connected.connect(tcpServer, function connected(addr, port, socketDescriptor){
    post.log("e", "" + addr + ":" + port +
             "  sslProto="      + this.getSslProto(socketDescriptor)  +
             "  clientCount="   + this.clientCount);
});
tcpServer.disconnected.connect(tcpServer, function disconnected(addr, port, socketDescriptor){
    post.log("e", "" + addr + ":" + port +
             "  sslProto="      + this.getSslProto(socketDescriptor)  +
             "  clientCount="   + this.clientCount);
});

void tcpServer.clientInfo(descriptor) Функция
Информация о соединении ввиде объекта

var dAll = tcpServer.clientDescriptors;
//post.log('w', 'cli dAll: '+dAll);
while(dAll.length)
{
    var d = dAll.shift();
    post.log('w', 'cli ' + d + ': '+JSON.stringify(tcpServer.clientInfo(d)) );
}

// Выведет подробную информацию о соединении
// {
//   addr:          "10.7.97.115",
//   port:          62027,
//   sslAuth:       "RSA",
//   sslEncrypt:    "AES(256)",
//   sslKeyExch:    "RSA",
//   sslProto:      "SSLv3"
// }


void tcpServer.sendAllClients(String mess) Функция

отправить всем подключенным клиентам сообщение

tcpServer.sendAllClients('Сообщение, для всех подключенных');

void tcpServer.sendClient(Variant mess, int socketDescriptor) Функция

отправить сообщение клиенту по дескриптору соединения

tcpServer.readyRead.connect(function messageRead(mess, socketDesc){
    post.log("i", 'Клиент прислал сообщение: ' + JSON.stringify(mess) + '  дескриптор соединения=' + socketDesc);
    tcpServer.sendClient('Сообщение для клиента, приславшего сообщение', socketDesc);
});

// равноценно следующей записи, но передается объект this в функцию обработчик
tcpServer.readyRead.connect(tcpServer, function messageRead(mess, socketDesc){
    post.log("i", 'Клиент прислал сообщение: ' + JSON.stringify(mess) + '  дескриптор соединения=' + socketDesc);
    //    здесь   (this === tcpServer) =  true
    this.sendClient('Сообщение для клиента, приславшего сообщение', socketDesc);
});

void tcpServer.getClient(int socketDescriptor) Функция

взять у сервера объект соединения по дескриптору соединения

tcpServer.readyRead.connect(tcpServer, function messageRead(mess, socketDesc){
    post.log("i", 'Клиент прислал сообщение: ' + JSON.stringify(mess) + '  дескриптор соединения=' + socketDesc);
    //    здесь   (this === tcpServer) =  true
    this.sendClient('Сообщение для клиента, приславшего сообщение', socketDesc);
});

// равноценно следующей записи, но запрашивается объект соединения
tcpServer.readyRead.connect(tcpServer, function messageRead(mess, socketDesc){
    post.log("i", 'Клиент прислал сообщение: ' + JSON.stringify(mess) + '  дескриптор соединения=' + socketDesc);
    //    здесь   (this === tcpServer) =  true
    var cli = this.getClient(socketDesc);
    cli.sendMessage('Сообщение для клиента, приславшего сообщение');
});