WebSocket_MQL_by_ufranco/Include/Socket.mqh

338 lines
11 KiB
MQL5

//+------------------------------------------------------------------+
//| Socket.mqh |
//| Copyright 2019, MetaQuotes Software Corp. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link "https://www.mql5.com"
#property version "1.00"
#property strict
//+------------------------------------------------------------------+
//| structs |
//+------------------------------------------------------------------+
struct CERT
{
string cert_subject;
string cert_issuer;
string cert_serial;
string cert_thumbprint;
datetime cert_expiry;
};
//+------------------------------------------------------------------+
//| Class CSocket. |
//| Purpose: Base class of socket operations. |
//| |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CSocket
{
private:
static int m_usedsockets; // tracks number of sockets in use in single program
bool m_log; // logging state
bool m_usetls; // tls state
uint m_tx_timeout; // send system socket timeout in milliseconds
uint m_rx_timeout; // receive system socket timeout in milliseconds
int m_socket; // socket handle
string m_address; // server address
uint m_port; // port
CERT m_cert; // Server certificate info
public:
CSocket();
~CSocket();
//--- methods to get private properties
int SocketID(void) const { return(m_socket); }
string Address(void) const { return(m_address); }
uint Port(void) const { return(m_port); }
bool IsSecure(void) const { return(m_usetls); }
uint RxTimeout(void) const { return(m_rx_timeout); }
uint TxTimeout(void) const { return(m_tx_timeout); }
bool ServerCertificate(CERT& certificate);
//--- methods to set private properties
bool SetTimeouts(uint tx_timeout, uint rx_timeout);
//--- general methods for working sockets
void Log(const string custom_message,const int line,const string func);
static uint SocketsInUse(void) { return(m_usedsockets); }
bool Open(const string server,uint port,uint timeout,bool use_tls=false,bool enablelog=false);
bool Close(void);
uint Readable(void);
bool Writable(void);
bool IsConnected(void);
int Read(uchar& out[],uint out_len,uint ms_timeout,bool read_available);
int Send(uchar& in[],uint in_len);
};
int CSocket::m_usedsockets=0;
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CSocket::CSocket():m_socket(INVALID_HANDLE),
m_address(""),
m_port(0),
m_usetls(false),
m_log(false),
m_rx_timeout(150),
m_tx_timeout(150)
{
ZeroMemory(m_cert);
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CSocket::~CSocket()
{
//--- check handle
if(m_socket!=INVALID_HANDLE)
Close();
}
//+------------------------------------------------------------------+
//| set system socket timeouts |
//+------------------------------------------------------------------+
bool CSocket::SetTimeouts(uint tx_timeout,uint rx_timeout)
{
if(m_socket==INVALID_HANDLE)
{
Log("Invalid socket",__LINE__,__FUNCTION__);
return(false);
}
if(SocketTimeouts(m_socket,tx_timeout,rx_timeout))
{
m_tx_timeout=tx_timeout;
m_rx_timeout=rx_timeout;
Log("Socket Timeouts set",__LINE__,__FUNCTION__);
return(true);
}
return(false);
}
//+------------------------------------------------------------------+
//| certificate |
//+------------------------------------------------------------------+
bool CSocket::ServerCertificate(CERT& certificate)
{
if(m_socket==INVALID_HANDLE)
{
Log("Invalid socket",__LINE__,__FUNCTION__);
return(false);
}
if(SocketTlsCertificate(m_socket,m_cert.cert_subject,m_cert.cert_issuer,m_cert.cert_serial,m_cert.cert_thumbprint,m_cert.cert_expiry))
{
certificate=m_cert;
Log("Server certificate retrieved",__LINE__,__FUNCTION__);
return(true);
}
return(false);
}
//+------------------------------------------------------------------+
//|connect() |
//+------------------------------------------------------------------+
bool CSocket::Open(const string server,uint port,uint timeout,bool use_tls=false,bool enablelog=false)
{
if(m_socket!=INVALID_HANDLE)
Close();
if(m_usedsockets>=128)
{
Log("Too many sockets open",__LINE__,__FUNCTION__);
return(false);
}
m_usetls=use_tls;
m_log=enablelog;
m_socket=SocketCreate();
if(m_socket==INVALID_HANDLE)
{
Log("Invalid socket",__LINE__,__FUNCTION__);
return(false);
}
++m_usedsockets;
m_address=server;
if(port==0)
{
if(m_usetls)
m_port=443;
else
m_port=80;
}
else
m_port=port;
//---
if(!m_usetls && m_port==443)
m_usetls=true;
//---
Log("Connecting to "+m_address,__LINE__,__FUNCTION__);
//---
if(m_usetls)
{
if(m_port!=443)
{
if(SocketConnect(m_socket,server,port,timeout))
return(SocketTlsHandshake(m_socket,server));
}
else
{
return(SocketConnect(m_socket,server,port,timeout));
}
}
return(SocketConnect(m_socket,server,port,timeout));
}
//+------------------------------------------------------------------+
//|close() |
//+------------------------------------------------------------------+
bool CSocket::Close(void)
{
//---
if(m_socket==INVALID_HANDLE)
{
Log("Socket Disconnected",__LINE__,__FUNCTION__);
return(true);
}
//---
if(SocketClose(m_socket))
{
m_socket=INVALID_HANDLE;
--m_usedsockets;
Log("Socket Disconnected from "+m_address,__LINE__,__FUNCTION__);
m_address="";
ZeroMemory(m_cert);
return(true);
}
//---
Log("",__LINE__,__FUNCTION__);
return(false);
}
//+------------------------------------------------------------------+
//|readable() |
//+------------------------------------------------------------------+
uint CSocket::Readable(void)
{
if(m_socket==INVALID_HANDLE)
{
Log("Invalid socket",__LINE__,__FUNCTION__);
return(0);
}
//---
Log("Is Socket Readable ",__LINE__,__FUNCTION__);
//---
return(SocketIsReadable(m_socket));
}
//+------------------------------------------------------------------+
//|writable() |
//+------------------------------------------------------------------+
bool CSocket::Writable(void)
{
if(m_socket==INVALID_HANDLE)
{
Log("Invalid socket",__LINE__,__FUNCTION__);
return(false);
}
//---
Log("Is Socket Writable ",__LINE__,__FUNCTION__);
//---
return(SocketIsWritable(m_socket));
}
//+------------------------------------------------------------------+
//|isconnected() |
//+------------------------------------------------------------------+
bool CSocket::IsConnected(void)
{
if(m_socket==INVALID_HANDLE)
{
Log("Invalid socket",__LINE__,__FUNCTION__);
return(false);
}
//---
Log("Is Socket Connected ",__LINE__,__FUNCTION__);
//---
return(SocketIsConnected(m_socket));
}
//+------------------------------------------------------------------+
//|read() |
//+------------------------------------------------------------------+
int CSocket::Read(uchar& out[],uint out_len,uint ms_timeout,bool read_available=false)
{
if(m_socket==INVALID_HANDLE)
{
Log("Invalid socket",__LINE__,__FUNCTION__);
return(-1);
}
//---
Log("Reading from "+m_address,__LINE__,__FUNCTION__);
if(m_usetls)
{
if(read_available)
return(SocketTlsReadAvailable(m_socket,out,out_len));
else
return(SocketTlsRead(m_socket,out,out_len));
}
else
return(SocketRead(m_socket,out,out_len,ms_timeout));
return(-1);
}
//+------------------------------------------------------------------+
//|send() |
//+------------------------------------------------------------------+
int CSocket::Send(uchar& in[],uint in_len)
{
if(m_socket==INVALID_HANDLE)
{
Log("Invalid socket",__LINE__,__FUNCTION__);
return(-1);
}
//---
Log("Sending to "+m_address,__LINE__,__FUNCTION__);
//---
if(m_usetls)
return(SocketTlsSend(m_socket,in,in_len));
else
return(SocketSend(m_socket,in,in_len));
//---
return(-1);
}
//+------------------------------------------------------------------+
//|log() |
//+------------------------------------------------------------------+
void CSocket::Log(const string custom_message,const int line,const string func)
{
if(m_log)
{
//---
int eid=GetLastError();
//---
if(eid!=0)
{
PrintFormat("[MQL error ID: %d][%s][Line: %d][Function: %s]",eid,custom_message,line,func);
ResetLastError();
return;
}
if(custom_message!="")
PrintFormat("[%s][Line: %d][Function: %s]",custom_message,line,func);
}
//---
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////