1

我需要维护的 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 的任何帮助都可以完成我需要做的事情,如上所述。我不会问我是否还没有看到其他运动控制做我想做的事情。

4

4 回答 4

4

查看github 上的VbAsyncSocket 存储WSAAsyncSelect库,了解纯 VB6 异步套接字实现(使用API 用于套接字发布事件通知)。

与它的名字相反,该类确实支持同步操作SyncSendArraySyncReceiveArray方法——没有DoEvents但有Timeouts。

在同一个 repo 中有一个方便的贡献类,它与嵌入操作系统cWinSockRequest的对象非常相似。WinHttpRequest如果您有使用 JSON/XML(通常是通过 http/https 的 RESTful 服务)通过普通 tcp/udp 套接字访问服务/设备的经验,那么这个帮助程序类将非常熟悉。

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

我们正在使用这些类从我们的 LOB 应用程序(同步)访问 ESP/POS 打印机。大多数 POS 打印机也提供串行(USB 到 COM)链接,因此我们正在抽象使用连接器类的访问——SyncWaitForEvent通过异步套接字和WaitForMultipleObjects重叠的ReadFile/ WriteFileAPI(哦,具有讽刺意味)

于 2018-05-04T12:43:35.010 回答
2

我认为很少适合同步进行网络,但这不是传统意义上的网络。这是从 PC 到控制器的电线。这就像两个罐子之间的一根绳子。在这种大型旧程序的情况下,最合适的方法是效果最好且最容易维护的方法。< /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;
    }
}
于 2018-05-03T19:42:38.827 回答
1

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

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

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

在与进行其他运动控制的人交谈后。客户端的逻辑结果非常简单。

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

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

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

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

于 2018-05-10T16:56:39.673 回答
0

您的问题是您错误地使用了 Winsock 控件。这可能源于您的交互模型中的缺陷。

不要“发送并等待”,因为这样阻塞是你的大错误。无论如何都没有“等待”,除非您认为坐在嗡嗡声循环中正在等待。

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

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

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

忘记“多任务处理”并避免像瘟疫一样的 DoEvents() 调用。

这是非常简单的东西。

于 2018-05-03T03:38:46.127 回答