跳至内容

SocketTlsReadAvailable

SocketTlsReadAvailable

读取来自安全TLS连接的所有可用数据。

int  SocketTlsReadAvailable(
   int           socket,               // 套接字
   uchar&        buffer[],             // 用于从套接字读取数据的缓冲区
   const uint    buffer_maxlen         // 即将读取的字节数
   );

参数

socket

[in] 套接字句柄通过SocketCreate函数返回。当不正确的句柄传递到_LastError时,错误号5270 (ERR_NETSOCKET_INVALIDHANDLE)被激活。

buffer

[out] 引用读取数据的uchar类型数组。动态数组大小随着读取字节数的增加而增加。数组大小不能超过INT_MAX (2147483647)。

buffer_maxlen

[in] 即将读取到buffer[]数组的字节数。不适合数组的数据保留在套接中。它们可以通过下一次调用SocketTlsReadAvailable或SocketTlsRead来接收。buffer_maxlen不能超过INT_MAX (2147483647)。

返回值

如果成功,则返回读取字节数。错误的话,返回-1。

注意

如果执行函数时系统套接字发生错误,那么通过SocketConnect建立的连接则中断。

如果数据读取错误,错误号5273 (ERR_NETSOCKET_IO_ERROR)会被写入_LastError

这个函数只能从EA交易和脚本中调用,因为它们在自己的执行线程中运行。如果从指标调用,GetLastError()则返回4014错误号– “函数不允许调用”。

示例:

//+------------------------------------------------------------------+
//|                                       SocketTlsReadAvailable.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com
#property version     "1.00"
#property script_show_inputs
//+------------------------------------------------------------------+
//| 脚本输入参数                                                       |
//+------------------------------------------------------------------+
input string InpMethod ="GET";            // Method (HEAD,GET)
input string InpServer ="www.google.com"; // Server
input uint   InpPort   =443;              // Port
input uint   InpTimeout=5000;             // Timeouts
//+------------------------------------------------------------------+
//| 脚本程序起始函数                                                   |
//+------------------------------------------------------------------+
void OnStart(void)
  {
   Print("Server: ",InpServer);
   Print("Port: ",InpPort);
//--- 创建一个套接字并取得它的句柄
   const int socket=SocketCreate();

   if(socket==INVALID_HANDLE)
     {
      Print("SocketCreate() failed. Error ",GetLastError());
      return;
     }
//--- 为套接字系统对象设置接收和发送数据的超时
   if(!SocketTimeouts(socket,InpTimeout,InpTimeout))
     {
      PrintFormat("SocketTimeouts(%u, %u) failed. Error %d",InpTimeout,InpTimeout,GetLastError());
      SocketClose(socket);
      return;
     }
//--- 通过端口连接到服务器
   if(!SocketConnect(socket,InpServer,InpPort,InpTimeout))
     {
      PrintFormat("SocketConnect('%s', %u, %u) failed. Error %d",InpServer,InpPort,InpTimeout,GetLastError());
      SocketClose(socket);
      return;
     }
//--- 获取用于保护网络连接的证书数据
   string   subject,issuer,serial,thumbprint;
   datetime expiration;

   if(SocketTlsCertificate(socket,subject,issuer,serial,thumbprint,expiration))
     {
      Print("TLS certificate:");
      Print("   Owner:      ",subject);
      Print("   Issuer:     ",issuer);
      Print("   Number:     ",serial);
      Print("   Print:      ",thumbprint);
      Print("   Expiration: ",expiration);
     }
   else
     {
      //--- 服务器不提供证书-报告不安全的连接并离开
      Print("The connection is not secured by a certificate");
      SocketClose(socket);
      return;
     }
//--- 向服务器发送请求
   string request=StringFormat("%s / HTTP/1.1\r\nHost: %s\r\nUser-Agent: MetaTrader 5\r\n\r\n",InpMethod,InpServer);

   if(HTTPSendTLS(socket,request))
     {
      //--- 请求已发送-获取响应
      Print("\nRequest sent. Starting page loading...");
      uchar response[]; // all received data (document header and body)

      if(!HTTPRecvTLS(socket,response,InpTimeout))
        {
         Print("There were errors while reading the page");
         SocketClose(socket);
         return;
        }
      //--- 报告接收到的数据字节数
      PrintFormat("%u bytes received",response.Size());
      //--- 仅显示获取的页头部
      string result    =CharArrayToString(response,0,WHOLE_ARRAY,CP_UTF8);
      int    header_end=StringFind(result,"\r\n\r\n");

      if(header_end>0)
        {
         Print("\nHTTP answer header received:");
         Print(StringSubstr(result,0,header_end));
        }
     }
//--- 在使用后关闭套接字
   SocketClose(socket);
   /*
   结果:
   Server: www.google.com
   Port: 443
   TLS certificate:
      Owner: /CN=www.google.com
      Issuer: /C=US/O=Google Trust Services/CN=WR2
      Number: 0d:43:b1:4a:bb:9c:15:96:10:e1:3d:55:23:9f:25:4e
      Print: 89167618e5017f813aff981c88ce422dc1016bdf
      Expiration: 2024.12.30 08:26:35

   Request sent. Starting page loading...
   HTTPRecvTLS: Document received within 27 attempts
   25185 bytes received

   HTTP answer header received:
   HTTP/1.1 200 OK
   Date: Fri, 25 Oct 2024 17:12:42 GMT
   Expires: -1
   Cache-Control: private, max-age=0
   Content-Type: text/html; charset=ISO-8859-1
   Content-Security-Policy-Report-Only: object-src 'none';base-uri 'self';script-src 'nonce-CUL2rdUOeAN7xIV6v0WUuQ' 'strict-dynamic' 'report-sample' 'unsafe-eval' 'unsafe-inline' https: http:;report-uri https://csp.withgoogle.com/csp/gws/other-hp
   Accept-CH: Sec-CH-Prefers-Color-Scheme
   P3P: CP="This is not a P3P policy! See g.co/p3phelp for more info."
   Server: gws
   X-XSS-Protection: 0
   X-Frame-Options: SAMEORIGIN
   Set-Cookie: AEC=AVYB7coyYMCdweTDTaWeGYzmRnxzKGqsOEosH_VkbCn8xhWkFz6v0kxQFw; expires=Wed, 23-Apr-2025 17:12:42 GMT; path=/; domain=.google.com; Secure; HttpOnly; SameSite=lax
   Set-Cookie: NID=518=J02X02Ff4v_9sMcNoUz-1SolmuG08E26Gs438ik0J_SOJUMy7of-P-qup-LaNSWVXUL8OjhOXpGIGuJQGIoEPBnzqDKCH-46_FN4J2foHeWTlGG8bVVvQ44AHWLg1OXjrGp3CUBexYdczLWNy3LxEcb7eh6mxSvFzOelPC6-vpXkaumLQ80x9gF_RpLcAYfN4ehT; expires=Sat, 26-Apr-2025 17:12:42 GMT; path=/; domain=.google.com; HttpOnly
   Alt-Svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
   Accept-Ranges: none
   Vary: Accept-Encoding
   Transfer-Encoding: chunked
   */
  }
