Проверка состояния сокета

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

bool SocketIsConnected(const int socket)

Функция проверяет, подключен ли сокет с указанным дескриптором (полученным из SocketCreate) к своему сетевому ресурсу (указанному в SocketConnect), и возвращает true в случае успеха.

Другая функция SocketIsReadable позволяет узнать, не появились ли в системном буфере, связанном с сокетом, данные для чтения. Это означает, что компьютер, к которому мы подключились по сетевому адресу, прислал (и может быть продолжает присылать) нам данные.

uint SocketIsReadable(const int socket)

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

Программисты, знакомые с системными API сокетов в Windows/Linux, знают, что значение 0 также может быть нормальным состоянием, когда во внутреннем буфере сокета нет входящих данных. Однако данная функция в MQL5 ведет себя иначе. При пустом системном буфере сокета, она спекулятивно возвращает 1, откладывая реальную проверку на доступность данных на следующий вызов одной из функций чтения. В частности, данная ситуация с фиктивным результатом 1 байт возникает, как правило, при первом вызове функции на сокете, когда приемный внутренний буфер еще пуст.

При выполнении этой функции может произойти ошибка, означающая что соединение, установленное через SocketConnect, было разорвано (в _LastError попадет код 5273, ERR_NETSOCKET_IO_ERROR).

Функцию SocketIsReadable полезно использовать в программах, которые спроектированы под "неблокирующее" чтение данных с помощью SocketRead. Дело в том, что функция SocketRead при отсутствии данных в приемном буфере будет ожидать их поступления, приостановив исполнение программы (на заданную величину таймаута).

С другой стороны, блокирующее чтение более надежно в том плане, что ваша программа "проснется" сразу же по приходу новых данных, а вот проверки на их наличие с помощью SocketIsReadable нужно делать периодически, по неким другим событиям (как правило, по таймеру или в цикле).

Особую осторожность следует проявить при использование функции SocketIsReadable в защищенном режиме TLS. Функция возвращает количество "сырых" данных, которые в режиме TLS представляют собой зашифрованный блок. Если "сырые" данные еще не накоплены в размере блока дешифрования, то последующий вызов функции чтения SocketTlsRead заблокирует выполнение программы, ожидая недостающий фрагмент. Если "сырые" данные уже содержат готовый для дешифрования блок, функция чтения вернет меньшее количество расшифрованных байтов, чем количество "сырых". В связи с этим, при включенном TLS, функцию SocketIsReadable рекомендуется всегда использовать в связке с функцией SocketTlsReadAvailable. В противном случае, поведение программы будет отличаться от ожидаемого. К сожалению, MQL5 не предоставляет функцию SocketTlsIsReadable, совместимую с режимом TLS и не накладывающую описанных условностей.

Похожая функция SocketIsWritable проверяет, возможна ли запись в данный сокет в текущий момент времени.

bool SocketIsWritable(const int socket)

Функция возвращает признак успеха (true) или ошибки (false). В последнем случае, соединение, установленное через SocketConnect, будет разорвано.

Приведем простой скрипт SocketIsConnected.mq5 для проверки функций. Во входных параметрах, как и прежде, предусмотрим возможность ввести адрес и порт.

input string Server = "www.mql5.com";
input uint Port = 443;

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

void OnStart()
{
   PRTF(Server);
   PRTF(Port);
   const int socket = PRTF(SocketCreate());
   if(PRTF(SocketConnect(socketServerPort5000)))
   {
      int i = 0;
      while(PRTF(SocketIsConnected(socket)) && !IsStopped())
      {
         PRTF(SocketIsReadable(socket));
         PRTF(SocketIsWritable(socket));
         Sleep(1000);
         if(++i >= 2)
         {
            PRTF(SocketClose(socket));
         }
      }
   }
}

В журнал выводятся такие записи.

Server=www.mql5.com / ok
Port=443 / ok
SocketCreate()=1 / ok
SocketConnect(socket,Server,Port,5000)=true / ok
SocketIsConnected(socket)=true / ok
SocketIsReadable(socket)=0 / ok
SocketIsWritable(socket)=true / ok
SocketIsConnected(socket)=true / ok
SocketIsReadable(socket)=0 / ok
SocketIsWritable(socket)=true / ok
SocketClose(socket)=true / ok
SocketIsConnected(socket)=false / NETSOCKET_INVALIDHANDLE(5270)