没有 Doevents 或子类化的 VB6 TCP IP 通信

VB6 TCP IP Communication without Doevents or subclassing

我维护的 VB6 应用程序需要执行以下操作。

  1. 通过以太网建立到已知地址和端口的连接 网络。
  2. 发送请求

  3. 等待回复。

我尝试使用 WinSock 和 Winsock 替代品,但它们都以一种或另一种形式依赖于各种 Windows 应用程序中固有的消息传递循环。我不太了解 Winsock API 如何在 VB6(或任何其他语言)中实现上述算法。

我的应用程序是一个 VB6 CAD/CAM 软件,它通过专用以太网网络控制金属切割机。该软件已经维护了20年,我们为不同类型的运动控制器开发了多个驱动程序。迄今为止,这些运动控制器的 API 包括

  1. 打开与硬件的连接
  2. 向硬件发送请求(例如轴的位置)
  3. 等待响应(通常以毫秒为单位)。

其中一些控制器通过以太网网络工作,但直到现在我都不必直接与端口交互。我使用公司提供的库来处理事情。它们按照我上面提到的方式工作,如果在某个定义的时间内没有响应,则会抛出超时错误。

Winsock 的问题是我必须插入 DoEvents 才能得到响应。这会严重破坏我们在 VB6 应用程序中处理多任务的方式。像 CSocketMaster 这样的替代品使用子类化,这也会对我们的多任务处理造成破坏。

所以关于如何使用 Winsock API 或第三方 dll 的任何帮助都可以完成我上面概述的需要做的事情。我不会问我没看到其他运动控制做我想做的事

你的问题是Winsock控件使用不当。这可能源于您的交互模型存在缺陷。

不要 "send and wait" 因为那样阻止是你的大错。反正没有 "waiting" 除非你认为坐在嗡嗡声循环中等待。

改为发送您的请求并退出该事件处理程序。您的所有代码都包含在事件处理程序中,这就是它的工作方式。然后,当引发 DataArrival 事件时,您将新到达的片段附加到流缓冲区,然后扫描组装的流以获取完整响应。然后继续处理响应。

使用发送后启用的计时器控件处理超时。组装完成的响应后,请禁用计时器。如果间隔结束并且引发了 Timer 事件,请在那里进行错误处理。

你似乎有一个特别健谈的协议,所以你不应该做任何其他事情。例如,您可以在处理完一个完整的响应后清除您的流缓冲区,因为那里无论如何都不会留下任何其他东西。

忘记 "multitasking" 并避免像瘟疫一样调用 DoEvents()。

这是非常简单的东西。

I think it is rare for it to be appropriate to do networking synchronously, however this isn't networking in the traditional sense. This is a wire from a PC to a controller. This is like a string between two cans. In this case with a large old program, the most appropriate approach is the one that works the best and is the easiest to maintenance. < /end2cents >

如果 VB6 + Winsock 不适合您,那么在 .NET 中编写此代码并将其构建为您的 VB6 程序的 COM 可见 DLL 将符合要求。

下面的示例将帮助您入门。如果你不只是偶尔调用它,它会很慢,因为它在每次调用时打开和关闭连接。扩展它应该很容易,以允许重新使用开放连接在 PC 和控制器之间来回通信。请注意不要造成内存泄漏!

/// <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;
    }
}

查看 VbAsyncSocket repo on github for pure VB6 asynchronous sockets implementation (using WSAAsyncSelect API 用于 post 事件通知的套接字)。

与其名称相反,class 支持同步操作的 SyncSendArray and SyncReceiveArray 方法——没有 DoEvents 但有 Timeouts.

在同一个 repo 中有一个方便的 cWinSockRequest 贡献了 class,它与 WinHttpRequest 对象非常相似 OS。如果您有使用 JSON/XML(通常 http/https 上的 RESTful 服务)通过普通 [=46] 访问 services/devices 的经验,那么这个助手 class 将非常熟悉=] 套接字。

另一种选择是使用 cTlsClient contributed class,它可以通过 tcp(这里没有 udp)连接到 host/device 并提供 ReadText/WriteTextReadArray/WriteArray (同步)方法。这里的额外好处是 class 支持普通未加密套接字和 SSL 加密通道(如果需要)。

我们正在使用这些 classes 从我们的 LOB 应用程序(同步)访问 ESP/POS 打印机。大多数 POS 打印机也提供串行(USB 到 COM)链接,所以我们正在抽象我们的访问 w/ connector classes -- SyncWaitForEvent over async sockets and WaitForMultipleObjects on overlapped ReadFile /WriteFile APIs(哦,讽刺)

事实证明,答案涉及实施艾伦的建议。

具体问题是涉及一台机器控制的两个设备之间的通信。进行运动控制的设备作为服务器,而提供运动数据的PC是客户端。

正在使用以太网代替专有总线接口或 RS-232/422 串行接口。在广泛的互联网上提供数据所涉及的许多考虑因素都不是一个因素。该网络由驻留在侦听特定端口的固定 IP 的已知设备组成。

和其他做运动控制的人聊过之后。事实证明,客户端的逻辑出奇地简单。

  1. 发送数据
  2. 如果响应时间过长,请循环等待响应。
  3. 处理连接中的任何错误。

在服务器端,我们很幸运,我们可以控制运动控制器上的软件运行。因此,通信回路被设计为尽可能快。一个关键点是将所有数据保持在 512 字节以下,以便所有数据都包含在一个数据包中。他们还非常小心地确定通信处理程序和数据结构的优先级,以便它可以在几十微秒内发出响应。

另一点是,在这种专用客户端和服务器的应用程序中,UDP 优于 TCP,因为操作系统特别是 Windows 习惯于意外关闭空闲 TCP 连接。

因为客户端软件正在慢慢过渡到 .NET 框架,这是实现 Allen 想法的另一个因素。 @wqw 讨论的库也能正常工作。