VB6 TCP IP-коммуникация без Doevents или подклассов - PullRequest
0 голосов
/ 02 мая 2018

Что мне нужно сделать с приложением VB6, которое я поддерживаю, так это:

  1. Установите соединение с известным адресом и портом через Ethernet сеть.
  2. Отправить запрос

  3. Ждите ответа.

Я пытался использовать замену WinSock и Winsock, но все они в той или иной форме используют цикл обмена сообщениями, свойственный различным приложениям Windows. Я не знаю достаточно об API Winsock, как реализовать вышеупомянутый алгоритм в VB6 (или на любом другом языке.

Мое приложение - это программное обеспечение VB6 CAD / CAM, которое управляет металлорежущими станками по выделенной сети Ethernet. Программное обеспечение поддерживается уже 20 лет, и мы разработали несколько драйверов для различных типов контроллеров движения. На сегодняшний день API для этих контроллеров движения состоит из

  1. Открытие соединения с оборудованием
  2. Отправка запроса аппаратному обеспечению (например, Положение оси)
  3. Ожидание ответа (обычно происходит в миллисекундах).

Некоторые из этих контроллеров работают через сеть Ethernet, но до сих пор мне никогда не приходилось напрямую взаимодействовать с портами. Я использовал поставляемые компанией библиотеки для обработки вещей. И они работают так, как я упоминал выше, и выдают ошибку тайм-аута, если ответ не происходит в течение определенного времени.

Проблема с Winsock заключается в том, что мне нужно вставить DoEvents, чтобы получить ответ. Это вызывает хаос в том, как мы справляемся с многозадачностью в нашем приложении VB6. Замена, такая как CSocketMaster, использует подклассы, которые также вызывают хаос с нашей многозадачностью.

Так что любая помощь о том, как использовать Winsock API или стороннюю DLL, которая может делать то, что мне нужно, как описано выше. Я бы не спрашивал, не видел ли я, чтобы другие элементы управления движением делали то, что я хочу.

Ответы [ 4 ]

0 голосов
/ 10 мая 2018

Как выяснилось, ответ заключался в реализации предложения Аллена.

Конкретной проблемой было взаимодействие между двумя устройствами, участвующими в управлении машиной. Устройство, которое выполняло управление движением, действует как сервер, в то время как ПК, предоставляющий данные движения, был клиентом.

Сеть Ethernet использовалась вместо проприетарного интерфейса шины или последовательного интерфейса RS-232/422. Многие из соображений, связанных с обслуживанием данных через широко распространенный Интернет, не были фактором. Сеть состояла из известных устройств с постоянными IP-адресами, которые прослушивали определенные порты.

После разговора с людьми, которые делают другое управление движением. Логика для клиента оказалась на удивление простой.

  1. Отправка данных
  2. Подождите в цикле, чтобы ответ вспыхнул, если он займет слишком много времени.
  3. Обработка любых ошибок в соединении.

На стороне сервера нам повезло, что мы контролировали программное обеспечение, запущенное на контроллере движения. Таким образом, коммуникационная петля была разработана так, чтобы быть максимально быстрой. Одним из ключевых моментов было держать все данные ниже 512 байт, чтобы они все содержались в одном пакете. Они также уделяли большое внимание установлению приоритета обработчику связи и структуре данных, чтобы он мог отправлять ответ за десятки микросекунд.

Другой момент заключался в том, что в этом приложении выделенных клиентов и серверов UDP предпочтительнее, чем TCP, поскольку операционные системы, особенно Windows, имели привычку неожиданно завершать бездействующие соединения TCP.

Поскольку клиентское программное обеспечение постепенно переходит на .NET Framework, это был еще один фактор для реализации идеи Аллена. Библиотека, обсуждаемая @wqw, тоже работает.

0 голосов
/ 03 мая 2018

Я думаю, что редко бывает уместно заниматься сетью синхронно, однако это не сеть в традиционном смысле. Это провод от ПК к контроллеру. Это как строка между двумя банками. В этом случае с большой старой программой наиболее соответствующий подход - тот, который работает лучше всего и является самым простым к обслуживанию. </ end2cents>

Если VB6 + Winsock не работает для вас, запись этого в .NET и встраивание его в видимую COM-DLL для вашей программы VB6 будет отвечать всем требованиям.

Пример ниже поможет вам начать. Если вы делаете больше, чем случайный вызов, он будет медленным, поскольку он открывает и закрывает соединение при каждом вызове. Его должно быть легко расширить, чтобы можно было повторно использовать открытое соединение для обратной связи между ПК и контроллером. Только будьте осторожны, чтобы не вызвать утечку памяти!

/// <summary>
/// Sends a message to the specified host:port, and waits for a response
/// </summary>
public string SendAndReceive(string host, int port, string messageToSend, int millisecondTimeout)
{
    try
    {
        using (var client = new TcpClient())
        {
            client.SendTimeout = client.ReceiveTimeout = millisecondTimeout;
            // Perform connection
            client.Connect(host, port);

            if (client.Connected)
            {
                using (var stream = client.GetStream())
                {
                    // Convert the message to a byte array
                    var toSend = Encoding.ASCII.GetBytes(messageToSend);

                    // Send the message
                    stream.Write(toSend, 0, toSend.Length);

                    // Get a response
                    var response = new byte[client.ReceiveBufferSize];
                    stream.Read(response, 0, client.ReceiveBufferSize);

                    return Encoding.ASCII.GetString(retVal);
                }
            }
            else
            {
                return null;
            }
        }
    }
    catch
    {
        return null;
    }
}
0 голосов
/ 04 мая 2018

Проверьте VbAsyncSocket repo на github для реализации чистых асинхронных сокетов VB6 (используя WSAAsyncSelect API для сокетов для отправки уведомлений о событиях).

Вопреки своему названию класс поддерживает поддержку SyncSendArray и SyncReceiveArray методов для синхронных операций - без DoEvents, но с Timeout s.

В том же репо есть удобный класс cWinSockRequest, который очень похож на объект WinHttpRequest, выпекаемый в ОС. Этот вспомогательный класс будет вам очень знаком, если у вас есть опыт работы с JSON / XML (обычно это RESTful-сервисы через http / https) для доступа к сервисам / устройствам через обычные сокеты tcp / udp.

Другим вариантом будет использование cTlsClient добавленного класса, который может подключаться к хосту / устройству по протоколу tcp (здесь нет udp) и обеспечивает ReadText / WriteText и ReadArray / WriteArray (синхронно) методов. Дополнительное преимущество заключается в том, что в этом случае класс поддерживает как простые незашифрованные сокеты, так и зашифрованные каналы SSL.

Мы используем эти классы для (синхронного) доступа к принтерам ESP / POS из наших LOB-приложений. Большинство принтеров POS также предоставляют последовательные (USB-COM) соединения, поэтому мы абстрагируем наши классы доступа с разъемами - SyncWaitForEvent через асинхронные сокеты и WaitForMultipleObjects на перекрывающихся ReadFile / WriteFile API (о, ирония)

0 голосов
/ 03 мая 2018

Ваша проблема в том, что вы используете элемент управления Winsock неправильно. Вероятно, это связано с недостатком вашей модели взаимодействия.

Не «отправляй и жди», потому что блокировка, подобная этой, является вашей большой ошибкой. В любом случае, нет никакого «ожидания», если вы не думаете, что сидение в петле гудения ждет.

Вместо этого отправьте запрос и выйдите из этого обработчика событий. Весь ваш код содержится в обработчиках событий, вот как это работает. Затем при возникновении событий DataArrival вы добавляете новый поступивший фрагмент в буфер потока, а затем сканируете собранный поток для получения полного ответа. Затем продолжайте и обработайте ответ.

Обработка тайм-аутов с помощью таймера, который вы включаете после отправки. Когда вы соберете законченный ответ, отключите таймер. Если интервал истек и событие Timer сгенерировано, выполните вашу обработку ошибок там.

Кажется, у вас очень болтливый протокол, поэтому вам не нужно больше ничего делать. Например, вы можете просто очистить буфер потока после обработки полного ответа, поскольку в любом случае в нем больше ничего не может быть.

Забудьте о «многозадачности» и избегайте вызовов DoEvents (), таких как чума.

Это очень простой материал.

...