Connexus客户端(第七部分):添加客户端层·综合运用
把请求和传输层解耦的客户端骨架
在 MT5 里做 REST 类行情或信号接口调用,最忌把 WebRequest 散落在业务代码各处。下面这段代码给出一种干净做法:用 CHttpTransport 包一层系统函数,再用 CHttpClient 把请求/响应对象与传输层绑起来。 CHttpTransport::Request 本身只是 WebRequest 的薄封装,入参依次是方法、URL、头、超时(毫秒)、发送体、接收体、响应头引用。真正的解耦点在 CHttpClient:它私有持有 IHttpTransport* 指针,对外只暴露 Send(CHttpRequest&, CHttpResponse&),业务层完全不碰底层网络实现。 这种结构让你换传输实现(例如本地代理转发)时,只改 m_transport 的实例,不用动调用方。外汇与贵金属接口调用涉及实时报价与下单,属于高风险操作,任何超时或返回异常都应按概率失效处理,而非假设必通。
class="type">int CHttpTransport::Request(class="kw">const class="type">class="kw">string method,class="kw">const class="type">class="kw">string url,class="kw">const class="type">class="kw">string headers,class="type">int timeout,class="kw">const class="type">char &data[],class="type">char &result[],class="type">class="kw">string &result_headers) { class="kw">return(WebRequest(method,url,headers,timeout,data,result,result_headers)); } class="macro">#include "../Interface/IHttpTransport.mqh" class CHttpClient { class="kw">private: IHttpTransport *m_transport; class=class="str">"cmt">// Instance to store the transport implementation 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); };
「把请求响应塞进传输层:CHttpClient 怎么写」
在 MT5 里做 REST 类行情抓取或信号推送,最怕把传输细节和报文组装搅在一起。下面这个类只干一件事:持有一个传输层指针,把 request / response 对象桥接到具体传输实现上,本身不碰 socket 细节。 类声明里 m_transport 是 IHttpTransport* 私有成员,用来存传输实现;公开侧给了两个构造,一个吃外部传入的 transport,一个无参时直接 new 一个 CHttpTransport()。这种双构造让你在回测和实盘可以换不同传输后端。 Send 方法是主干:先从 request.Body() 取出二进制体到 body_request[],再准备 body_response[] 和 headers_response 承接返回。注意这里只贴了前半段,真正发往 m_transport 的调用在原文后续节,但仅看这段就能确认——请求体走 GetAsBinary 而非字符串拼接,省了编码踩坑。 别把正态当圣经:直接抄这段进 EA,若忘了给无参构造的 CHttpTransport 释放内存,跑几天可能漏句柄;开 MT5 用 Class 视图看一眼继承树,确认 IHttpTransport 在你版本里不是纯虚摆设再编译。外汇与贵金属接口调用涉及网络延迟与断连,属高风险操作,参数和超时都得自己压测。
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. | class=class="str">"cmt">//| | class=class="str">"cmt">//+------------------------------------------------------------------+ class CHttpClient { class="kw">private: IHttpTransport *m_transport; class=class="str">"cmt">// Instance to store the transport implementation class="kw">public: CHttpClient(IHttpTransport *transport); CHttpClient(class="type">void); }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Constructor | class=class="str">"cmt">//+------------------------------------------------------------------+ CHttpClient::CHttpClient(IHttpTransport *transport) { m_transport = transport; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Constructor | class=class="str">"cmt">//+------------------------------------------------------------------+ CHttpClient::CHttpClient(class="type">void) { m_transport = new CHttpTransport(); } class=class="str">"cmt">//+------------------------------------------------------------------+ 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">//--- Request class="type">uchar body_request[]; request.Body().GetAsBinary(body_request); class=class="str">"cmt">//--- Response class="type">uchar body_response[]; class="type">class="kw">string headers_response;
◍ 给请求掐表看真实耗时
在 MT5 里做 HTTP 请求,最容易被忽略的是网络往返本身吃掉的时间。上面这段把发送前后的微秒计数抓出来,相减再除以 1000 得到毫秒级耗时,写进 response 的 Duration 字段,比只看状态码实在。 GetMicrosecondCount 返回的是微秒,精度足够暴露一次 HTTPS 调用是 20ms 还是 800ms。若你接的是行情类 API,这个差值能直接帮你判断是该换节点还是限频。 状态码成功与否只由 IsSuccess 决定,但 Duration 留下来后,你能在日志里横向比不同 URL 的延迟。外汇与贵金属接口普遍波动大,高杠杆下慢响应可能让你错过入场,记得把超时和耗时当独立指标看。
class=class="str">"cmt">//--- Send class="type">class="kw">ulong start = GetMicrosecondCount(); class="type">int status_code = m_transport.Request(request.Method().GetMethodDescription(),request.Url().FullUrl(),request.Header().Serialize(),request.Timeout(),body_request,body_response,headers_response); class="type">class="kw">ulong end = GetMicrosecondCount(); class=class="str">"cmt">//--- 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">//--- Return is success class="kw">return(response.StatusCode().IsSuccess()); }
收束
把 CHttpClient 从 WebRequest 上拆下来,靠的是接口加模拟对象这一层客户端抽象。解耦之后,调用方不必真发请求就能跑通各种异常与正常分支,单元测试和后续替换传输实现都不再被底层网络卡住。 对外汇与贵金属自动化交易而言,这类库层改动不直接产生信号,但能降低 EA 在 MT5 实盘接入行情或经纪商 API 时的耦合风险;高杠杆品种下,任何通信层的不确定都可能放大滑点与断连损失。 Connexus 这轮客户端层落地后,开发者拿到的是可_mock_的 HTTP 骨架,下一步接真实 endpoint 时只需换实现,不必动业务代码。