Discussion of article "Working with sockets in MQL, or How to become a signal provider" - page 3

 
// basetsd.h поставляемого с wine'ом
#if defined(__midl) || defined(__WIDL__)
  typedef unsigned __int3264 UINT_PTR, *PUINT_PTR;
  typedef unsigned __int3264 ULONG_PTR, *PULONG_PTR;
  typedef ULONG_PTR                   DWORD_PTR, *PDWORD_PTR;
 #elif defined(_WIN64)
  typedef unsigned __int64 UINT_PTR, *PUINT_PTR;
#else
  typedef unsigned long UINT_PTR, *PUINT_PTR;
#endif

typedef UINT_PTR SOCKET;

but in the article.

#define  SOCKET           uint
// but
#define  DWORD_PTR         ulong

Do you need my comments here at all? I am linking mql code with external code, in parallel I look at your developments, I find errors, I may not write here.

 

it's okay, comments are welcome.

I'm sending the type edits to the article.

I'll use 64 bits in the examples

 
Don't forget to write explicitly about bitness in the article. Something like: "code for x64" or make a switch on macros to compile for different architectures. By the way, what is sizeof(ULONG) on x64? I have some contradictory data. On the one hand it should be 4 always, the LLP64 model in win hints at it, and msdn writes:
https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751%28v=vs.85%29.aspx.
ULONG:<br/ translate="no">An unsigned LONG. The range is 0 through 4294967295 decimal This type is declared in WinDef.h as follows: typedef unsigned long ULONG;

but I'm not sure.


SO:

if I'm right, you've crammed ulong here for nothing:

struct sockaddr_in
  {
   ushort            sin_family;
   ushort            sin_port;
   /*ulong*/uint     sin_addr; //struct in_addr == 4 bytes
   char              sin_zero[8];
  };
/*ulong*/ uint inet_addr(char& cp[]);
Windows Data Types (Windows)
  • msdn.microsoft.com
The data types supported by Windows are used to define function return values, function and message parameters, and structure members. They define the size and meaning of these elements. For more information about the underlying C/C++ data types, see Data Type Ranges. The following table contains the following types: character, integer...
 
Yes, and this structure was not used in the examples either.
 

I got such a synchronous TCP client:

// tcp_client.mqh

//#define X64
#define MAKEWORD(low, high) ((ushort) ((low & 0xff) | ((ushort)high << 8)))
#ifdef X64
  #define SOCKET          ulong
#else
  #define SOCKET          uint
#endif
#define INVALID_SOCKET    (SOCKET)(~0)
#define WSA_DATA_SZ       420

struct sockaddr_in
{
  short sin_family;
  ushort sin_port;
  uint sin_addr;
  char sin_zero[8];
};

#import "ws2_32.dll"
  int WSAStartup(ushort wVersionRequested, uchar &lpWSAData[]);
  int WSACleanup(void);
  SOCKET socket(int af, int type, int protocol);
  uint inet_addr(char &cp[]);
  ushort htons(ushort hostshort);
  int bind(SOCKET s, sockaddr_in &name, int namelen);
  int connect(SOCKET s, sockaddr_in &name, int namelen);
  int closesocket(SOCKET s);
  int recv(SOCKET s, char &buf[], int len, int flags);
  int send(SOCKET s, char &buf[], int len, int flags);
#import

class TCP_Client
{
  SOCKET socket;
  uchar state;
  TCP_Client(const TCP_Client&);
  TCP_Client operator=(TCP_Client &);
public:
  // Address in "IPv4:port" format
  TCP_Client(string server_address);
  ~TCP_Client();
  // Returns true if the connection to the server is active
  bool good()const;
  // Receiving a block of data. At the beginning of each block there must be a 4-x
  // byte integer whose value is equal to the block size. Returns
  // the size of the received block or 0;
  uint recv(char &buf[]);
  // Send block. 4 bytes of size will be added to the beginning of the block.
  // Returns the size of the block sent or 0;
  uint send(char &buf[], uint len);
};

TCP_Client::TCP_Client(string server_address): socket(INVALID_SOCKET), state(0)
{
  uchar wsa_buf[WSA_DATA_SZ];
  if( WSAStartup(MAKEWORD(2,2), wsa_buf) )
    return;
  this.state |= B'1';
  
  if( (this.socket = socket(2, 1, 0)) == INVALID_SOCKET)
    return;
  this.state |= B'10';
  
  if(true)
  { 
    string server_address_ar[];
    if(StringSplit(server_address, ':', server_address_ar) != 2)
      return;
    char ch_addr[];
    StringToCharArray(server_address_ar[0], ch_addr);
    sockaddr_in addr = {0};
    addr.sin_family = 2;
    addr.sin_addr = inet_addr(ch_addr);
    addr.sin_port = htons( (ushort)StringToInteger(server_address_ar[1]) );
    if( connect(this.socket, addr, sizeof(addr)) )
      return;
    this.state |= B'100';
  }
}

