Моделирование рынка (Часть 12): Сокеты (VI)
Введение
В предыдущей статье, "Моделирование рынка (Часть 11): Сокеты (V)", мы рассказали, как создать приложение на Python для использования в Excel. Цель данного приложения - показать, как создать эхо-сервер в Python. Особенность заключалась в том, что данные, связанные с событиями подключения и закрытия, отображались в Excel.
На самом деле, данный сервер не очень полезен для нас, в основном из-за того, что он позволяет только одно соединение, и серверы, целью которых является установление только одного соединения, не слишком полезны. Однако я не хочу, чтобы вы зацикливались на этой детали. Замысел состоял в том, чтобы показать, как скрипт, написанный на Python, может прозрачно работать в Excel. Однако для того, что нам нужно, наш сервер должен быть немного сложнее. Для этого нам придется сделать ещё несколько вещей.
Идея здесь не заключается в том, чтобы создать готовое приложение. Как уже говорилось ранее, сокеты - это очень сложная тема, которая требует много времени на изучение и исследование. Не ждите, что за один день возможно создать что-то действительно надежное и совершенное. Когда речь заходит о сокетах, необходимо вникнуть во многие детали, одни из них более простые, а другие - более сложные.
Но поскольку наше приложение требует нечто более сложное для обеспечения связи между Excel и MetaTrader 5, было решено немного сгладить кривую разработки. Это связано с тем, что цель - показать всем, кто начинает изучать тему сокетов, как всё может происходить на самом деле.
Я извиняюсь перед теми, кто более опытен, если вам это покажется повторением или если статья не принесет новых знаний. Но данный материал будет очень полезен для тех, кто начинает, тем более что мы будем делать всё на чистом Python.
В этой статье мы не будем работать непосредственно с Excel или MQL5. Но в случае с MQL5 мы сможем его использовать. Точнее, мы будем использовать то, что разработали в этой же серии. Для полного понимания материала, будет полезно также ознакомиться с тем, что было сделано в MQL5.
Если вам интересно, можете прочитать следующие статьи:
Именно в этих двух статьях мы создали то, что станет основой этой статьи. В упомянутых статьях мы разработали мини-чат, позволяющий пользователям MetaTrader 5 обмениваться информацией с помощью текстовых сообщений. Дело в том, что код сервера в то время был разработан на C++. Здесь мы разработаем похожий сервер, но на языке Python.
Давайте сначала разберемся в некоторых моментах
Преобразование эхо-сервера в мини-сервер, на первый взгляд, не кажется чем-то сложным. А преобразование эхо-сервера в тот вид, который нужен нам для связи Excel и MetaTrader 5, тоже не является сложной задачей. Разумеется, для тех, кто уже обладает достаточным опытом и знаниями. Однако, когда речь заходит о сокетах и особенно об использовании сокетов несколькими клиентами одновременно на одном сервере, ситуация несколько усложняется. Это происходит так, потому что многие, особенно новички, не имеют представления о том, что в действительности нужно делать.
Давайте вернемся к коду Python, рассмотренному в предыдущей статье. Его можно увидеть ниже:
01. import socket 02. import xlwings as xw 03. 04. def Echo(): 05. wb = xw.Book.caller() 06. server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 07. server.bind(("127.0.0.1", 27015)) 08. wb.sheets[0]['A1'].value = "Waiting connection..." 09. server.listen(1) 10. client, addr = server.accept() 11. client.send("Wellcome to Server in Python\n\r".encode()) 12. wb.sheets[0]['A2'].value = str(f"Client connected by {addr}") 13. while True: 14. info = client.recv(512) 15. if not info: 16. break 17. client.send(b"Echo Server:" + info) 18. wb.sheets[0]['A3'].value = "Client disconnected" 19. client.close()
Сервер Python
Хотя данный код не работает без материала, показанного в предыдущей статье, его можно заставить работать и использовать в дальнейшем в качестве модуля. Учитывая, что Excel не будет использоваться в связке с Python, придется удалить некоторые части кода, чтобы внести эти изменения. Код будет выглядеть так:
01. import socket 02. 03. def Echo(): 04. server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 05. server.bind(("127.0.0.1", 27015)) 06. server.listen() 07. client, addr = server.accept() 08. client.send("Wellcome to Server in Python\n\r".encode()) 09. while True: 10. info = client.recv(512) 11. if not info: 12. break 13. client.send(b"Echo Server:" + info) 14. client.close() 15. 16. if __name__ == "__main__": 17. Echo()
Сервер Python
Хорошо. Если вы уже немного знакомы с Python, то наверняка заметили, что здесь происходит. Но если вы не знакомы с Python, то можете подумать: «Что это за безумие?» На самом деле в этом нет ничего безумного. Просто предыдущий код, который не мог быть выполнен напрямую интерпретатором Python, теперь может делать это. Это стало возможным благодаря добавлению строк 16 и 17, но код по-прежнему можно использовать внутри модуля именно благодаря этим строкам.
Но в данном случае речь идет не об этом. Вопрос в том, как сделать так, чтобы представленный код позволял подключаться к нему нескольким клиентам, даже если в принципе это невозможно.
Хорошо. Если вы изучили справочные материалы, которые включили в предыдущие статьи, то вы уже должны понимать некоторые детали и проблемы, присутствующие в этом коде. Это связано с тем, что он, по сути, предназначен для приема только одного соединения за раз. Но как это возможно? Это связано с тем, что функция accept из строки 07, однажды выполненная, больше не будет выполняться. Это препятствует подключению большего числа клиентов. Даже если тот же клиент закроет соединение, у него не получится подключиться снова.
Причина та же: функция accept больше не будет выполняться. «Итак, можно ли добавить в этот код ещё один цикл, чтобы содержимое между строками 07 и 14 выполнялось снова, если клиент решит вернуться?» Да, можно. И действительно, в некоторых более простых случаях именно так и поступит программист. Однако давайте подумаем немного дальше, так как если мы реализуем это решение, у нас всё равно будет только один подключенный клиент. А для нашей цели для мини-чата этого будет недостаточно.
Помимо этой проблемы, существует и другая. Если вы тестировали то, что было представлено в предыдущих двух статьях, то вы, наверное, заметили, что Excel становится практически недоступным, когда сервер используется. Среди упомянутых статей есть следующие:
Это для тех, кто ещё не читал эти статьи. И теперь возникает вопрос: почему Excel становится трудно использовать, когда работает эхо-сервер? Чтобы ответить на этот вопрос, необходимо понять, что происходит внутри серверного кода. Необходимо помнить, что Excel вызывает через VBA код Python, который должен быть выполнен. Когда запускается код Python, он начинает конкурировать за использование процессора с Excel.
Для ОС код Python не является отдельным процессом. На самом деле, это был бы своего рода поток Excel. Слово "поток", на самом деле, не самое подходящее для использования в данном случае, поскольку поток не будет конкурировать с основной программой за использование процессора. Однако, поскольку закрытием Excel также завершается работа кода Python, мы можем использовать слово "поток" здесь с необходимой осторожностью. Но с этим термином следует быть осторожным, и причина этого будет объяснена чуть позже.
Однако есть справедливый вопрос, почему код Python конкурирует за использование процессора, когда у Excel есть окно времени для его использования? Это связано с двумя моментами в коде. Давайте вернемся к исходному коду эхо-сервера. Прошу заметить, что в строке 10 происходит вызов функции accept. Данная функция блокируется. То есть при выполнении код останавливается в этой точке и не продолжается до тех пор, пока не установится какое-то соединение. Эта функция представляет собой первое узкое место, т.е. первый раз, когда скрипт Python будет конкурировать с Excel за использование процессора.
Когда соединение установлено, возникает вторая проблема. Это происходит как раз в строке 14, где скрипт Python снова конкурирует с Excel. Данная конкуренция повторяется при каждом взаимодействии со строкой 14. Таким образом, Excel получает меньше внимания, чем скрипт Python, что затрудняет его использование во время работы сервера.
Но есть ли способ решить эту проблему? Да, есть несколько вариантов. Первый - заставить скрипт Python стать настоящим потоком Excel. Таким образом, когда Excel войдет в окно выполнения, тогда и Excel, и скрипт Python перестанут конкурировать за использование процессора. Так происходит, потому что ОС настраивает задачи таким образом, чтобы каждая из них получала часть времени обработки. В случае многоядерных процессоров ОС может позволить Excel использовать одно ядро, а скрипт Python - другое. Однако, чтобы это произошло, Excel должен сообщить ОС, что скрипт Python станет потоком.
Но о каком именно потоке идет речь? Чтобы понять это, давайте перейдем к новой теме.
Давайте разберемся, что такое поток
Грубо говоря, поток - это будто ещё одна программа, существующая внутри основной программы. Действительно, это простой способ описать поток. Поток существует только до тех пор, пока выполняется основная программа. Когда основная программа, в нашем случае Excel, прекращает свое существование, всё связанное с ней тоже умирает.
Концепция потоков появилась вместе с параллельной обработкой. На самом деле, поток служит именно этой цели. Он помогает основной программе выполнять задачи, которые было бы очень сложно запрограммировать непосредственно в основном коде. Для этого мы поместим часть кода в фрагмент, который будет выполняться параллельно с основной программой.
Как только поток завершает свою работу, основная программа может использовать вычисленные или полученные данные. Они будут доступны в той области, которую мы забронировали. Это довольно интересная форма программирования, поскольку при использовании потока можно выполнить задачу, позволяя основной программе продолжать работу, даже если на выполнение задачи, отправленной в поток, уйдет несколько минут.
Эта возможность в Python, как и в других языках, очень интересна. Однако, чтобы понять то, что происходит на самом деле и в чем преимущество использования потока, необходимо увидеть всё в действии. Чтобы не оставлять тему без практического примера, мы рассмотрим случай использования потоков в Python.
01. import socket as sock 02. import threading as thread 03. 04. def NewClientHandler(conn, addr): 05. print(f"Client [%s:%s] is online..." % addr) 06. while True: 07. msg = conn.recv(512).decode().rstrip(None) 08. if msg: 09. print(f"Message from [%s:%s]: {msg}" % addr) 10. if msg.lower() == "/see you later": 11. break 12. print(f"Client [%s:%s] is disconnected..." % addr) 13. conn.close() 14. 15. server = sock.socket(sock.AF_INET, sock.SOCK_STREAM) 16. server.bind(('localhost', 27015)) 17. server.listen() 18. print("Waiting connections...") 19. while True: 20. conn, addr = server.accept() 21. conn.send("Wellcome to Server.\n\r".encode()) 22. thread.Thread(target=NewClientHandler, args=(conn, addr)).start()
Сервер Python
Данный сервер, код которого приведен выше, использует потоки для одновременной работы с несколькими клиентами. Прошу обратить внимание на некоторые детали. Во-первых, информацию, предоставленную одним клиентом, данный сервер не будет пересылать другим. Во-вторых, он предназначен только для образовательных целей, то есть для демонстрации использования потоков. Но если понять, что происходит, то вы сможете создать много интересных вещей, просто изменив приведенный выше код.
Но давайте разберемся сначала, что происходит внутри этого кода. Если вам уже знаком Python, тогда придется проявить немного терпения к начинающим программистам. Помните, что кто-то в свое время научил вас или вы узнали об этом из документов, написанных другими людьми. Пожалуйста, будьте терпеливы.
В строке 01 мы импортируем пакет сокетов. Прошу заметить, что мы излагаем всё немного по-другому. Во строке 02 мы импортируем пакет, который позволяет нам создавать потоки. Как и в строке 01, здесь также есть нечто, что может показаться странным, но не пугайтесь. Мы создаем псевдоним, чтобы сделать вещи в Python более приятными и понятными.
Между строками 4 и 13 находится процедура. Мы пока оставим его в стороне и перейдем к строке 15. Обратите внимание, что между строками 15 и 18 код почти такой же, как и в предыдущих примерах в сегодняшней статье. Данная часть отвечает за создание сокета на сервере, поэтому код идентичен. Один важный момент: в этих примерах мы используем протокол TCP. Но стоит помнить, что существуют и другие сетевые протоколы, поэтому код может немного отличаться от сервера к серверу, в основном из-за некоторых аспектов, которые мы рассмотрим позже.
В любом случае, в строке 18 мы указываем, что сервер готов к приему соединений. Помните о детали, которая упоминалась ранее - перед вызовом accept нужно поставить цикл? Ну что ж, здесь мы именно этим и занимаемся. Таким образом, даже если клиент отключится, он сможет снова подключиться к серверу, поскольку цикл позволит более одного взаимодействия с вызовом accept. Теперь давайте вспомним следующую деталь: вызов accept блокирует код и заставляет его ждать соединения. Когда это происходит, выполнение продолжается до строки 21, которая отправляет основное сообщение новому клиенту, давая ему понять, что соединение было установлено. Далее, в строке 22, создается поток в Python.
Именно здесь всё становится по-настоящему интересным, и именно здесь появляется нечто очень важное. Если бы мы поместили код между строкой 04 и строкой 13, а не 22, поведение сервера было бы совершенно иным. Это связано с тем, что код больше не будет разрешать более одного соединения: будет принимать только одно.
«Минутку, как такое возможно?» Чтобы понять это, необходимо осознать, что строка 22 не является магической строкой. На самом деле она вызывает процедуру, определенную в строке 04. Однако есть один момент, который делает разницу между вызовом, который становится потоком, и тем, который им не становится. Как только вызов становится потоком, как показано в этом коде, ОС делает следующее: она берет код из потока, выделяет его в памяти и делит время или окно выполнения основного кода между самим кодом и потоком.
Поначалу это, возможно, немного запутано. Но подумайте об этом так: время, которое ОС отводит на выполнение программы, можно сравнить с шоколадным батончиком. Когда у программы нет потоков, она может занять весь бар. Но когда на нем появляются потоки, этот бар распадается на всё более мелкие кусочки. Таким образом, основная программа сможет потреблять только один из этих кусочков, а остальные будут потребляться потоками. Это было бы довольно простым объяснением, если бы наш процессор имел только одно ядро.
Возвращаясь к нашему коду, отметим, что после выполнения строки 22 программа "распадается" на две части. Одна часть сразу же возвращается в строку 19, где снова ждет в строке 20. Важно помнить, что функция accept блокирует код, не позволяя ему дальнейшего выполнения. Другая часть будет направлена к строке 4.
Хорошо. «Теперь я совсем запутался, как может работать код в строке 4, если код заблокирован в строке 20? Это меня очень смущает». Успокойтесь, мои дорогие читатели, нет никаких причин для путаницы. Вспомните пример с шоколадным батончиком. В результате основной код будет заблокирован в строке 20, в ожидании нового клиентского соединения. Однако код между строками 4 и 13 будет выполняться независимо от нового клиентского подключения. Важным моментом является то, что сервер начнет прослушивать информацию от подключенного клиента. Поэтому мы вводим новый цикл в строке 06. Обратите на это внимание, потому что если вы не поймете эту часть, то окончательно запутаетесь в том, как работают потоки.
Данный цикл, который начинается в строке 06 и заканчивается в строке 11, будет существовать только внутри потока. И у каждого клиента, и это важно понимать, будет свой цикл. Таким образом, даже если подключены два или более клиентов, один из них не будет мешать работе другого. Теперь представьте, что у каждого клиента есть собственный сервер, предназначенный только для него. Другими словами, каждый клиент, который подключен или будет подключен, будет создавать сервер для себя. И данный сервер будет отвечать только этому клиенту.
В этом и заключается магия потока. Однако не все эти чудеса совершенны. При использовании потоков возникают некоторые проблемы. Многие из этих проблем появляются именно тогда, когда мы, как программисты, начинаем использовать их без разбора. Всегда помните, что поток будет отнимать часть времени у основной программы, даже если кажется, что он работает параллельно. Это связано с тем, что, сколько бы ядер ни было у нашего процессора, они не бесконечны. Поэтому чрезмерное использование потоков может задействовать все доступные ядра, что приведет к увеличению времени выполнения и, соответственно, к уменьшению преимущества использования потоков.
Возможно, вам интересно и другое. Хотя данный код способен работать с несколькими клиентами, он не позволяет им общаться друг с другом. Разве нельзя передавать информацию между клиентами с помощью потоков? Если вы так подумали, значит, на самом деле вы всё ещё не до конца понимаете, как работают потоки. Но не расстраивайтесь. Напротив, вы должны чувствовать удовлетворение, ведь вы задались этим вопросом. Если, глядя на код и читая статью, вы подумали именно так, это говорит о том, что вы действительно поняли, как работает сервер. Однако сам факт возникновения данного вопроса говорит о том, что вы ещё не до конца освоили концепцию потоков. Поэтому мы решим эти сомнения. Чтобы разделить идеи, мы рассмотрим это в следующем разделе.
Передача информации между клиентами управляется потоком
Чтобы понять, как это происходит, надо разобраться в том, что происходит до этого момента. Если вы не поняли предыдущие темы, вернитесь и прочитайте их ещё раз. Важно понять их, иначе вы ещё больше запутаетесь в этом разделе, который, хотя и является относительно коротким, может оставить вас в замешательстве и, что еще хуже, без понимания всего происходящего.
Давайте посмотрим на код.
01. import socket as sock 02. import threading as thread 03. 04. CONN_LIST = [] 05. 06. def NewClientHandler(conn, addr): 07. global CONN_LIST 08. CONN_LIST.append(conn) 09. print(f"Client [%s:%s] is online..." % addr) 10. while True: 11. msg = conn.recv(512).decode().rstrip(None) 12. if msg: 13. print(f"Message from [%s:%s]: {msg}" % addr) 14. for slave in CONN_LIST: 15. if slave != conn: 16. slave.send(f"[{addr[0]}]: {msg}\n\r".encode()) 17. if msg.lower() == "/see you later": 18. break 19. print(f"Client [%s:%s] is disconnecting..." % addr) 20. conn.close() 21. CONN_LIST.remove(conn) 22. 23. server = sock.socket(sock.AF_INET, sock.SOCK_STREAM) 24. server.bind(('0.0.0.0', 27015)) 25. server.listen() 26. print("Waiting connections...") 27. while True: 28. conn, addr = server.accept() 29. conn.send("Wellcome to Server.\n\r".encode()) 30. thread.Thread(target=NewClientHandler, args=(conn, addr)).start()
Сервер Python
«Но что это такое? Вы добавили всего несколько строк кода, уверены ли вы, что этот код позволяет клиентам отправлять информацию друг другу?» Да, уверяю вас, данный код не только позволяет пересылать информацию между клиентами, но и заставляет сервер отображать эти самые сообщения. Это можно увидеть в окне командной строки, в котором запущен сервер.
Однако в этом коде есть и один важный момент, старые сообщения будут недоступны. Это означает, что сообщения будут получать только те клиенты, которые действительно подключены к серверу на данный момент. «Но как именно это происходит? Я смотрю на код и не могу его понять».
Чтобы понять, как серверу удается передавать сообщения, отправленные другим клиентам, необходимо разобраться в том, как работает поток. Мы написали об этом в предыдущей теме. Если вы поняли, как работает поток, то теперь мы можем понять, как они общаются друг с другом. Именно так. Для того, чтобы поток мог передавать сообщения другим клиентам, им необходимо обмениваться информацией. Но как это происходит? В самом простом случае это происходит через часть общей памяти; эта общая часть точно указана в строке 04. То есть список, объявленный в строке 04, будет виден всем потокам.
Чтобы всё прошло гладко, в строке 07 потока мы указываем, что список является глобальным. Это не позволит Python создать его локально. Если бы Python создавал его локально, память не была бы разделена между потоками, а нам это необходимо. В строке 08 мы добавляем соединение, создавшее поток, в наш список. Данный шаг очень важен, поскольку каждое новое соединение будет генерировать новый поток, добавляя новое значение в список.
Когда строка 11 будет выполнена, она заблокируется в ожидании получения данных от соединения, и это произойдет во всех потоках по отдельности. Как только клиент отправляет что-то на сервер, мы вводим небольшой цикл в строке 14. Именно в этот момент произойдет "волшебство". Прошу заметить, что мы пройдемся по списку соединений, которые были созданы отдельными потоками. Однако цикл игнорирует это, поскольку память делится между потоками. Таким образом, он будет просматривать все соединения поочередно.
Если условие в строке 15 ложно (то есть если соединение точно соответствует соединению в потоке цикла), мы ничего не сделаем. Но если этот тест верен, выполнится строка 16. Она отправит соединению, за которым следит другой поток, то, что соединение получило от этого потока. Надеюсь, вы эту часть поняли. Помните, что каждый поток соответствует соединению. Когда соединение получает данные, оно заставляет цикл передавать эти данные другим соединениям. Но это не приводит к тому, что другие потоки становятся активными.
«Подождите минутку. Как такое возможно? Если один поток отправляет данные другим соединениям, это должно привести к тому, что остальные потоки станут активными». Нет, дорогой читатель. Ваши рассуждения ошибочны. Не полностью ошибочны, но вы забыли о следующем: сервер не слушает, что происходит внутри потоков. Он прислушивается к тому, что происходит у клиентов. Для каждого клиента сервер кажется подключенным исключительно к выделенному серверу. А клиент не знает, что он находится внутри потока.
Наконец, когда клиент отключается, мы удаляем из списка идентификатор соединения, который добавили в строке 21. Таким образом, все остальные потоки больше не увидят только что закрытое соединение.
Заключительные идеи
Хотя это может показаться очень запутанным, тема потоков такая же серьёзная, как и тема сокетов. Однако данное решение с использованием потоков - лишь одно из многих возможных. В следующей статье мы расскажем о другом способе решения этой проблемы, поскольку данное решение на основе потоков не решает проблему Excel. Это связано с тем, что у нас всё ещё есть проблема с функцией accept, которая блокирует сервер, делая опыт использования Python в Excel не таким уж приятным, по крайней мере для тех, кому нужны сокеты.
| Файл | Описание |
|---|---|
| Experts\Expert Advisor.mq5 | Демонстрирует взаимодействие между Chart Trade и советником (для взаимодействия требуется Mouse Study). |
| Indicators\Chart Trade.mq5 | Создает окно для настройки отправляемого ордера (для взаимодействия требуется Mouse Study). |
| Indicators\Market Replay.mq5 | Создайте элементы управления для взаимодействия с сервисом репликации/моделирования (для взаимодействия требуется Mouse Study). |
| Indicators\Mouse Study.mq5 | Обеспечивает взаимодействие между графическими элементами управления и пользователем (необходимо как для работы системы репликации, так и на реальном рынке). |
| Servicios\Market Replay.mq5 | Создает и поддерживает сервис репликации/моделирования рынка (основной файл всей системы). |
| Код VS C++ Server.cpp | Создает и поддерживает сокет-сервер, разработанный на C++ (версия мини-чат). |
| Code in Python\Server.py | Создание и поддержка сокета Python для связи между MetaTrader 5 и Excel. |
| ScriptsCheckSocket.mq5 | Позволяет выполнить тест соединения с внешним сокетом |
| Indicators\Mini Chat.mq5 | Позволяет реализовать мини-чат через индикатор (для работы требуется использование сервера) |
| Experts\Mini Chat.mq5 | Позволяет реализовать мини-чат в советнике (для работы требуется использование сервера). |
Перевод с португальского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/pt/articles/12745
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Алгоритм эволюции элитных кристаллов — Elite Crystal Evolution Algorithm (CEO-inspired): Практика
Машинное обучение и Data Science (Часть 33): Pandas Dataframe в MQL5, упрощаем сбор данных для машинного обучения
Нейросети в трейдинге: Рекуррентное моделирование микродвижений рынка (Окончание)
Автоматизация запуска терминала для выполнения сервисных задач
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Спасибо за информацию статьи еще читаю... как то сокеты могут помочь для связки двух терминалов МТ5 от разных брокеров? на одном форекс на другом moex - парный трейдинг с двух терминалов можно реализовать через сокеты?
сам эту тему копаю... извините заранее, если маленько не в тему статей вопрос мой... еще читаю статьи и ищу решения для двух МТ5 торги в связке и получение котировок символов от разных бирж по сути и торги в связке после анализа данных по котировкам 2-3-4-5 ти символов....
вот буду сокеты крутить:
Межтерминальный обмен: данные идут напрямую между МТ5 А и МТ5 Б.
События: OnSocketEvent() срабатывает мгновенно при получении данных.
Гибкость данных: можно передавать JSON, бинарные структуры, массивы.
Скорость: задержка на порядок ниже, чем у опроса переменных.
Надёжность: есть механизмы повторной отправки и подтверждения.