今天我们一起来聊聊多进程实现与多个客户端进行通信。
如果是在while中循环accept, 然后循环处理事情, 此时, 这种服务是迭代服务, 只能逐一处理客户端的请求, 后一个请求必须等前一个请求处理完毕, 无法并发处理, 真是急死人。 要实现并发, 我们可以考虑多线程, 也可以考虑多进程, 本文来说说后者。 在我们的多进程服务器模型中, 我们用父进程来处理连接(监听 socket ), 用fork子进程的方法来处理通信(通信socket), 各司其职, 美哉。
一旦涉及到fork, 就必须注意僵尸进程的处理, 所以, 我们要用waitpid进行收尸, 这一点, 我们已经说过了。 另外, 要注意, 父子进程共享socket句柄的文件表(如果不理解的话, 建议看看APUE), 所以close socket的时候, 只是使引用计数减1, 并不是真正地直接关闭socket(减为0才是真正的关闭)。
废话少说, 直接上菜。
服务端程序为:
启动它。
客户端程序为:
我们开启一个客户端, 此时如下:
客户端信息为:
服务端信息为:
可以看到, 服务端16402子进程是与客户端通信的进程, 父进程16096是监听的父进程(主进程)。
另外再开启一个客户端(不要关闭旧的客户端), 此时如下:
新客户端信息为:
服务端信息为:
可以看到, 父进程16096新开了一个子进程16497来与新的客户端进行通信。
我们关闭第一个客户端, 然后看到服务端为:
我们再关闭第二个客户端, 然后看到服务端为:
显然, 客户端退出后, 发FIN包, 服务端子进程的recv函数就为0, 退出子进程的while循环了, 因此, 对应的子进程就over了, 而且不会留下僵尸进程(有waitpid)。 而且, 我们可以看到, 负责连接管理(accept)的父进程(主进程)依然安然无恙, 优哉游哉地等待下一个客户端连接。
在这里, 我们可以看到, 这个服务器是并发的, 而不是迭代的。 什么意思呢? 你看, 即使子进程处理业务需要很久很久, 那么上述服务依然能并发地响应n个几乎同时到达的客户端, 此时,父进程开启n个子进程, 并发地工作, 并发地与客户端进行通信, 而且还互不干扰, 大大提升了服务满意度。
注:需要C/C++ Linux服务器开发学习资料私信“资料”(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享