TCP_Client::~TCP_Client()
{
  if( bool(this.state & B'10') )
    closesocket(this.socket);
  if( bool(this.state & B'1') )
    WSACleanup();
}

bool TCP_Client::good()const  {return this.state == B'111';}

uint TCP_Client::recv(char &buf[])
{
  struct Data_sz_int    {uint sz;} sz_int = {};
  if(true)
  {
    struct Data_sz_char   {char sz[4];} sz_char;
    int read = recv(this.socket, sz_char.sz, sizeof(sz_char), 8);
    if(read != sizeof(sz_char))
    {
      this.state &= B'11';
      return 0;
    }
    sz_int = (Data_sz_int)sz_char;
  }
  
  if( (uint)ArraySize(buf) < sz_int.sz )
    if(ArrayResize(buf, sz_int.sz) != sz_int.sz)
      return 0;
  int read = recv(this.socket, buf, sz_int.sz, 8);
  if(read != sz_int.sz)
  {
    this.state &= B'11';
    return 0;
  }
  return read;
}

uint TCP_Client::send(char &buf[], uint len)
{
  if(true)
  {
    struct Data_sz_int    {uint sz;} sz_int;
    sz_int.sz = len;
    struct Data_sz_char   {char sz[4];} sz_char;
    sz_char = (Data_sz_char)sz_int;
    int sent = send(this.socket, sz_char.sz, sizeof(Data_sz_char), 0);
    if( sent != sizeof(Data_sz_char) )
    {
      this.state &= B'11';
      return 0;
    }
  }
  
  int sent = send(this.socket, buf, len, 0);
  if( sent != len )
  {
    this.state &= B'11';
    return 0;
  }
  return sent;
}

//------------------------------------------------------------
// main.mq4

#property strict
#include "tcp_client.mqh"

void OnStart()
{
  TCP_Client s("127.0.0.1:10500");
  if( ! s.good() )
    return;
    
  char buf[];
  StringToCharArray("Hello world", buf);
  // Send 4 bytes + ArraySize(buf)
  if( s.send(buf, ArraySize(buf)) != ArraySize(buf) )
    Alert("The connection has been severed.");
  else
    Alert("ok");
}

I don't need a server on µl, so just a client. Tested superficially.

ZЫЫ: Thanks for the article.
 
pavlick_:

I have such a synchronous TCP client:

Could you share for which tasks you are going to use it? There is even a request in the article

If you also have ideas of application - share them in comments to the article

I can't think of any application where it would be convenient for me.

 
fxsaber:

Could you share for which tasks you are going to use it? There's even a request in the article

I can't think of anywhere where it would be convenient for me.

I'm in linux, hence ipc in general becomes a non-trivial task (communication between terminal under wine and linuex exe). And ipc over a network is a universal way. I connect the µl script to the linux programme via loopback (127.0.0.1) on the same comp. Basically, I wrote a linux api for the terminal (the µl script handles requests and sends price data or places orders).

In my situation this is the best way of IPC from what I have tried. And I don't want to transfer my developments to µl - I don't want to be bound to a particular language, as a matter of principle.

 
pavlick_:

I'm in linux, hence ipc in general becomes a non-trivial task (communication between terminal under wine and linuex exe). And ipc over the network is a universal way. I connect the µl script to the linux programme via loopback (127.0.0.1) on the same comp. Basically, I wrote a linux api for the terminal (the µl script handles requests and sends price data or places orders).

In my situation this is the best way of IPC from what I have tried. I don't want to transfer my developments to µl - I don't want to be bound to a particular language, as a matter of principle.

It would be a great article! You get your own trading universal API for any platform. And for a platform you write only a receiver/transmitter on the API of the platform itself.

And you can write TS in any language. Programmers are like that!

Payback is only in latency. That's why it won't work for a very narrow kind of TC - and that's okay.

 
Is it possible to write a class to work with web sockets in the same way?
 
The article is very interesting and informative. I plan to use socket technology to transfer from VPS server with MT-5 terminals to server with MS-SQL database information on financial status of accounts at the end of the day/week/month.
Implemented in practice, but practice shows that after some time packets start to be lost. And judging by the logs at first the server part "stops", and client parts at this time continue to think that everything is OK, because the code of the client part of the check of type
if(client==INVALID_SOCKET) StartClient(Host,Port);
does not give anything. I.e. clients can't diagnose the loss of connection with the server before sending and even at the moment of sending packets.

Perhaps it is necessary to add some function like "CheckSocket" for preliminary check of server socket operability? How to implement it then ?