简体   繁体   English

多个子进程在同一个管道上读/写

[英]Multiple child processes reading/writing on the same pipe

I am currently learning sockets programming using C in a Linux environment. 我目前正在Linux环境中学习使用C的套接字编程。 As a project I am attempting to write a basic chat server and client. 作为一个项目,我试图编写一个基本的聊天服务器和客户端。

The intention is to have the server fork a process for each client that connects. 目的是让服务器为每个连接的客户端分叉一个进程。

The problem that I am having is reading data in one child and writing it to all of the connected clients. 我遇到的问题是在一个孩子中读取数据并将其写入所有连接的客户端。

I have tried to accomplish this by looping on a call to select in the child that waits for data to arrive on the socket or read end of the pipe. 我试图通过循环调用select等待数据到达套接字或读取管道末端的子进程来完成此操作。 If it arrives on the socket the idea is that it writes to the write end of the pipe which causes select to return the read end of the pipe as ready for reading. 如果它到达套接字的想法是它写入管道的写端,这导致select返回管道的读端,准备好读取。

As this pipe is shared between all the children, each child should then read the data on the pipe. 由于此管道在所有子项之间共享,因此每个子项都应读取管道上的数据。 This does not work as the data pipe, it appears, cannot be read by each child process at the same time and the children that "miss" the data block in the call to read. 这不起作用,因为它看起来不是每个子进程同时读取的数据管道以及在调用中“错过”数据块的子进程。

Below is the code in the child process that does this: 以下是子进程中执行此操作的代码:

for( ; ; )
{
  rset = mset;
  if(select(maxfd+1, &rset, NULL, NULL, NULL) > 0)
  {
    if(FD_ISSET(clientfd, &rset))
    {
      read(clientfd, buf, sizeof(buf));
      write(pipes[1], buf, strlen(buf));
    }
    if(FD_ISSET(pipes[0], &rset))
    {
      read(pipes[0], buf, sizeof(buf));
      write(clientfd, buf, sizeof(buf));
    }
  }
}

I am presuming the method that I am currently using simply will not work. 我假设我目前使用的方法根本不起作用。 Is it going to be possible for messages received from a client to be written to all of the other connected clients via IPC? 是否可以通过IPC将从客户端收到的消息写入所有其他连接的客户端?

Thanks 谢谢

To get around the problem of a child reading from the pipe more data than it should (and in-turn making another child get "stuck" trying to read from an empty pipe), you should probably look into using either POSIX message queues or a single pipe between the parent and an individual child processes rather than a single global pipe to communicate between the parent and child processes. 为了解决孩子从管道中读取的数据超出应有的数据(并且反过来让另一个孩子“试图从空管道中读取”),你应该考虑使用POSIX消息队列或者父节点和单个子进程之间的单个管道进程而不是单个全局管道在父进程和子进程之间进行通信。 As it stands right now, when the server writes to the pipe to communicate with its children, it's not really able to control exactly which child will read from the pipe at any given time, since the scheduling of processes by the OS is non-deterministic. 就目前而言,当服务器写入管道与其子进程通信时,由于操作系统对进程的调度是不确定的,因此无法确切地控制在任何给定时间从管道中读取哪个子进程。 。 In other words, without some type of synchronizing mechanism or read/write barriers, if the server writes to the pipe, there is nothing in your code stopping one child from "skipping" a read, and a second child from doing a double-read, leaving another child that should have gotten the broadcasted data from the server starved, and therefore blocked. 换句话说,如果没有某种类型的同步机制或读/写障碍,如果服务器写入管道,则代码中没有任何内容阻止一个孩子“跳过”读取,而第二个孩子不进行双读留下另一个应该从服务器上获取广播数据的孩子,因此被阻止了。

A simple way around this could again be to have a single private pipe shared between the parent and an individual child. 解决这个问题的一个简单方法可能是在父级和单个子级之间共享一个私有管道。 Thus in the server the child processes can read from the client, send that data back to the parent process, and the parent can then, using the entire list of pipe descriptors that it's accumulated for all the children, write back to each individual child process the broadcast message which is then sent back to each client. 因此,在服务器中,子进程可以从客户端读取,将该数据发送回父进程,然后父进程可以使用为所有子进程累积的整个管道描述符列表,回写每个子进程广播消息然后被发送回每个客户端。 No child ever gets "starved" of data since there is no possibility of a double-read by another child-process. 没有孩子会被“饥饿”的数据,因为没有可能被另一个子进程双重阅读。 There is only a single reader/writer on each pipe, and the communication is deterministic. 每个管道上只有一个读写器,通信是确定的。

If you don't want to handle multiple pipes for each child in the server's parent process, you could use a global message queue using POSIX message queues (found in mqueue.h ). 如果您不想为服务器的父进程中的每个子进程处理多个管道,则可以使用POSIX消息队列(在mqueue.h找到)使用全局消息队列。 With that approach, if a child grabs a message that it's not suppose to have (ie, you would need to pass around a struct that contained some type of ID value), it would place the message back in the queue and attempt to read another message ... that's not quite as efficient speed-wise as the direct pipe approach, but it would allow you to write-back a message that was not designated for the current child without the interleaving complications that would take place with a global pipe or FIFO mechanism. 有了这种方法,如果一个孩子抓起一条消息,它不是想拥有(即,你可能需要通过围绕struct包含某种类型的ID值),这将使该消息放回队列,并尝试读取另一消息...这不像直接管道方法那样快速有效,但是它可以让你回写一个没有为当前孩子指定的消息,而没有使用全局管道发生的交错复杂性或FIFO机制。

Each byte of data written to a pipe will be read exactly once. 写入管道的每个数据字节将只读取一次。 It isn't duplicated to every process with the read end of the pipe open. 在管道的读取端打开的情况下,它不会复制到每个进程。

If you want the data duplicated to multiple destination processes, you have to explicitly duplicate the data. 如果要将数据复制到多个目标进程,则必须显式复制数据。 For example, you could have one "master" process that has a pipe to and from every "slave" process. 例如,您可以拥有一个“主”进程,该进程具有进出每个“从属”进程的管道。 When a slave wants to broadcast a message to the other slaves, it sends it to the master process, which loops around and writes it once to each pipe going to the other slaves. 当一个从站想要向其他从站广播一条消息时,它会将它发送给主进程,该进程循环并将其写入到每个管道中,然后写入其他从站。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM