建立和断开网络套接字连接

在前面的章节中,我们熟悉了 MQL5 的高级网络函数:它们各自为特定的应用协议提供支持。例如,SMTP 用于发送电子邮件 (SendMail),FTP 用于文件传输 (SendFTP),而 HTTP 允许接收 Web 文档 (WebRequest)。所有提及的标准都基于更底层的传输层 TCP(Transmission Control Protocol,传输控制协议)。它在层级结构中并非最底层,还有更低层次的协议,但我们在此不作讨论。

应用协议的标准实现隐藏了许多内部的技术细节,使程序员无需花费数小时例行公事般地遵循规范。然而,标准实现缺乏灵活性,并且没有考虑到标准中嵌入的高级特性。因此,有时需要在 TCP 层面,即套接字层面进行网络通信编程。

可以将套接字比作磁盘上的文件:套接字也由一个整数描述符来描述,通过该描述符可以读取或写入数据,但这发生在分布式的网络基础设施中。与文件不同,计算机上的套接字数量是有限的,因此在将套接字描述符与网络资源(地址、URL)关联之前,必须预先向系统请求它。我们还要预先说明,通过套接字访问信息是流式的,也就是说,不可能像在文件中那样将某个“指针”回退到开头。

写入和读取线程互不相交,但可能会影响未来的读取或写入数据,因为传输的信息通常被服务器和客户端程序解释为控制命令。协议标准定义了流中包含的是命令还是数据。

SocketCreate 函数允许在 MQL5 中创建一个“空的”套接字描述符。

int SocketCreate(uint flags = 0)

其唯一的参数保留给将来用于指定决定套接字模式的标志位模式,但目前仅支持一个存根标志:SOCKET_DEFAULT 对应当前模式,可以省略。在系统层面,这相当于一个阻塞模式的套接字(网络程序员可能会对此感兴趣)。

如果成功,函数返回套接字句柄。否则,返回 INVALID_HANDLE。

一个 MQL 程序最多可以创建 128 个套接字。当超出限制时,错误 5271 (ERR_NETSOCKET_TOO_MANY_OPENED) 会被记录到 _LastError 中。

打开套接字后,应将其与网络地址关联。

bool SocketConnect(int socket, const string server, uint port, uint timeout)

SocketConnect 函数使套接字连接到位于指定地址和端口的服务器(例如,Web 服务器通常分别在 HTTP 和 HTTPS 的 80 或 443 端口上运行,SMTP 在 25 端口上运行)。地址可以是域名或 IP 地址。

timeout 参数允许你设置等待服务器响应的超时时间(以毫秒为单位)。

函数返回成功连接的标志 (true) 或错误 (false)。错误代码写入 _LastError,例如 5272 (ERR_NETSOCKET_CANNOT_CONNECT)。

请注意,连接地址必须添加到终端设置中允许的地址列表中(对话框Service -> Settings -> Advisors)。

完成网络工作后,应使用 SocketClose 释放套接字。

bool SocketClose(const int socket)

SocketClose 函数通过其句柄关闭先前使用 SocketCreate 函数打开的套接字。如果套接字先前已通过 SocketConnect 连接,则连接将被断开。

函数还会返回成功 (true) 或错误 (false) 的指示。特别是,向 _LastError 传递无效句柄时,将在日志中记录错误 5270 (ERR_NETSOCKET_INVALIDHANDLE)。

需要注意的是,本节及后续章节的所有函数都禁止在指标中使用:在指标中尝试使用套接字将导致出现错误 4014(ERR_FUNCTION_NOT_ALLOWED“系统函数不允许被调用”)。

来看一个入门示例:SocketConnect.mq5 脚本。在输入参数中,你可以指定服务器的地址和端口。我们应该从像 mql5.com 这样的常规 Web 服务器开始测试。

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)))
   {
      PRTF(SocketClose(socket));
   }
}

如果终端中的所有设置都正确并且已连接到互联网,我们将得到以下“报告”。

Server=www.mql5.com / ok
Port=443 / ok
SocketCreate()=1 / ok
SocketConnect(socket,Server,Port,5000)=true / ok
SocketClose(socket)=true / ok