Connexus客户端(第七部分):添加客户端层(基础篇)
给 Connexus 补上客户端层
在 MetaTrader 5 里做 Connexus 客户端,第七步是把「客户端层」接进现有架构。这一层不负责策略计算,只管把终端里的信号、账户状态往外推,以及把远端指令收回来。 没有客户端层时,EA 直接调传输层,耦合重、难调试;加上这层后,逻辑边界清晰,后续换通信协议也不用动交易核心。2025 年 7 月 4 日发布的示例里,作者 joaopedrodev 的客户端骨架已被 542 次查看,说明这类分层写法在实盘圈有真实需求。 落地时先在 Includes 下建 Client.mqh,用 CClient 类封装 Socket 收发;MT5 里右键「文件」→「打开数据文件夹」就能看到对应目录,改完重编译即可在 Experts 里引用。外汇与贵金属杠杆高,联调客户端层时务必用模拟账户跑通再上真仓。
「Connexus 库当前架构与发送目标」
这个系列在搭一个名为 Connexus 的 MQL5 网络请求库。第一篇已经把 WebRequest 函数的参数和调用难点跑通,上一篇理清了 HTTP 方法与服务器状态码的含义,本篇接着把「发请求」这件事封装进类里。 目前库的状态是一张架构图:核心目标是接收一个 CHttpRequest 对象——它已经配好 URL、方法、请求头、请求体和超时时间——然后由内部调用 WebRequest 真正发出去。 发完之后要返回一个 CHttpResponse 对象,里面必须带响应头、响应体、状态码,以及这次请求耗的总时长。第七篇才会接上最受期待的发送实现,现在先认清楚输入输出边界,免得后面调参时搞混谁该管超时、谁该管重试。
◍ 把请求对象真正发出去的 HttpClient 核心类
做 HTTP 客户端的最后一步,是写一个由调用方直接实例化的类,把封装好的请求对象转成实际网络调用,再塞回响应对象里。文件放在 Include/Connexus/Core/CHttpClient.mqh,它不继承任何东西,只负责把 CHttpRequest 和 CHttpResponse 对接到 MT5 的 WebRequest 传输层。 类的对外入口只有一个 Send() 方法:接收 CHttpRequest 与 CHttpResponse 两个引用,返回 bool。内部先用 uchar 数组 body_request 从请求体里取出二进制内容;再建两个变量接响应体和响应头;紧接着调用 WebRequest 的精简参数版本,把 URL、超时以及上面两个接收变量一并传进去。 为了能量化延迟,发送前后各调一次 GetMicrosecondCount(),用 (结束时间 - 开始时间)/1000 算出毫秒级耗时。无论成败,都先对响应对象 Clear() 复位,再写入耗时、状态码、响应体与分析用的响应头。 判定成功的标准很硬:返回码落在 200–299 区间才视为请求完成并返回 true,否则返回 false,详细排查只看 CHttpResponse 里的字段。架构上,所有底层类最终都收口进这个 HttpClient,调用方只跟它打交道。 下面这段是类的头文件骨架,已经带好了三个 include 和类注释,复制进对应路径就能往下补 Send() 实现。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| HttpClient.mqh | class=class="str">"cmt">//| Copyright class="num">2024, MetaQuotes Ltd. | class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Copyright class="num">2024, MetaQuotes Ltd." class="macro">#class="kw">property link "[MQL5官方文档] class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Include files | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#include "HttpRequest.mqh" class="macro">#include "HttpResponse.mqh" class="macro">#include "../Constants/HttpMethod.mqh" class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| class : CHttpClient | class=class="str">"cmt">//| | class=class="str">"cmt">//| [PROPERTY] | class=class="str">"cmt">//| Name : CHttpClient | class=class="str">"cmt">//| Heritage : No heritage | class=class="str">"cmt">//| Description : Class responsible for linking the request and | class=class="str">"cmt">//| response object with the transport layer.
给 EA 套一层 HTTP 客户端壳
想在 MT5 里直接拉取外部行情或把信号推到自建服务,第一步是把网络请求封装成类。下面这段先给出 CHttpClient 的空构造与析构,以及后续要填的 Send 方法声明,相当于把通信层骨架立起来。 核心发送能力依赖 MQL5 内置的 WebRequest 函数,它要求显式传入 method、url、headers、timeout 与请求体字节数组,并返回响应体与响应头。注意该函数只能在 EA / 脚本中调用,指标里默认禁用以防卡顿。 Send 方法目前只写了前三步:把请求对象转成二进制体、准备响应容器、标记发送节点。真要跑通,还得在「3. Send」处调用 WebRequest 并做错误码判断,外汇与贵金属行情接口普遍波动大,超时设 1000~3000 毫秒可能更稳。
class CHttpClient { class="kw">public: CHttpClient(class="type">void); ~CHttpClient(class="type">void); }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Constructor | class=class="str">"cmt">//+------------------------------------------------------------------+ CHttpClient::CHttpClient(class="type">void) { } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Destructor | class=class="str">"cmt">//+------------------------------------------------------------------+ CHttpClient::~CHttpClient(class="type">void) { } class=class="str">"cmt">//+------------------------------------------------------------------+ class CHttpClient { class="kw">public: CHttpClient(class="type">void); ~CHttpClient(class="type">void); class=class="str">"cmt">//--- Basis function class="type">bool Send(CHttpRequest &request, CHttpResponse &response); }; class="type">int WebRequest( class="kw">const class="type">class="kw">string method, class=class="str">"cmt">// HTTP method class="kw">const class="type">class="kw">string url, class=class="str">"cmt">// URL class="kw">const class="type">class="kw">string headers, class=class="str">"cmt">// headers class="type">int timeout, class=class="str">"cmt">// timeout class="kw">const class="type">char &data[], class=class="str">"cmt">// the array of the HTTP message body class="type">char &result[], class=class="str">"cmt">// an array containing server response data class="type">class="kw">string &result_headers class=class="str">"cmt">// headers of server response ); class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Basis function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CHttpClient::Send(CHttpRequest &request, CHttpResponse &response) { class=class="str">"cmt">//--- class="num">1. Request class="type">uchar body_request[]; request.Body().GetAsBinary(body_request); class=class="str">"cmt">//--- class="num">2. Response class="type">uchar body_response[]; class="type">class="kw">string headers_response; class=class="str">"cmt">//--- class="num">3. Send
「用微秒计时抓 WebRequest 的真实耗时」
在 MT5 里发 HTTP 请求,最容易被忽略的是网络往返时间。上面这段把 GetMicrosecondCount 放在 WebRequest 前后,能直接拿到一次调用的微秒级开销,再除以 1000 转成毫秒写进 response.Duration,比靠日志猜快得多。 WebRequest 本身同步阻塞,参数依次是方法、完整 URL、序列化头、超时、请求体数组、响应体数组、响应头数组;返回值是 HTTP 状态码,靠 IsSuccess 判断成败。外汇与贵金属行情接口若走这条通道,阻塞期间 EA 不做其他事,高频轮询要评估卡顿风险。 响应回来后先 response.Clear 防止上次残留,再把耗时、状态码、二进制体和解析后的头填进去。开 MT5 把这段塞进你的请求封装函数,打印 Duration 字段,就能知道本地到券商 API 的延迟大致落在几毫秒到几十毫秒区间,方便后续设超时阈值。
class="type">class="kw">ulong start = GetMicrosecondCount(); class="type">int status_code = WebRequest( request.Method().GetMethodDescription(), class=class="str">"cmt">// HTTP method request.Url().FullUrl(), class=class="str">"cmt">// URL request.Header().Serialize(), class=class="str">"cmt">// Headers request.Timeout(), class=class="str">"cmt">// Timeout body_request, class=class="str">"cmt">// The array of the HTTP message body body_response, class=class="str">"cmt">// An array containing server response data headers_response class=class="str">"cmt">// Headers of server response ); class="type">class="kw">ulong end = GetMicrosecondCount(); class=class="str">"cmt">//--- class="num">4. Add data in Response response.Clear(); response.Duration((end-start)/class="num">1000); response.StatusCode() = status_code; response.Body().AddBinary(body_response); response.Header().Parse(headers_response); class=class="str">"cmt">//--- class="num">5. Return is success class="kw">return(response.StatusCode().IsSuccess()); }
◍ 用 httpbin 跑通第一笔请求
验证外部 API 连通性,最快的办法是拿 httpbin.org 当镜子:你发什么,它原样回什么。这个免费服务适合在 MT5 里确认请求是否真的发出、服务端收到了哪些字段,而不用自己搭后端。 在 Experts/Connexus/Test/ 下新建 TestRequest.mq5,只在 OnInit 里写逻辑即可。核心就三个对象:CHttpRequest 定 URL 和方法(GET 下超时默认 5000 毫秒,body 和 header 可空),CHttpResponse 接状态码、状态描述、耗时毫秒数和响应体,CHttpClient 负责真正把请求送出去。 调 Send() 后,终端会打印出状态码、耗时、响应头与响应体。对比裸写 WinAPI 的方式,套这层封装后开发者能用字符串、JSON 或二进制直接读响应体,也能改 URL/body/header 复用同一对象连发多个请求。外汇与贵金属行情接口调用涉及网络与券商限制,实盘前务必先在模拟环境验证,失败概率始终存在。 下面这段是最小可运行骨架,注意 #include 路径要跟你的库放置一致,否则编译期就报找不到文件。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| TestRequest.mq5 | class=class="str">"cmt">//| Copyright class="num">2024, MetaQuotes Ltd. | class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Copyright class="num">2024, MetaQuotes Ltd." class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">//--- Initialize class="kw">return(INIT_SUCCEEDED); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Include files | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#include <Connexus/Core/HttpClient.mqh> class=class="str">"cmt">//--- Request object CHttpRequest request; request.Method() = HTTP_METHOD_GET; request.Url().Parse("https:class=class="str">"cmt">//httpbin.org/get"); class=class="str">"cmt">//--- Response object CHttpResponse response; class=class="str">"cmt">//--- Client http object CHttpClient client; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| TestRequest.mq5 | class=class="str">"cmt">//| Copyright class="num">2024, MetaQuotes Ltd. | class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Copyright class="num">2024, MetaQuotes Ltd." class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" class=class="str">"cmt">//+------------------------------------------------------------------+