我目前正在Linux环境中学习使用C的套接字编程。作为一个项目,我试图编写一个基本的聊天服务器和客户端。
目的是让服务器为每个连接的客户端分叉一个进程。
我遇到的问题是在一个孩子中读取数据并将其写入所有连接的客户端。
我试图通过循环调用select等待数据到达套接字或读取管道末端的子进程来实现此目的。如果它到达套接字的想法是它写入管道的写端,导致select返回管道的读端,准备好读取。
由于此管道在所有子项之间共享,因此每个子项都应读取管道上的数据。这不起作用,因为它看起来不是每个子进程同时读取的数据管道以及在调用中“错过”数据块的子进程。
以下是子进程中执行此操作的代码:
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));
}
}
}
我认为我目前使用的方法根本不起作用。是否可以通过IPC将从客户端收到的消息写入所有其他连接的客户端?
由于
答案 0 :(得分:2)
为了解决孩子从管道中读取的数据超出应有的数据(并且反过来让另一个孩子“试图从空管道中读取”),你应该考虑使用任何一个POSIX消息父级和单个子进程之间的队列或单个管道进程,而不是单个全局管道在父进程和子进程之间进行通信。就目前而言,当服务器写入管道与其子进程通信时,由于操作系统对进程的调度是不确定的,因此无法确切地控制在任何给定时间从管道中读取哪个子进程。 。换句话说,如果没有某种类型的同步机制或读/写障碍,如果服务器写入管道,则代码中没有任何内容阻止一个孩子“跳过”读取,而第二个孩子不进行双读留下另一个应该从服务器上获取广播数据的孩子,因此被阻止了。
解决这个问题的一个简单方法可能是在父级和单个子级之间共享一个私有管道。因此,在服务器中,子进程可以从客户端读取,将该数据发送回父进程,然后父进程可以使用为所有子进程累积的整个管道描述符列表,回写每个子进程广播消息然后被发送回每个客户端。没有孩子会被“饥饿”的数据,因为没有可能被另一个子进程双重阅读。每个管道上只有一个读写器,通信是确定性的。
如果您不想为服务器的父进程中的每个子进程处理多个管道,则可以使用POSIX消息队列(在mqueue.h
中找到)使用全局消息队列。通过这种方法,如果一个孩子抓住了一条消息,它不会被认为(即,你需要传递包含某种类型ID值的struct
),它会将消息放回队列中并且尝试读取另一条消息......这种方式与直接管道方法的速度不同,但它可以让您回写未指定给当前子节点的消息,而不会出现与之相关的交错复杂情况全局管道或FIFO机制。
答案 1 :(得分:2)
写入管道的每个数据字节将只读取一次。在管道的读取端打开的情况下,不会复制到每个进程。
如果要将数据复制到多个目标进程,则必须显式复制数据。例如,您可以拥有一个“主”进程,该进程具有进出每个“从属”进程的管道。当一个从站想要向其他从站广播一条消息时,它会将它发送给主进程,主进程循环并将其写入到每个去往其他从站的管道。