SocketTlsHandshake

通过TLS Handshake协议启动与指定主机的安全TLS (SSL)连接。Handshake(交握)过程中,客户和服务器就连接参数达成一致:应用的协议版本和数据加密方法。

bool  SocketTlsHandshake(
   int           socket,               // 套接字
   const string  host                  // 主机地址
   );

参数

socket

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

host

[in]  建立安全连接的主机地址。

返回值

如果成功返回true,否则返回false。

注释

安全连接之前,该程序应该用SocketConnect与主机建立一个标准的TCP连接。

如果安全连接失败,错误号5274 (ERR_NETSOCKET_HANDSHAKE_FAILED)会被写入_LastError

连接到443端口时,不需要调用该函数。这是一个用于安全TLS(SSL)连接的标准的TCP端口。

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

示例:

//+------------------------------------------------------------------+
//|                                           SocketTlsHandshake.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"
 
#define   SERVER    "smtp.gmail.com"
#define   PORT      587
//+------------------------------------------------------------------+
//| 手动切换到安全连接                                                  |
//+------------------------------------------------------------------+
bool TlsHandshake(int socket)
  {
//--- 获取服务器问候语
   string rsp;
 
   if(!RecvString(socket,rsp))
      return(false);
//--- 问候服务器
   if(!SendString(socket,"EHLO my.domain.com\r\n"))
      return(false);
//--- 获取服务器响应,其中包含支持的命令列表
   if(!RecvString(socket,rsp))
      return(false);
//--- 打印问候语
   Print("SERVER: ",rsp);
//--- 通知服务器,我们想使用TLS从不安全连接切换到安全连接
   if(!SendString(socket,"STARTTLS\r\n"))
      return(false);
//--- 获取服务器响应
   if(!RecvString(socket,rsp))
      return(false);
//--- 在该示例中,我们不检查服务器关于切换到TLS的准备就绪(“准备启动TLS”)的响应
 
//--- 使用TLS握手协议启动与指定主机的安全TLS(SSL)连接
   if(SocketTlsHandshake(socket,InpTimeout))
      return(true);
 
   Print("SocketTlsHandshake() failed. Error ",GetLastError());
   return(false);
  }
//+------------------------------------------------------------------+
//| 脚本程序起始函数                                                   |
//+------------------------------------------------------------------+
void OnStart(void)
  {
//--- 创建一个套接字并取得它的句柄
   int socket=SocketCreate();
 
   if(socket==INVALID_HANDLE)
     {
      Print("SocketCreate() failed. Error ",GetLastError());
      return;
     }
//--- 通过 PORT 端口连接到服务器 SERVER
   if(!SocketConnect(socket,SERVER,PORT,10000))
     {
      Print("SocketConnect() failed. Error ",GetLastError());
     }
   else
     {
      //--- 建立了非安全连接
      PrintFormat("%s connection has been established to %s:%d",(PORT==443 ? "A secured" : "An unsecured"),SERVER,PORT);
      //--- 切换到安全连接
      if(PORT!=443 && TlsHandshake(socket))
        {
         PrintFormat("Unsecured connection to %s:%d switched to secured",SERVER,PORT);
         //--- 如果连接有证书保护, 显示它的数据
         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);
           }
        }
     }
//--- 在使用后关闭套接字
   SocketClose(socket);
   /*
   结果:
   An unsecured connection has been established to smtp.gmail.com:587
   SERVER220 smtp.gmail.com ESMTP a640c23a62f3a-a9b1f298319sm82305866b.105 - gsmtp
 
   SERVER250-smtp.gmail.com at your service, [37.193.40.122]
   250-SIZE 35882577
   250-8BITMIME
   250-STARTTLS
   250-ENHANCEDSTATUSCODES
   250-PIPELINING
   250-CHUNKING
   250 SMTPUTF8
 
   SERVER220 2.0.0 Ready to start TLS
 
   SocketTlsHandshake(): A secure connection to smtp.gmail.com:587 is now established
   TLS certificate:
      Owner:  /CN=smtp.gmail.com
      Issuer:  /C=US/O=Google Trust Services/CN=WR2
      Number:     1f:f4:db:2a:5a:e6:dc:52:0a:4c:05:ce:81:cc:c3:f7
      Printd6be8af229b5329cd3d4c2789c02aa94f89b421c
      Expiration2024.12.30 08:25:30
   */
  }
//+------------------------------------------------------------------+
//| 向服务器发送字符串                                                  |
//+------------------------------------------------------------------+
bool SendString(int socket,const string str)
  {
//--- 把字符串转换成字符数组
   uchar data[];
   int   size=StringToCharArray(str,data,0,str.Length(),CP_UTF8);
//--- 发送数据至套接字
   ResetLastError();
   if(SocketSend(socket,data,size)==size)
      return(true);
//--- 数据发送错误
   Print("Failed to send data to server. Error ",GetLastError());
   return false;
  }
//+------------------------------------------------------------------+
//| 从服务器获取字符串                                                  |
//+------------------------------------------------------------------+
bool RecvString(int socket,stringresult,uint timeout_ms=1000)
  {
//--- 等待数据出现在套接字上
   ulong wait_time_end=GetMicrosecondCount()+timeout_ms*1000;
 
   while(!SocketIsReadable(socket))
     {
      Sleep(10);
      //--- 等待数据超时-返回NULL作为响应
      if(wait_time_end<GetMicrosecondCount())
        {
         Print("ERROR: No response from server");
         return(false);
        }
     }
//--- 从套接字读取数据
   uchar  data[128];
   uint   size=0;
   string resp=NULL;
 
   do
     {
      uchar b[1];
      int   n=SocketRead(socket,b,1,1000);
 
      if(n < 0)
         break;
 
      if(n)
        {
         data[size++]=b[0];
 
         if(size==data.Size())
           {
            resp += CharArrayToString(data,0,data.Size(),CP_UTF8);
            size = 0;
           }
        }
     }
   while(SocketIsReadable(socket));
//--- 将读取的数据复制到字符串中
   if(size)
      resp+=CharArrayToString(data,0,size,CP_UTF8);
//--- 如果字符串为空,则出现错误
   if(!resp.Length())
     {
      Print("ERROR: No response from server");
      return(false);
     }
//--- 返回字符串
   result=resp;
   return(true);
  }