【问题标题】:How to detect a client disconnect using a named pipe client/server?如何使用命名管道客户端/服务器检测客户端断开连接?
【发布时间】:2010-03-03 21:58:58
【问题描述】:

我正在学习命名管道,并且正在使用 MSDN 文档中的命名管道客户端和服务器示例:

Named Pipe Server

Named Pipe Client

我修改了客户端,这样我就可以在控制台中输入消息,然后将它们发送到服务器,在那里它显示消息并发回回复。本质上,我添加了一个循环,它在 SetNamedPipeHandleState() 调用之后开始并在 CloseHandle() 调用之前结束(即打开和关闭发生在循环之外,所以我在循环中使用相同的管道句柄)。

我的问题是,如果我杀死客户端(通过关闭它或通过任务管理器结束它),服务器端有什么方法可以检测到断开连接?

我尝试使用 GetNamedPipeHandleState() 希望它返回失败,而对 GetLastError() 的调用将返回 ERROR_PIPE_NOT_CONNECTED,但事实并非如此。由于此服务器的设置方式,我必须在 CompletedReadRoutine 函数中执行此操作并创建“受控”故障。我所做的是,在服务器的 CompletedReadRoutine 上有一个断点:

  1. 启动服务器
  2. 启动客户端
  3. 通过客户端发送消息(在此处命中服务器中的断点)
  4. 杀死了客户
  5. 单步执行 GetNamedPipeHandleState

对 GetNamedPipeHandleState() 的调用成功返回,所以我从来不用调用 GetLastError()。当它到达 WriteFileEx 调用时,它会失败,此时对 GetLastError 的调用会返回 ERROR_NO_DATA。

查看管道函数,我看不到任何其他可能对这里有所帮助的东西。我遗漏了一些东西,或者客户端断开连接只是无法检测到。

我唯一能想到的另一件事是收集连接客户端的 pid(通过 GetNamedPipeClientProcessId)并关闭看门狗线程以检查它们是否还活着。不过,光是考虑这样做就会激起我的诡异感。

有没有办法在使用命名管道时检测断开的客户端?

【问题讨论】:

    标签: winapi named-pipes disconnect


    【解决方案1】:

    ReadFile() 不返回和错误,GetLastError() 然后返回ERROR_BROKEN_PIPE

    【讨论】:

    • 只有当我非常小心地使用服务器中的断点终止客户端时,我才能在服务器逻辑中的特定点终止客户端。例如,如果我启动客户端并在初始连接之后但在服务器第一次调用 ReadFileEx 之前将其终止,那么是的,我得到 ERROR_BROKEN_PIPE。问题是服务器等待对 WaitForSingleObjectEx 的调用。如果客户端出现故障,它不会写入或读取先前连接到管道实例的数据。
    • 我真的不认为这些样本是如何做事的一个很好的例子......就我个人而言,我总是在管道上有一个重叠的读取待处理,这样你应该总是得到一个客户离开时的通知...
    • 我听到你的声音并同意我所拥有的示例不是我应该查看的我希望通信工作的方式。感谢您的意见。
    • 我有一个类似的问题,命名管道服务器使用重叠 I/O 并且线程正在等待 OVERLAPPED 结构的事件对象。问题是,当客户端断开连接时,即使在服务器端调用了 ReadFile(重叠 I/O),管道的服务器端也没有发出任何事件信号。编辑:我试图将其发布为对先前答案的评论,但无法发布。
    【解决方案2】:

    ReadFile() + GetLastError() 把工作做好。以下是它们如何与 I/O 完成端口一起使用(我的实现是在 python+ctypes 中,但想法应该很清楚):

    def connect():
        GetQueuedCompletionStatus()
        receive()
    
    def receive():
        while True:
            ret_code = ReadFile()
            if ret_code == 0 and GetLastError() == ERROR_BROKEN_PIPE:
                # client disconnected
            GetQueuedCompletionStatus()
    

    我们正在等待完成数据包,当客户端连接时,我们切换到主循环。在主循环中,我们读取管道并通过查看ReadFile() 返回码和GetLastError() 检查客户端是否已断开连接。然后,我们将再次等待完成数据包。

    客户端可以在任何阶段断开连接。完成数据包将被排队,我们将得到ERROR_BROKEN_PIPE

    【讨论】:

    • 这是哪种编程语言?它看起来有点像 Python,但 AFAIK winapi 仅适用于 C、C++、C# 和 Visual Basic。
    • 是的,这是 Python,但是您可以使用 ctypes 库调用 winapi 函数。允许这样做的机制称为 FFI:en.wikipedia.org/wiki/Foreign_function_interface