待你进一步完善了前面的 echo 服务器后,也处理了对端发送而来的 RST 段而导致的错误。现在,我们遇到了一个新问题,即客户端在关闭退出后,服务器也关闭退出了。
1. 让服务器永远运行
解决的办法很简单,我们将 server 改进为下面这样:
void server_routine() { // bind, listen while(1) { sockfd = accept(listenfd); doServer(sockfd); close(sockfd); } }
不过,这样改进有一个缺点,即服务器同时只能为一个客户端服务。如果在执行一个耗时的 doServer 的时候,又有客户端连接进来,那么这个后来客户端将迟迟得不到服务器的响应。
我们将上面这种服务器称为迭代服务器,如果 doServer 执行的任务非常简单,比如只是发送一下时间给对方,这种设计足够满足需求。如果是其它的耗时任务,这种设计则会让用户体验非常差。有各种各样的方案可以让我们挑选,不过,先来让我们用多进程的方案来解决它。
2. 基于多进程的并发服务器
基于多进程的并发服务器非常简单,我们只要将任务交给子进程处理就行了,而父进程只专注于接受新的连接,它的伪代码如下:
void server_routine() { // bind, listen while(1) { sockfd = accept(listenfd); // 让子进程去处理 IO pid = fork(); if (pid == 0) { // child close(listenfd); doServer(sockfd); close(sockfd); exit(0); } // father; close(sockfd); } close(listenfd) }
3. 程序代码
代码托管在 gitos 上,请使用下面的命令获取:
git clone https://git.oschina.net/ivan_allen/unp.git
如果你已经 clone 过这个代码了,请使用 git pull 更新一下。本文所使用的程序路径是:
unp/program/echo/concurrent_server
4. 程序运行
- 服务器在 sun 主机上运行
$ ./echo -s -h sun
- 分别在 moon 和 flower 主机上启动两个客户端:
$ ./echo -h sun
图1 两个客户端都可以连接上服务器
- 接下来,我按下 CTRL + D 关掉了 flower 主机上的客户端,然后又重新启动了一个,发现可以正常连接。但是,服务端这边貌似出了点问题!见图2.
图2 服务器端情况
可以看到图 2 中,多了一个僵小鱼,清理它对你来说不是什么难事,如果你学完了《Linux 环境编程》的话。如果你对此知识点有遗忘,请阅读这里的三篇文章:《wait 大战僵尸》,《wait大变身之waitpid》 和 《标准信号及其不可靠性》。相信你一定能解决此问题。
提示一下,如果你使用信号机制解决了问题后,可能会遇到新的坑。
最后,不管你有没有解决上面的问题,下一篇笔记我都得写^_^.
5. 总结
- 迭代服务器及其使用场景
- 掌握基本的多进程并发服务器模型
练习 1:将多进程服务器改为多线程模型(thread per connection, TPC).
练习 2:尝试测一下你的多进程服务器的并发数,也就是同时能够连接多少客户端。
思考:在 fork 出子进程后,子进程执行了 close(listenfd),为什么要这样做?说说你的理解。
最后,欢迎入群讨论:610441700,加群记得注明来意,否则会被拒绝。