Не получается прочитать данные от сервера с помощью SocketRead. ERR_NETSOCKET_IO_ERROR код 5273

 

Все проверки открытого соединения сюда писать не буду, для краткости. Скажу сразу, что сервер данные получает (отображает в интерфейсе) и отправляет ответ (16 байт). Но ответ советник не может прочитать.

int _socketHandle = SocketCreate();

SocketConnect(_socketHandle, Address, Port, 1000);



// Отправка данных на сервер (работает хорошо)

SocketSend(_socketHandle, _symbolData, ArraySize(_symbolData));



// Здесь иногда false, а иногда true. Не знаю почему, но думаю, что это неправильно. Должно быть строго, либо true, либо false.

// Как это правильно готовить?

if (!SocketIsReadable(_socketHandle))
   return false;



// Здесь всегда возвращается -1 по таймауту, хотя сервер возвращает ответ через несколько миллисекунд. Код ошибки 5273 ERR_NETSOCKET_IO_ERROR.

int c = SocketRead(_socketHandle, _serverBuffer, 100, 10000);
if (c == -1)
{
   int err = GetLastError();
   Print("Не удалось прочитать данные из сокета. Error " + IntegerToString(err));
   return false;
}
 

Прочтите детальное описание функции SocketIsReadible и что она возвращает.

Не true/false.

 
Renat Fatkhullin:

Прочтите детальное описание функции SocketIsReadible и что она возвращает.

Не true/false.

Да, увидел. Но это всё равно странно, когда функция возвращает мне число больше нуля, но SocketRead всё равно не читает данные.

В общем я написал простую dll на C# и там через TcpClient всё нормально отправляется и читается. Хотел обойтись без прослойки. Надеюсь программисты доделают сокеты в mql.

 
Anton_M:

Да, увидел. Но это всё равно странно, когда функция возвращает мне число больше нуля, но SocketRead всё равно не читает данные.

В общем я написал простую dll на C# и там через TcpClient всё нормально отправляется и читается. Хотел обойтись без прослойки. Надеюсь программисты доделают сокеты в mql.

А сколько вы читаете данных?

Вы можете мгновенно без ожидания прочесть только то количество байтов, которое вам вернула функция SocketIsReadable. Если попытаетесь прочесть больше, то попадете на синхронное ожидание внутри сетевой WinAPI функции.

В вашем коде явно указано "читать 100 байт вне зависимости от наличия доступных данных во входящем буфере сокета". При том, что вы сами указали "сервер отправляет только 16 байт ответа". То есть, вы четко/жестко требуете получения 100 байт и получаете закономерный таймаут. Ведь вам прислали только 16.


Сокеты в MQL5 работают правильно. Но надо знать их особенности.

 
Renat Fatkhullin:

А сколько вы читаете данных?

Вы можете мгновенно без ожидания прочесть только то количество байтов, которое вам вернула функция SocketIsReadable. Если попытаетесь прочесть больше, то попадете на синхронное ожидание внутри сетевой WinAPI функции.

В вашем коде явно указано "читать 100 байт вне зависимости от наличия доступных данных во входящем буфере сокета". При том, что вы сами указали "сервер отправляет только 16 байт ответа". То есть, вы четко/жестко требуете получения 100 байт и получаете закономерный таймаут. Ведь вам прислали только 16.


Сокеты в MQL5 работают правильно. Но надо знать их особенности.

Ренат, а почему не убрать ограничение на количество чтения? Зачем это ограничение, если ответ уже к нам пришёл?

Почему не отдать полный ответ и далее с ним работать по-усмотрению?

int  SocketTlsRead(
   int           socket,               // сокет
   uchar&        buffer[],             // буфер для чтения данных из сокета
   uint          buffer_maxlen         // количество байт, которые нужно прочитать
   ); 
 
Vitaly Muzichenko:

Ренат, а почему не убрать ограничение на количество чтения? Зачем это ограничение, если ответ уже к нам пришёл?

Почему не отдать полный ответ и далее с ним работать по-усмотрению?

Сразу отдавать пришедшие данные нельзя, так как TLS использует блочное декодирование вплоть до 16 кб на блок. Поэтому любая TLS реализация собирает сырые пакеты и только при возможности декодировки очередного пакета, начинает отдавать запрашивающему.

Если отдавать любой плавающий декодированный размер вместо запрошенного размера, то получится другая, даже еще более страшная проблема. Вам надо 10 байт, а вы вдруг получили 12 кб. Теперь вам нужно принимать нечеловеческие усилия для собственной буферизации и работы с этим буфером вместо прямого чтения из сокета.

С сетевыми операциями очень рекомендуется использовать четкий бинарный протокол с прямым указанием размеров. То есть, фреймовую структуру типа [size,type][...data...], чтобы на любом этапе четко знать сколько нужно читать. Для такого случая текущая реализация SocketTlsRead подходит хорошо.

А иначе остаются тормоза и неудобности нечетких протоколов типа текстового HTTP, где в реальности приходится вычитывать строку по одному символу или буферизированно/сложнее ради скорости.


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

В принципе, мы можем сделать возможность при указании нулевого размера выдавать весь доступный объем данных в функциях SocketRead и SocketTlsRead. Это будет гибко.

 
Renat Fatkhullin:

