【问题标题】:Arbitrary two-way UNIX socket communication任意双向 UNIX 套接字通信
【发布时间】:2012-01-26 10:06:29
【问题描述】:

我一直在用 C 语言开发一个复杂的服务器-客户端系统,但我不确定如何实现套接字通信。

简而言之,系统是一个服务器应用程序,它与数据库通信并使用 UNIX 套接字与一个或多个使用fork() 创建的子进程通信。孩子们的目的是运行游戏服务器。启动游戏服务器的流程是这样的:

  1. 服务器/“管理器”标识数据库中要制作的游戏服务器。 (假设数据库通信已经排序。)
  2. 经理派生出一个孩子(“游戏控制器”)。
  3. 游戏控制器设置两个管道对,然后分叉,用一个管道替换其子级的 stdin,用另一个管道替换它的 stdout 和 stderr。
  4. 然后游戏控制器的子进程运行 execlp() 以开始运行实际的游戏服务器可执行文件。

我在套接字方面的经验相当少。我之前在服务器应用程序上使用过select() 来“多路复用”众多客户端,如 GNU C 文档here 中的简单示例所示。

我现在有一个新的挑战,因为系统必须能够做更多事情:管理器需要能够任意向游戏控制器子节点发送命令(它会通过定期检查数据库来找到)并获得回复,但期望从他们那里传入任意命令/错误并发送回复。

所以,我需要一种“上下文”系统,其中套接字仅在它们之间有意义。换句话说,当命令从管理器发送到游戏控制器时,每一方都需要知道谁在询问并知道回复是什么(因此,它是对哪个命令的回复)。

因为select() 仅用于知道我们何时有传入数据,并且线程应该阻塞它,我是否需要另一个线程来发送数据并获取回复?这是否会要求每个游戏控制器(虽然从技术上讲是“客户端”)使用监听套接字并同时使用 select()

我希望我已经简明扼要地解释了系统和问题;如果需要,我会添加更多细节。谢谢!

【问题讨论】:

  • 我想我理解了大部分内容,但你能解释一下“套接字只在它们之间有意义”的意思吗?
  • 通过套接字发送的命令将得到其对应方的回复,就像您如何accept() 一样,您会收到一个在另一个方向流动的额外套接字?或者也许有一种更有效的方法来实现底层的外推过程。
  • 不会“每一方都知道谁在询问”,因为他们知道他们从哪个套接字接收数据?

标签: c linux sockets ipc fork


【解决方案1】:

好的,我仍然不确定我是否完全理解您的问题所在,所以我将简单介绍一些关于编写客户端/服务器应用程序的内容。如果我偏离了轨道,请告诉我。

  1. 服务器知道哪些客户端对应哪个套接字的方式是客户端告诉服务器。本质上,您需要有一个登录协议。当游戏控制器连接到服务器时,它会发送一条消息,上面写着“嗨,我正在主机 xyz 上注册为控制器 foo1,端口 abc...”以及服务器需要了解其客户端的任何其他信息。服务器将保留一个将套接字映射到客户端元数据、状态等的数据结构。每当它收到一条新消息时,它都可以轻松地从传入的主机/端口映射到它的元数据。或者您的协议可以要求在每条传入消息上,将客户端将其注册的名称作为字段发送。

  2. 可以通过多种方式处理请求/响应。首先让我们在服务器端处理它的网络部分。正如您所提到的,管理此问题的一种方法是使用 select(或 poll,或 epoll)来多路复用套接字。这实际上通常被认为是更复杂的做事方式。另一种方法是为每个传入的客户端生成一个线程(或派生一个进程,这在当今不太常见)。每个生成的线程都可以读取自己分配的套接字,一次响应一个消息,而不必担心除了它自己的客户端之外还有其他客户端。如果有很多客户端,这种简单的一对一线程到套接字模型会失效,但如果不是这种情况,那么值得考虑。

第 2 部分仅涵盖客户端向服务器发送消息,以及服务器进行回复。当服务器想要发起通信时会发生什么?它是如何做到的,客户又是如何处理的?此外,您如何在应用程序级别对模型进行通信建模,这意味着假设我们已经完成了读/写部分,我们如何知道要发送什么?您可能希望根据状态机对事物进行建模。还有很多事情需要处理,比如客户端崩溃时会发生什么?服务器崩溃的时候怎么办?另外,如果您真的有使用 select 的心,也许是因为您期望有很多客户呢?明天我会尝试在这个答案中添加更多内容。

【讨论】: