1

在用 C++ 实现应用服务器及其客户端库时,我无法找到一种干净可靠的方法来在 Windows 上的服务器关闭时停止客户端进程。

假设服务器和它的客户端在同一个用户下运行,要求是:

  • 该解决方案应适用于以下情况:
    • 每个客户端都可能具有控制台或 gui。
    • 用户可能没有特权。
    • 客户端可能会或变得无响应(无限循环、死锁)。
    • 客户端可能是也可能不是服务器的孩子(直接或间接)。
  • 除非被客户端缺陷阻止,否则应允许客户端有机会干净地退出(释放他们的资源,将一些数据同步到磁盘......)和一些合理的时间这样做。
  • 在关闭过程中,所有客户端返回代码都应提供给服务器(如果可能)。
  • 服务器将等到所有客户端都消失。

截至本次编辑,以下大多数答案都主张在服务器及其客户端之间使用共享内存(或其他 IPC 机制)来传达关闭命令和客户端状态。这些解决方案可以工作,但需要客户端成功初始化库。

我没有说的是,服务器还用于启动客户端,在某些情况下,其他程序/脚本根本不使用客户端库。不依赖于服务器和客户端之间的优雅通信的解决方案会更好(如果可能的话)。

前段时间,我偶然发现了一个 C 代码片段(我相信在 MSDN 中),它执行了以下操作:

  1. 在进程中通过 CreateRemoteThread 启动一个线程来关闭。
  2. 让那个线程直接调用ExitProcess。

不幸的是,现在我正在寻找它,但我无法找到它,而且搜索结果似乎暗示这个技巧在 Vista 上不再适用。对此有任何专家意见吗?

4

5 回答 5

2

如果使用线程,一个简单的解决方案是使用命名系统事件,线程在事件上休眠等待它发出信号,控制应用程序可以在它希望客户端应用程序退出时发出事件信号。

对于 UI 应用程序,它(线程)可以将消息发布到主窗口,WM_ CLOSE 或 QUIT 我忘了哪个,在控制台应用程序中它可以发出 CTRL-C 或者如果主控制台代码循环它可以检查一些退出条件由线程设置。

无论哪种方式,与其找到客户端应用程序并告诉他们退出,不如使用操作系统发出信号他们应该退出。如果它使用 WaitForSingleObject 睡眠,睡眠线程将几乎不使用 CPU 占用空间。

于 2008-09-17T12:48:42.100 回答
1

您需要客户端和服务器之间的某种 IPC。如果所有客户都是孩子,我认为管道会最简单;因为它们不是,我猜服务器操作的共享内存段可用于注册客户端,发出关闭命令,并收集成功关闭的客户端发布的返回代码。

在这个共享内存区域中,客户端放置它们的进程 ID,以便服务器可以使用 TerminateProcess() 强制终止任何无响应的客户端(以服务器权限为模)。

于 2008-09-17T10:27:18.740 回答
1

如果你愿意走IPC路由,就让客户端和服务端正常通信双向,让服务端请求客户端关闭。或者,如果做不到这一点,让客户轮询。或者作为最后的手段,当向服务器发出请求时,应指示客户端退出。您可以让库用户注册退出回调,但我知道的最好方法是在客户端被告知关闭时简单地在客户端库中调用“退出”。如果客户端卡在关闭代码中,服务器需要能够通过忽略该客户端的数据结构和连接来解决它。

于 2008-09-17T12:51:06.523 回答
1

使用 PostMessage 或命名事件。

回复:PostMessage——除 GUI 之外的应用程序,以及除 GUI 线程之外的线程,都可以有消息循环,这对于这样的东西非常有用。(事实上​​,COM 在底层使用了消息循环。)我以前用 ATL 做过,但对它有点生疏。

如果您想对来自“不良”进程的恶意攻击具有鲁棒性,请将客户端/服务器共享的私钥作为消息中的参数之一包含在内。

命名事件方法可能更简单。使用名称为客户端/服务器共享的秘密名称的 CreateEvent,并让相应的应用程序在其主循环中检查事件的状态(例如,超时为 0 的 WaitForSingleObject)以确定是否关闭。

于 2008-12-24T00:13:02.790 回答
0

这是一个非常笼统的问题,并且存在一些不一致之处。

虽然这不是 100% 的规则,但大多数控制台应用程序运行完成,而 GUI 应用程序运行直到用户终止它们(并且服务运行直到通过 SCM 停止)。因此,请求关闭 GUI 会更容易。您向他们发送相当于 Alt-F4 的内容。但是对于控制台程序,您必须向他们发送相当于 Ctrl-C 的命令并希望他们处理它。在这两种情况下,您只需等待。如果该过程持续存在,则将其击落(TerminateProcess)并祈祷损害有限。但是您的硬盘驱动器可能会装满临时文件。

GUI 应用程序通常没有退出代码——它们会去哪里?并且根据定义强制终止的控制台进程不会退出,因此它没有退出代码。因此,在服务器关闭的情况下,不要期望退出代码。

如果您连接了调试器,则通常无法从另一个应用程序关闭该进程。这将使调试器无法调试退出代码!

于 2008-09-17T10:28:20.160 回答