Обсуждение статьи "Работа с сокетами в MQL, или Как стать провайдером сигналов" - страница 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;

но в статье

#define SOCKET           uint
// но
#define DWORD_PTR         ulong

Мои замечания вообще нужны здесь? Я связываю mql код с внешним кодом, параллельно посматриваю на ваши наработки, находятся ошибки, могу и не писать сюда.

 

все ок, замечания пишите.

правки типов я отправляю в статью.

в примерах поиспользую 64 бит

 
Не забудьте явно написать в статье про битность. Что-то вроде: "код для x64" или сделать на макросах переключатель для компиляции под разные архитектуры. Кстати, чему у вас на x64 равен sizeof(ULONG)? Какие-то противоречивые данные у меня. С одной стороны вроде как должен быть 4 всегда, модель LLP64 в win намекает, да и msdn пишет:
https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751%28v=vs.85%29.aspx
ULONG:
An unsigned LONG. The range is 0 through 4294967295 decimal This type is declared in WinDef.h as follows: typedef unsigned long ULONG;

но не уверен.


ЗЫ:

если я прав, то вот сюда вы зря ulong впихнули:

struct sockaddr_in
  {
   ushort            sin_family;
   ushort            sin_port;
   /*ulong*/uint     sin_addr; //struct in_addr == 4 байтам
   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...
 
да, причем эта структура тоже не испльзовалась в примерах.
 

У меня получился такой синхронный TCP клиент:

// 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:
  // Адрес в формате "IPv4:port"
  TCP_Client(string server_address);
  ~TCP_Client();
  // Возвращает истину, если соединение с сервером активно
  bool good()const;
  // Приём блока данных. Вначале каждого блока должно стоять 4-ёх
  // байтовое целое, значение которого равно размеру блока. Возвращает
  // размер принятого блока или 0;
  uint recv(char &buf[]);
  // Отправка блока. 4 байта размера будут добавлены в начало блока.
  // Возвращает размер отправленного блока или 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);
  // Отсылаем 4байта + ArraySize(buf)
  if( s.send(buf, ArraySize(buf)) != ArraySize(buf) )
    Alert("Соединение разорвано");
  else
    Alert("ok");
}

На мкл мне сервер не нужен, поэтому только клиент. Проверил поверхностно.

ЗЫ: Спасибо за статью.
 
pavlick_:

У меня получился такой синхронный TCP клиент:

Могли бы Вы поделиться, для каких задач собираетесь применять? В статье даже просьба такая есть

Если у вас тоже есть идеи применения — поделитесь ими в комментариях к статье

В голову не приходит, где это было бы мне удобно.

 
fxsaber:

Могли бы Вы поделиться, для каких задач собираетесь применять? В статье даже просьба такая есть

В голову не приходит, где это было бы мне удобно.

Я в линуксе, следовательно, ipc вообще становится нетривиальной задечей (общение между терминалом под wine и linuex exe). А IPC через сеть - универсальный способ. Я соединяю мкл скрипт с linux программой через loopback (127.0.0.1) на одном компе. По сути, я написал linux api для терминала (мкл скрипт обрабатывает запросы и отсылает ценовые данные или выставляют ордера).

В моей ситуации это лучший способ IPC из того что я пробовал. А перносить свои наработки на мкл не хочу - не хочу привязываться к конкретному языку, принципиально.

 
pavlick_:

Я в линуксе, следовательно, ipc вообще становится нетривиальной задечей (общение между терминалом под wine и linuex exe). А IPC через сеть - универсальный способ. Я соединяю мкл скрипт с linux программой через loopback (127.0.0.1) на одном компе. По сути, я написал linux api для терминала (мкл скрипт обрабатывает запросы и отсылает ценовые данные или выставляют ордера).

В моей ситуации это лучший способ IPC из того что я пробовал. А перносить свои наработки на мкл не хочу - не хочу привязываться к конкретному языку, принципиально.

Статья вышла бы знатная! Получается свой торговый универсальный API под любую платформу. А для платформы пишется только приемо/передатчик на API самой уже платформы.

И можно писать ТС на любом языке. Программеры - они такие!

Расплата только в лэтенси. Поэтому для совсем узкого вида ТС не подойдет - и не страшно.

 
А возможно ли таким же образом написать класс для работы с веб соккетами?
 
Статья очень интересная и познавательная. Планирую использовать сокетную технологию для передачи с VPS сервера с МТ-5 теминалами на сервер с БД MS-SQL информации по финансовому состоянию счетов в конце дня/недели/месяца.
Реализовал на практике, но практика показывает что по прошествии некоторого времени пакеты начинают теряться. Причем судя по логам вначале "глохнет" серверная часть, а клиентские части в это время продолжают считать что все Ок, т.к. проведение в коде клиентской части проверки вида
if(client==INVALID_SOCKET) StartClient(Host,Port);
ничего не дает. Т.е. клиенты не могут диагностировать потерю коннекта с сервером до начала отпраки и даже в момент отправки пакетов.

Возможно необходимо дополнительно какую-то функция типа "CheckSocket" для предварительной проверки работоспособности серверного сокета? Как ее тогда реализовать ?
Причина обращения: