Connexus客户端(第七部分):添加客户端层·综合运用
📘

Connexus客户端(第七部分):添加客户端层·综合运用

第 3/3 篇

把请求和传输层解耦的客户端骨架

在 MT5 里做 REST 类行情或信号接口调用,最忌把 WebRequest 散落在业务代码各处。下面这段代码给出一种干净做法:用 CHttpTransport 包一层系统函数,再用 CHttpClient 把请求/响应对象与传输层绑起来。 CHttpTransport::Request 本身只是 WebRequest 的薄封装,入参依次是方法、URL、头、超时(毫秒)、发送体、接收体、响应头引用。真正的解耦点在 CHttpClient:它私有持有 IHttpTransport* 指针,对外只暴露 Send(CHttpRequest&, CHttpResponse&),业务层完全不碰底层网络实现。 这种结构让你换传输实现(例如本地代理转发)时,只改 m_transport 的实例,不用动调用方。外汇与贵金属接口调用涉及实时报价与下单,属于高风险操作,任何超时或返回异常都应按概率失效处理,而非假设必通。

MQL5 / C++
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 在你版本里不是纯虚摆设再编译。外汇与贵金属接口调用涉及网络延迟与断连,属高风险操作,参数和超时都得自己压测。

MQL5 / C++
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 的延迟。外汇与贵金属接口普遍波动大,高杠杆下慢响应可能让你错过入场,记得把超时和耗时当独立指标看。

MQL5 / C++
  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 时只需换实现,不必动业务代码。

常见问题

用客户端层只管拼请求和解析响应,传输层只管发收数据;两层通过明确接口传参,互不碰内部状态,后续换协议只动传输层。
至少写 Send() 发请求、Receive() 收响应、Parse() 抽字段;构造时传入地址和超时,调用方拿不到 socket 细节。
可以。小布能按你贴的客户端代码跑诊断,逐请求掐表给出耗时分布,并提示哪段解析或等待拖了后腿。
你测的是端到端含序列化与解析,broker 只算网络往返;在 Send 前后和 Receive 后分别打点,就能拆出真实瓶颈。
补错误码映射、超时重试上限、日志开关;收束时把层间依赖写进注释,别人接手能直接按层改。