MQL5Book/Include/ws/wstransport.mqh
super.admin 1c8e83ce31 convert
2025-05-30 16:09:41 +02:00

183 lines
5.3 KiB
MQL5

//+------------------------------------------------------------------+
//| wstransport.mqh |
//| Copyright 2020-2022, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#include "wstools.mqh"
#include "wsinterfaces.mqh"
//+------------------------------------------------------------------+
//| Network processing based on built-in MQL5 Socket-functions |
//+------------------------------------------------------------------+
class MqlWebSocketTransport: public IWebSocketTransport
{
protected:
int handle;
uint timeout;
bool TLS;
bool connected;
MqlWebSocketTransport(const string scheme, const string host, const uint port, const uint t = 1)
{
handle = SocketCreate();
timeout = t;
TLS = scheme == "wss";
if(handle != INVALID_HANDLE)
{
Print("Connecting to ", host, ":", port);
if(SocketConnect(handle, host, port, timeout))
{
SocketTimeouts(handle, timeout, timeout);
string subject, issuer, serial, thumbprint;
datetime expiration;
if(TLS)
{
if(!SocketTlsHandshake(handle, host))
{
Print("TLS handshake error: ", _LastError);
TLS = false;
}
else
{
Print("TLS enabled");
}
if(SocketTlsCertificate(handle, subject, issuer, serial, thumbprint, expiration))
{
Print("TLS:");
Print(" Owner: ", subject);
Print(" Issuer: ", issuer);
Print(" Number: ", serial);
Print(" Signature: ", thumbprint);
Print(" Expiration: ", expiration);
}
}
}
else
{
Print("Can't connect: ", GetLastError());
}
}
else
{
Print("Can't create socket: ", GetLastError());
}
}
~MqlWebSocketTransport()
{
SocketClose(handle);
}
public:
static MqlWebSocketTransport *create(const string scheme, const string host, const uint port, const uint timeout)
{
MqlWebSocketTransport *result = new MqlWebSocketTransport(scheme, host, port, timeout);
if(result.getHandle() == INVALID_HANDLE)
{
Print("Socket create failed: ", _LastError);
delete result;
result = NULL;
}
return result;
}
bool isConnected(void) const override
{
return SocketIsConnected(handle);
}
int getHandle() const override
{
return handle;
}
int write(const uchar &buffer[]) override
{
if(!SocketIsConnected(handle))
{
Print("Can't write to non-connected socket");
return -1;
}
const uint len = ArraySize(buffer);
int written = 0;
ResetLastError();
if(TLS)
{
written = SocketTlsSend(handle, buffer, len);
}
else
{
written = SocketSend(handle, buffer, len);
}
return written;
}
int read(uchar &buffer[]) override
{ // can be disconnected already but still having data in internal buffer
if(!SocketIsConnected(handle) && SocketIsReadable(handle) == 0)
{
Print("Can't read from non-connected socket");
return -1;
}
ResetLastError();
ArrayResize(buffer, 0); // ensure buffer is of zero size
uint available = SocketIsReadable(handle);
int received = 0;
#ifdef NETWORK_PURGE
ResetLastError();
// depending from network conditions it may require to call
// SocketIsReadable twice in a row!
// yes, this is ridiculous but may help here
available = SocketIsReadable(handle);
#endif
if(!available) return 0;
else if(available == !_LastError && TLS) return 0;
if(TLS)
{
// SocketIsReadable is not compatible with SocketTlsRead!
// first returns raw data count, second tries to read clean deciphered bytes count
received = SocketTlsReadAvailable(handle, buffer, available); // waiting for data (block)
}
else
{
received = SocketRead(handle, buffer, available, timeout);
}
if(received == -1)
{
// NB: even if we got -1 result and _LastError = 5273,
// buffer may contain valid data received before connection was lost/closed
Print("SocketRead failed: ", _LastError, " Available: ", available);
if(_LastError == 5273 && available == 1 && !TLS) return -1;
}
return ArraySize(buffer);
}
bool isReadable(void) const override
{
return SocketIsReadable(handle) > 0;
}
bool isWritable(void) const override
{
return SocketIsWritable(handle);
}
void close(void) override
{
SocketClose(handle);
handle = INVALID_HANDLE;
}
};
//+------------------------------------------------------------------+