//+------------------------------------------------------------------+
//| 通过安全连接发送HTTP请求                                            |
//+------------------------------------------------------------------+
bool HTTPSendTLS(int socket,const string request)
  {
//--- 把字符串转换成字符数组, 丢弃结尾的零
   char req[];
   int  len=StringToCharArray(request,req,0,WHOLE_ARRAY,CP_UTF8)-1;

   if(len<0)
      return false;

   return(SocketTlsSend(socket,req,len)==len);
  }
//+------------------------------------------------------------------+
//| 通过安全连接获取网页                                                |
//+------------------------------------------------------------------+
bool HTTPRecvTLS(int socket,uchar &response[],const uint timeout_ms)
  {
//--- 在超时到期之前从安全TLS连接读取可用数据
   ulong timeout_check=GetTickCount64()+timeout_ms;
   uchar block[1024];   // 用于从套接字读取块数据的缓冲区
   uint  attempt=0;     // 请求的数据块数量
   int   err    =0;     // 错误代码

   ResetLastError();

   do
     {
      //--- 读入块,最大1024字节
      int len=SocketTlsReadAvailable(socket,block,1024);

      if(len>0)
        {
         attempt++;
         //--- 合并获得的数据块
         ArrayCopy(response,block,response.Size());
         //--- 分析获得的数据,定义头部、页主体、结尾或加载错误等。
         //...
         //...
         //...
         timeout_check=GetTickCount64()+timeout_ms;
        }
      else
         Sleep(10);

      err=GetLastError();
     }
   while(!IsStopped() && GetTickCount()<timeout_check && !err);
//--- 读取时有错误吗?
   if(err)
     {
      Print("Error ",err);
      return(false);
     }

   PrintFormat("%s: Document received within %d attempts",__FUNCTION__,attempt);
   return(true);
  }

另见

SocketTimeoutsMathSwap