检查套接字状态

使用套接字时,有必要检查其状态,因为分布式网络不像文件系统那样可靠。特别是,连接可能会因某种原因丢失。SocketIsConnected 函数允许你查明这一点。

bool SocketIsConnected(const int socket)

该函数会检查具有指定句柄(从 SocketCreate获得)的套接字是否已连接到其网络资源(在 Socket Connect中指定),并在成功时返回 true

另一个函数 SocketIsReadable 让你知道与套接字关联的系统缓冲区中是否有任何可读取的数据。这意味着我们通过网络地址连接到的计算机已向我们发送(并可能继续发送)数据。

uint SocketIsReadable(const int socket)

该函数返回可以从套接字读取的字节数。如果出错,返回 0。

熟悉 Windows/Linux 套接字系统 API 的程序员知道,当套接字内部缓冲区中没有传入数据时,值 0 也可能是一种正常状态。然而,该函数在 MQL5 中的行为有所不同。当系统套接字缓冲区为空时,它会推测性地返回 1,将实际的数据可用性检查推迟到下一次调用某个读取函数时。特别是,这种返回虚拟 1 字节结果的情况,通常发生在首次对套接字调用该函数且接收内部缓冲区仍为空时。

执行此函数时可能会发生错误,这意味着通过 SocketConnect 建立的连接已断开(在 _LastError 中我们将得到代码 5273,ERR_NETSOCKET_IO_ERROR)。

SocketIsReadable 函数在设计用于使用 SocketRead进行“非阻塞”数据读取的程序中很有用。关键在于,当接收缓冲区中没有数据时,SocketRead 函数会等待数据到达,从而暂停程序的执行(暂停时间取决于指定的超时值)。

另一方面,阻塞读取在某种意义上更可靠,因为一旦新数据到达,你的程序就会“唤醒”,但是需要根据其他一些事件(通常是定时器或在循环中)定期使用 SocketIsReadable 检查数据是否存在。

TLS 安全模式下使用 SocketIsReadable 函数时应特别小心。该函数返回“原始”数据的量,在 TLS 模式下,这是一个加密块。如果“原始”数据尚未累积到解密块的大小,则后续调用读取函数 SocketTlsRead 将阻塞程序执行,等待缺失的片段。如果“原始”数据已包含准备好解密的块,则读取函数将返回比“原始”字节数少的已解密字节。因此,启用 TLS 时,建议始终将 SocketIsReadable 函数与 SocketTlsReadAvailable结合使用。否则,程序的行为将与预期不同。遗憾的是,MQL5 没有提供与 TLS 模式兼容且不强加所述约定的 SocketTlsIsReadable 函数。

类似的 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)