Chinaunix首页 | 论坛 | 博客
  • 博客访问: 541963
  • 博文数量: 129
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1888
  • 用 户 组: 普通用户
  • 注册时间: 2013-06-20 11:09
文章分类

全部博文(129)

文章存档

2016年(1)

2015年(5)

2014年(64)

2013年(59)

我的朋友

分类: LINUX

2013-09-05 16:06:55

只有一个进程的服务器是最简单的实现,被称为“迭代服务器”,但是一般的我们并不希望整个服务器被单个用户长期占用,而是希望同时服务多个用户,因此就需要并发服务器

UNIX中编写并发服务器程序最简单的方法就是fork一个子进程来服务每个客户

下面提供了并发服务器程序的框架:
 

  1. pid_t pid;
  2. int listenfd, connfd;
  3.                                  
  4. listenfd = socket ( ... );
  5. bind (listenfd, ... );
  6. listen (listenfd, LISTENQ);
  7.                                  
  8. for ( ; ; )
  9. {
  10.     connfd = accept (listenfd, ... );
  11.                                          
  12.     if ( (pid = fork()) == 0 )
  13.     {
  14.         close (listenfd);
  15.         doit (connfd);
  16.         close (connfd);
  17.         exit (0);
  18.     }
  19.     close (connfd);
  20. }

      当一个连接建立时,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连接终止序列的发生,导致连接一直打开着。

阅读(980) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~