分类: LINUX
2013-09-05 16:06:55
只有一个进程的服务器是最简单的实现,被称为“迭代服务器”,但是一般的我们并不希望整个服务器被单个用户长期占用,而是希望同时服务多个用户,因此就需要并发服务器
UNIX中编写并发服务器程序最简单的方法就是fork一个子进程来服务每个客户
下面提供了并发服务器程序的框架:
当一个连接建立时,accept 返回,服务器接着调用 fork,然后由子进程服务客户(通过已连接套接字 connfd),父进程则等待另一个连接(通过监听套接字 listenfd),既然新的客户由子进程提供服务,父进程就关闭已连接套接字。
上述代码中的 doit 函数即是我们假定的客户所需的所有操作,当该函数返回时,我们调用close关闭了已连接套接字。这一点并非必需,事实上,在调用 exit 函数时,系统会自动进行进程终止处理,其中就包括所有内核打开的描述符的关闭工作,因此此时是否显式调用 close 函数并没有什么影响。
需要注意的是,父进程 close connfd 并不会终止与客户的连接,因为每个文件或套接字都有一个引用计数,引用计数在文件表项中维护,他是当前打开着的引用该文件或套接字的描述符的个数,每次调用 close 函数,会将相应的描述符的引用计数减一,只有在引用计数被减为零时才会关闭该文件或套接字,fork 后由于子进程复制了父进程的全部描述符,因此与这两个套接口相关联的文件表项各自的访问计数均值为2.因此父进程进行对 connfd 的 close 并不会造成子进程中的连接被关闭。该套接口真正的清理和资源释放要等到其引用计数值达到0时才发生。
accept 函数返回已连接套接字,使用这个套接字就可以跨连接读写数据了,之后父进程 fork 出子进程,此时父进程与子进程共享该连接以及监听套接字,紧接着,父进程关闭 connfd,子进程关闭 listenfd,此时,只有子进程与客户维持连接状态,而父进程则可以使用监听套接字 listenfd 再次调用 accept 来处理下一个客户连接。
我们还得清楚,如果父进程对每个由accept返回的已连接套接口都不调用close,那么并发服务器中将会发生什么。首先,父进程最终将耗尽可用描述字,因为任何进程在任何时刻可拥有的打开着的描述字通常是有限制的。不过更重要的是,没有一个客户连接会被终止。当子进程关闭已连接套接口时,它的引用计数值将由2减为1且保持1,因为父进程永不关闭任何已连接套接口。这将妨碍TCP连接终止序列的发生,导致连接一直打开着。