0

我正在尝试使用 C 对群聊系统的服务器端进行编程,而我的朋友正在对客户端进行编程。对于服务器接收到的每个客户端连接,它会派生一个子进程以处理客户端并继续接受任何可能的其他客户端。

服务器需要向每个当前连接的客户端发送所有在线用户(连接的客户端)的列表,因此我使用了管道。基本上,当创建子进程时,它通过套接字从客户端接收信息,并将这些信息发送给父进程,父进程通过管道保存所有客户端的列表。每次客户进行更改(例如开始聊天或断开连接)时,都必须更新此列表。例如,如果客户端断开连接,则子级通过管道向父级发送消息,并且父级对列表进行必要的操作以使其得到更新。请注意,管道是为每个新连接创建的。

我的问题是,例如,如果我一个接一个地收到 3 个连接,而第二个孩子断开连接,则父母不会从管道中读取信息,因为这样的父母与第二个孩子有不同的管道。(请记住,由于已建立第三个连接,因此已创建新管道)。我该如何解决这个问题?

我也尝试过创建一个公共管道,但如果我在读/写之前没有关闭管道,我会收到错误,如果我关闭它们,我会在第二个客户端连接时出现分段错误,因为管道将被关闭。

任何帮助将不胜感激,因为我一直在寻找几个小时无济于事。

谢谢。

4

1 回答 1

0

父服务器进程知道何时创建子进程,因为它创建了子进程。它可以通过设置 SIGCLD 信号处理程序来判断孩子何时死亡,以便在孩子死亡时得到通知。第 N孩子有 N-1 条管道要关闭——那些通往其他孩子的管道(除非一些孩子已经死了)。父进程关闭它创建的管道的写端;子进程关闭它继承的管道的读取端(这留下了一个到客户端的套接字以及为其创建的管道的写入端以与父进程通信)。

如果您需要知道孩子何时开始与客户端通信,那么您需要通过管道将消息从孩子发送到父母。如何判断孩子何时停止交流并不那么明显——在您宣布孩子再次空闲之前需要多长时间?

在父级中,您最终会以某种形状或形式(select(), poll(), epoll())在侦听套接字和所有读取管道上进行轮询。当某些活动发生时,父母会醒来并做出适当的反应。这是一个可行的设计,只要它不必扩展到数千或更多的客户。它需要一些小心,特别是在关闭足够多的文件描述符时。

你说:

我的问题是,例如,如果我一个接一个地收到 3 个连接,而第二个孩子断开连接,则父母不会从管道中读取信息,因为这样的父母与第二个孩子有不同的管道。(请记住,由于已建立第三个连接,因此已创建新管道)。我该如何解决这个问题?

父级应该有一个打开的文件描述符数组(为读取不同子级而打开的管道),以及管道另一端的子级(PID)指示。当父母在管道上获得EOF时,或者当它被通知孩子已经死亡(通过waitpid()或亲戚)时,父母将关闭管道。轮询机制会告诉你管道何时关闭,至少是间接的(你会被告知文件描述符不会阻塞,然后你会得到 EOF——读取的零字节)。

在您的场景中,父级打开了一个侦听套接字,外加 3 个用于通向 3 个子级的管道的读取文件描述符(加上标准输入、输出和错误,可能还有 syslog)。

尽管您可以使用来自所有孩子的单个管道,但处理起来要复杂得多。您必须确定消息中的每条消息是由哪个孩子编写的,以确保消息是由孩子以原子方式编写的。父母必须能够在任何时候告诉阅读多少,以免感到困惑。单管道的优点是轮询系统调用要做的文件描述符操作较少;它还可以无限扩展(不会用完文件描述符)。

在这两种情况下,您都不应该遇到核心转储问题。

于 2013-05-26T06:54:30.673 回答