За последние 24 часа нас посетили 22176 программистов и 1145 роботов. Сейчас ищут 874 программиста ...

Отправка данных серверному приложению DServer.exe средствами PHP

Тема в разделе "Прочие вопросы по PHP", создана пользователем opiums, 1 апр 2019.

  1. opiums

    opiums Активный пользователь

    С нами с:
    6 фев 2015
    Сообщения:
    24
    Симпатии:
    0
    Доброго времени суток, уважаемые форумчане. У меня есть серверное приложение DServer.exe от IL-2 Battle of Stalingrad, приложение принимает команды консоли от RConClient.exe. Я хочу написать некий аналог RConClient.exe на PHP.
    Из документации следует:
    Код (Text):
    1.  
    2.   Commands, which sre sended to server, and responces, recieved from the serverver are nested into  packets. Every packet should have a structure as follows:
    3.  
    4.  
    5. | length of data  |                              data                  |
    6. |        |        | The string containing command or responce |00000000|
    7. |     two bytes   |    length of string                       |one byte|
    8.  
    9.   So, in fact, the whole packet consists of two bytes, representing the length of data, and the data, which is a C-string (just chars, bytes), terminated with 0-byte. One command/responce is nested in one packet. So as you can see, the maximum packet data lendth is 64K (two bytes for data length).
    После поисков в интернете, я написал базовый начальный код:
    PHP:
    1. header("Content-Type: text/plain");
    2. $ip = '192.168.1.3' ;
    3. $port = '28001';
    4. $timeout = 5;
    5. if(!$fp = @fsockopen($ip, $port, $errno, $errstr, $timeout))
    6. {
    7.     echo $errstr.' ('.$errno.')\r\n';
    8. } else {
    9.     $out = 'auth 123 123'; // не бинарные данные
    10.     fwrite($fp, $out);
    11.     while (!feof($fp)) {
    12.         echo fgets($fp, 128);
    13.     }
    14.     fclose($fp);
    15. }
    После длительных поисков примерно понял, что нужно преобразовать данные в бинарный формат. Но вот беда, я не знаю как на php можно преобразовать такую строку в бинарную, используя pack();, чтобы можно было отправить данные и получить ответ, с вышеописанным кодом я только вижу что появляется подключение на стороне серверного приложения, а на стороне клиента, отправляющего запрос средствами php я получаю ошибку по тайм ауту. Прошу вашей помощи в правильном преобразовании строки и отправки данных на сервер, может кто знает?
     
  2. Emilien

    Emilien Активный пользователь

    С нами с:
    30 июн 2016
    Сообщения:
    246
    Симпатии:
    156
    PHP:
    1. function packet_build($data) {
    2.     return pack("va*", strlen($data) + 1, $data . "\0");
    3. }
    PHP:
    1. $out = packet_build('auth 123 123');
     
    opiums нравится это.
  3. opiums

    opiums Активный пользователь

    С нами с:
    6 фев 2015
    Сообщения:
    24
    Симпатии:
    0
    Отлично! данные упакованы, но ответ почему то приходит в виде файла... Хм... Значит нужно обратно как то декодировать ответ от сервера? Тем не менее в файле я получаю:
    Код (Text):
    1.      STATUS=6
    Что говорит об этом - RCR_ERR_AUTH_INCORRECT=6
    Но, если я введу верные данные для авторизации, открывается на стороне сервера сессия, от PHP я уже не получаю ответа, а просто "висю" в режиме ожидания, может как то есть способ отправить 2 команды одновременно, потом закрыть сессию? Не нашёл команды, подобной exit или logout, значит связь заканчивается по таймауту...

    P.S. Нашёл способ выводить просто в браузере: header("Content-Type: text/html");
     
    #3 opiums, 2 апр 2019
    Последнее редактирование: 2 апр 2019
  4. Emilien

    Emilien Активный пользователь

    С нами с:
    30 июн 2016
    Сообщения:
    246
    Симпатии:
    156
    Запрос и ответ упакованы одинаково. Сначала два байта указывают размер данных, затем идёт команда или ответ и завершается всё нулевым байтом.
    PHP:
    1. $len = unpack('v', fread($fp, 2))[1];
    2. $str = unpack('Z*', fread($fp, $len))[1];
    3. echo $str;
     
    opiums нравится это.
  5. opiums

    opiums Активный пользователь

    С нами с:
    6 фев 2015
    Сообщения:
    24
    Симпатии:
    0
    Если я правильно понял, то скрипт должен выглядеть так:
    PHP:
    1. $timeout = 5;
    2. if(!$fp = fsockopen($ip, $port, $errno, $errstr, $timeout))
    3. {
    4.     echo $errstr.' ('.$errno.')\r\n';
    5. } else {
    6.     $out = packet_build($cmd);
    7.     fwrite($fp, $out);
    8.         while (!feof($fp)) {
    9.             //echo fgets($fp, 128);
    10.             $len = unpack('v', fread($fp, 2))[1];
    11.             $str = unpack('Z*', fread($fp, $len))[1];
    12.             echo $str;
    13.         }
    14.     fclose($fp);
    15.     }
    Тем не менее, ответ я получаю не сразу, а по истечению времени, и вместе с ним вижу такие ошибки:
    Warning: unpack(): Type v: not enough input, need 2, have 0 in (...).php on line 28
    Warning
    : fread(): Length parameter must be greater than 0 in (...).php on line 29
    Fatal error:
    Maximum execution time of 300 seconds exceeded in (...).php on line 28

    28 и 29 строки соответственно:
    PHP:
    1. $len = unpack('v', fread($fp, 2))[1];
    2. $str = unpack('Z*', fread($fp, $len))[1];
    Есть ли какая то возможность получать ответ от сервера сразу, а не по истечении лимита времени?
     
  6. Emilien

    Emilien Активный пользователь

    С нами с:
    30 июн 2016
    Сообщения:
    246
    Симпатии:
    156
    При таком условии выход из цикла будет только когда сервер закрыл соединение. Это и происходило при неверных логине и пароле. Сейчас соединение продолжает оставаться открытым.
    PHP:
    1. $out = packet_build($cmd);
    2. fwrite($fp, $out);
    3. $len = unpack('v', fread($fp, 2))[1];
    4. $str = unpack('Z*', fread($fp, $len))[1];
    5. echo $str;
    6. fclose($fp);
     
    opiums нравится это.
  7. opiums

    opiums Активный пользователь

    С нами с:
    6 фев 2015
    Сообщения:
    24
    Симпатии:
    0
    Ах вот оно что, теперь всё понятно, спасибо большое за помощь! :)
     
  8. opiums

    opiums Активный пользователь

    С нами с:
    6 фев 2015
    Сообщения:
    24
    Симпатии:
    0
    Работает то как надо, но не могу разобраться каким образом можно отправить 2 команды за раз в 1 сессию чтобы получить необходимый ответ?
    Попытался циклом, но отправляется только ответ 1-й команды, попробовал сделать два fwrite();, друг за другом, до закрытия соединения fclose(); - аналогично, на 2й команде я уже не авторизован. Консольный клиент RConClient.exe по факту создаёт сессию и в ней работает, но в данном случае я не понимаю как мне попасть внутрь сессии и отправлять туда команды, после уже получить ответ и закрыть соединение.
     
  9. Emilien

    Emilien Активный пользователь

    С нами с:
    30 июн 2016
    Сообщения:
    246
    Симпатии:
    156
    По идее там сначала формируешь пакет с командой auth и отправляешь. Потом читаешь ответ сервера. Потом формируешь пакет с другой командой и отправляешь. Снова читаешь ответ сервера.
     
    opiums нравится это.
  10. opiums

    opiums Активный пользователь

    С нами с:
    6 фев 2015
    Сообщения:
    24
    Симпатии:
    0
    Вы правы, и снова я вам очень благодарен! Теперь код выглядит просто, но правильно:
    PHP:
    1.         $out1 = packet_build($auth);
    2.         $out2 = packet_build($cmd);
    3.             fwrite($fp, $out1);
    4.             $len = unpack('v', fread($fp, 2))[1];
    5.             $str = unpack('Z*', fread($fp, $len))[1];
    6.             echo $str.'<br>';
    7.            
    8.             fwrite($fp, $out2);
    9.             $len2 = unpack('v', fread($fp, 2))[1];
    10.             $str2 = unpack('Z*', fread($fp, $len2))[1];
    11.             echo $str2.'<br>';
    12.            
    13.         fclose($fp);