А сколько вы читаете данных?

Вы можете мгновенно без ожидания прочесть только то количество байтов, которое вам вернула функция SocketIsReadable. Если попытаетесь прочесть больше, то попадете на синхронное ожидание внутри сетевой WinAPI функции.

В вашем коде явно указано "читать 100 байт вне зависимости от наличия доступных данных во входящем буфере сокета". При том, что вы сами указали "сервер отправляет только 16 байт ответа". То есть, вы четко/жестко требуете получения 100 байт и получаете закономерный таймаут. Ведь вам прислали только 16.


Сокеты в MQL5 работают правильно. Но надо знать их особенности.

Интересная особенность, но неочевидная и неудобная. В C# эта же функция работает по-другому, она читает все данные, которые пришли и возвращает кол-во прочитанных байт. К тому же позволяет указать смещение в буфере, по которому эти данные записывать.

int  SocketRead(
   int           socket,               // сокет
   uchar&        buffer[],             // буфер для чтения данных из сокета
   uint          buffer_maxlen         // количество байт, которые нужно прочитать
   ); 

buffer_maxlen - это максимальный размер буфера (у меня 100 байт), судя по имени, а не строгое количество байт, которое надо узнавать заранее у функции SocketIsReadable.

SocketIsReadable - булевская функция, судя по имени, которая просто показывает, что из сокета можно читать.

Это логично. Если в документации не так, то это очень плохо, потому как я полдня убил на то, что на первый взгляд должно работать и в C# работает именно так.

Очень жаль, но MQL за последние 10 лет так и не стал удобным средством программирования. Лучше бы у терминала было открытое API для возможности писать под него на любом удобном языке.

Спасибо за ответы. Тема закрыта.

 
Anton_M:

Интересная особенность, но неочевидная и неудобная. В C# эта же функция работает по-другому, она читает все данные, которые пришли и возвращает кол-во прочитанных байт. К тому же позволяет указать смещение в буфере, по которому эти данные записывать.

buffer_maxlen - это максимальный размер буфера (у меня 100 байт), судя по имени, а не строгое количество байт, которое надо узнавать заранее у функции SocketIsReadable.

SocketIsReadable - булевская функция, судя по имени, которая просто показывает, что из сокета можно читать.

Это логично. Если в документации не так, то это очень плохо, потому как я полдня убил на то, что на первый взгляд должно работать и в C# работает именно так.

Очень жаль, но MQL за последние 10 лет так и не стал удобным средством программирования. Лучше бы у терминала было открытое API для возможности писать под него на любом удобном языке.

Спасибо за ответы. Тема закрыта.

Вы действовали по наитию, не удосужившись прочитать документацию, где четко сказано, что:

  • Возвращаемое значение у SocketIsReadable - количество байт, которые можно прочитать. В случае ошибки возвращает 0.

  • Параметр buffer_maxlen у SocketRead - количество байт, которые необходимо прочитать в массив buffer[]. Данные, которые не поместятся в массив, останутся в сокете. Их можно будет получить следующим вызовом SocketRead.


Дожили:

  • программист не читает документацию (она обширнейшая, около 6000 страниц с примерами и разъяснениями)
  • не видит ни статей, ни форума, где ему детально объясняют функционирование
  • все равно считает свое видение главнее
и выдает сказочный вывод "не стал удобным средством". Это вообще-то программирование, да еще и нетривиальное сетевое. Это всегда боль и нет никаких легких средств.


 
Renat Fatkhullin:

Сразу отдавать пришедшие данные нельзя, так как TLS использует блочное декодирование вплоть до 16 кб на блок. Поэтому любая TLS реализация собирает сырые пакеты и только при возможности декодировки очередного пакета, начинает отдавать запрашивающему.

Если отдавать любой плавающий декодированный размер вместо запрошенного размера, то получится другая, даже еще более страшная проблема. Вам надо 10 байт, а вы вдруг получили 12 кб. Теперь вам нужно принимать нечеловеческие усилия для собственной буферизации и работы с этим буфером вместо прямого чтения из сокета.

С сетевыми операциями очень рекомендуется использовать четкий бинарный протокол с прямым указанием размеров. То есть, фреймовую структуру типа [size,type][...data...], чтобы на любом этапе четко знать сколько нужно читать. Для такого случая текущая реализация SocketTlsRead подходит хорошо.

А иначе остаются тормоза и неудобности нечетких протоколов типа текстового HTTP, где в реальности приходится вычитывать строку по одному символу или буферизированно/сложнее ради скорости.


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

В принципе, мы можем сделать возможность при указании нулевого размера выдавать весь доступный объем данных в функциях SocketRead и SocketTlsRead. Это будет гибко.

Кстати да, это будет отличное решение!

 

Прошу указать причину появления ошибки в примере ниже.

Функция SocketIsReadable возвращает значение 1292, и никакой ошибки не возникает:

Однако в дальнейшем функция SocketTlsReadAvailable возвращает -1, и возникает ошибка 5273:


 

Кстати, ошибка возникает только если последний параметр в SocketTlsReadAvailable включает первый символ с нулевым кодом в результате:

Причина обращения: