3

我正在用 C++ 编写一个分叉聊天服务器,每个传入的客户端都是它自己的进程。服务器-客户端交互是通过使用处理消息队列和 IPC 的 ZeroMQ 套接字的普通套接字完成的。基本问题是,当服务器分叉以适应新客户端时,客户端的进程具有上下文的副本(这就是 fork 所做的,对吗?),因此当它将套接字与上下文绑定时,其他客户端都没有知道套接字。长话短说:如何让每个客户端线程具有相同的上下文,以便它们可以通过 ZeroMQ 相互交谈?

我已经研究了在进程之间共享上下文的各种方法,到目前为止,我只找到了这个。问题在于 1)它使用了一个线程池,据我从那里写的内容来看,只创建了 5 个线程;该服务器需要支持至少 256 个,因此至少有那么多线程,并且 2)它使用 ZeroMQ 来与客户端通信和后端任务;我仅限于将 ZeroMQ 用于后端。

我查看了 ZeroMQ 邮件列表,其中一条消息说 fork() 与 ZeroMQ 的工作方式是正交的。这是否意味着我不能在分叉的子进程之间共享我的上下文?如果是这种情况,我如何在多个进程之间共享上下文,同时牢记支持至少 256 个客户端并仅将 ZeroMQ 用于后端的要求?

编辑:清除线程/进程混淆。对于那个很抱歉。

EDIT2:我也喜欢分叉而不是线程的原因是我习惯于有一个主进程接受传入的套接字连接然后分叉,将新的套接字提供给子进程。我不知道如何以线程方式做到这一点(不是很好实践,但不是完全超出我的范围)

EDIT3:所以,开始用线程重写它。我想这是唯一的方法吗?

EDIT4:为了进一步澄清,到服务器的传入连接可以是 TCP 或 UDP,当客户端连接时我必须处理它是哪种类型,所以我不能使用 ZeroMQ 套接字来监听。

4

1 回答 1

6

上下文共享

在链接的示例代码中共享 ZMQ 上下文的原因是 server( main()) 使用inproc套接字与 workers( worker_routine()) 通信。Inproc除非它们是从相同的 ZMQ 上下文创建的,否则套接字无法相互通信,即使它们位于同一进程中。在您的情况下,我认为没有必要共享它,因为不inproc应该使用任何套接字。因此,您的代码可能如下所示:

void *worker_routine (void *arg)
{
    // zmq::context_t *context = (zmq::context_t *) arg;    // it's not necessary for now.
    zmq::context_t context(1);    // it's just fine to create a new context

    zmq::socket_t socket (context, ZMQ_REP);
    // socket.connect ("inproc://workers");    // inproc socket is useless here.
    socket.connect("ipc:///tmp/workers");    // need some sockets who can cross process.

    // handling code omitted.
}

int main ()
{
    //  omitted...

    // workers.bind ("inproc://workers");    // inproc socket is useless here.
    workers.bind("ipc:///tmp/workers");

    //  Launch pool of worker processes
    for (int i = 0; i < 5; ++i) {
        if (fork() == 0) {
            // worker process runs here
            worker_routine(NULL);
            return 0;
        }
    }
    //  Connect work processes to client process via a queue
    zmq::proxy (clients, workers, NULL);
    return 0;
}

每个请求的处理过程

现在谈谈您的要求,每个请求一个进程。最后一个示例代码只是为了说明 的用法zmq::proxy,提供它是为了简化具有 ROUTER-DEALER 模式的服务器代码。但它不能满足你的要求。因此,您必须手动实现它。它只是看起来像另一个例子。不同之处在于您需要fork()在前端套接字可读时调用并将while循环放入子进程中。

if (items[0].revents & ZMQ_POLLIN) {
    if (fork() == 0) {
        // sub process runs here
        while (1) {
            // forward frames here
        }
        // sub process ends here
        return 0;
    }
}

建议

最后,我不得不说,除非你的场景真的很特别,否则为一个请求创建一个流程太繁重了。请使用线程,或考虑异步 IO,如zmq::poll.

于 2013-04-20T03:43:31.787 回答