如何使用命名管道客户端/服务器检测客户端断开连接?

10

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

命名管道服务器

命名管道客户端

我修改了客户端,以便可以在控制台中输入消息并将其发送到服务器,服务器显示该消息并发送回复。基本上,在SetNamedPipeHandleState()调用之后,在CloseHandle()调用之前添加了一个循环(即开放和关闭发生在循环外部,因此我在循环内使用相同的管道句柄)。

我的问题是,如果我关闭客户端或通过任务管理器结束客户端,是否有任何方法让服务器端检测到断开连接?

我尝试使用GetNamedPipeHandleState(),希望它返回失败并且调用GetLastError()会返回ERROR_PIPE_NOT_CONNECTED,但事实并非如此。由于必须在CompletedReadRoutine函数中执行此操作,并创建一个“受控”失败,所以我必须按照服务器上的CompletedReadRoutine设置断点:

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

调用GetNamedPipeHandleState()成功返回,因此我无法执行GetLastError()调用。当它到达WriteFileEx调用时,它将失败,并且在该点调用GetLastError将返回ERROR_NO_DATA。

查看管道函数,我找不到其他可能有帮助的内容。我是否遗漏了某些东西或是客户端断开连接不可检测的情况?

我能想到的唯一另外一件事情就是通过GetNamedPipeClientProcessId收集连接客户端的pid,并开启看门狗线程来检查它们是否还存活。然而,光是想到这样做就让我的警觉感觉起来了。

在使用命名管道时,是否有一种方法可以检测到已断开连接的客户端?

2个回答

9
<不是ReadFile()返回错误,然后GetLastError()返回ERROR_BROKEN_PIPE吗?>

只有在服务器使用断点仔细地杀死客户端,以便可以在服务器逻辑的特定点上杀死客户端时,才会出现ERROR_BROKEN_PIPE。例如,如果我启动客户端并在初始连接后立即将其关闭,但在服务器第一次调用ReadFileEx之前将其关闭,则是的,我会得到ERROR_BROKEN_PIPE。问题在于服务器等待调用WaitForSingleObjectEx。如果客户端关闭,则不会写入或读取先前连接到的管道实例。 - Robert Groves
4
个人认为这些样本并不是如何做事情的好示例...就我个人而言,我总是会在管道上有一个重叠的读取等待,这样当客户端关闭时,您应该总是能够得到通知... - Len Holgate
我听到了您的意见,认同我的例子不是我想要的通信方式。感谢您的建议。 - Robert Groves
2
我有一个类似的问题,其中命名管道服务器使用重叠I/O,并且线程正在等待OVERLAPPED结构的Event对象。 问题在于,当客户端断开连接时,在管道的服务器端没有事件被标记,即使在服务器端调用了ReadFile(重叠I/O)。编辑:我试图将其发布为先前答案的评论,但无法实现。 - user415104

1

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,但据我所知,winapi只能用于C、C++、C#和Visual Basic。 - wizzwizz4
是的,这是Python,但您可以使用ctypes库调用winapi函数。允许这样做的机制称为FFI:https://en.wikipedia.org/wiki/Foreign_function_interface - wombatonfire

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接