Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1024170
  • 博文数量: 242
  • 博客积分: 10209
  • 博客等级: 上将
  • 技术积分: 3028
  • 用 户 组: 普通用户
  • 注册时间: 2008-03-12 09:27
文章分类

全部博文(242)

文章存档

2014年(1)

2013年(1)

2010年(51)

2009年(65)

2008年(124)

我的朋友

分类: LINUX

2010-04-29 15:44:38

UNP第五章:讲述了通过fork子进程来构建并发服务器的方法和注意的问题。
UNP 5.7 5.8 5.9 5.10节

1.fork子进程时,必须捕获SIGCHLD信号,若不捕获该信号,子进程退出时会产生僵尸进程。

2.捕获信号时,必须处理被中断的系统调用。比如按1中所说的,父进程捕获SIGCHLD信号,同时也会造成 accept调用返回错误,这时需要判断,若是该系统调用的确是因为信号被中断的,那就得重启该系统调用。

3.捕获SIGCHID信号的处理函数应该调用waitpid而不是wait,因为可能会出现多个客户端同时发送FIN的情况,为了保证服务器端所有相应的子进程都在终止时被父进程回收,需要以WNOHANG为参数循环调用waitpid;若是循环调用wait,可能会出现服务器阻塞于wait的情况。

4.还有一些情形也会造成accept调用返回错误,如5.11节中客户端向已处于ESTABLISHED状态的连接发送复位请求RST,各种实现对该情形的处理不同:Berkeley的实现直接在内核处理,服务器进程根本看不到;SVR4中accept返回EPROTO错误;POSIX要求返回ECONNABORTED错误。针对ECONNABORTED也需要重启accept系统调用。


5.我们在第五章的程序还有一个问题:若当客户端阻塞在fgets上时(等待用户输入),服务器端发起主动关闭(比如服务器进程终止或者崩溃),客户端的内核中的tcp模块收到了fin,却无法通知客户端进程(因为客户端进程正阻塞在fgets上),接下来若有客户输入文本并按下回车,客户端发送数据。然后,在服务器一端,收到数据便返回RST(因为服务器进程已经崩溃了); 在客户端,用户调用readline,若readline调用时还未收到RST,由于前面的fin,readline调用read时就可以读到EOF进而终止, 若readline调用时已经将收到了RST,readline就返回ECONNRESET错误。  这里需要改进的就是希望客户端在收到fin时,立即被告知,而不是阻塞在fgets上。这可用select或poll来实现。

UNP第六章:讲述了使用select、poll等函数进行I/O复用。
1.使用select函数解决了上面第五章5中提到的问题,这就是在6.4节中所示的程序。 但是,这里的程序又有了另一个问题,那就是当我们把客户端从标准输入读并向标准输出写的动作改为批量的读入写出(也即从文件读和向文件写)时,却发现输出文件总是小于输入文件。究其原因,当我们读到文件末尾时,fgets读到EOF,便直接return导致主函数退出,其实这时虽然数据从客户端发送完了,但是服务器端的数据并没有全部到达客户端,所以客户端输出文件总是小于输入文件了。客户端退出时向服务器发送FIN,服务器收到FIN后并不会停止发送数据,服务器是发送完数据后才发送自己的FIN给客户端,服务器发的数据 客户端内核是可以收到的,但客户端进程已经退出,无法处理这些数据(即无法将这些数据写入文件)。至于该问题的解决办法,UNP6.7节中说是引入shutdown函数,其实我觉得并不是引入shutdown函数才解决了该问题,而是我们设置一个标志stdineof,当读入文件的末尾时,置该标志为1,并不退出主程序,在以后select时不再检查文件描述符而只检查套接口了。这样套接口能继续读到服务器发来的数据,直到服务器发来FIN。

2.上述客户端程序还有一个问题:我们在客户端中使用了stdio的fgets来每次获取文件的一行,但是我们应该知道,stdio中的函数是自带缓冲区的,调用一次fgets可能会导致其从文件获取很多行放入缓冲区中,但只返回一行到fgets的第一个参数buf中,但select函数是不知道stdio函数库中函数自带的缓冲区的,它只是从read系统调用的角度之处是否有数据可读。这会导致什么问题呢? 书上没说清楚,只是说很可能出错,但我实在想不出可能会出现的错误。至于解决办法就是使用read、write、writen等不自带缓冲区的函数。

3.UNP6.8节中给出了一个单进程的使用select处理客户端并发访问的服务器,它是一个使用select的出色例子。
         a.但它存在的问题是可能会因为一个用户的行为(不发出换行符)而阻塞于单个用户,解决方法是使用非阻塞IO或者在IO操作上限定时间。 
         b.该服务器还有一个潜在的问题,若有一个客户端建立连接后,服务器还没来得及调用accept时,客户端接着发送了一个RST,在Berkeley的实现上,服务器端内核直接从已完成队列中删除该连接而不通知服务器进程,这样当服务器进程调用accept时,如无其他已完成连接,则服务器阻塞在accept调用上,无法处理任何其他已就绪的描述字。(在其他实现上,服务器端内核会向服务器进程返回ECONNABORTED或者EPROTO错误,从而服务器不会阻塞在accept上)这种情况在16.6节中有所讲解。
 
         c.该单进程服务器存在的最大的问题是客户端若发送一个RST,readline就会在调用read时返回一个错误,接着服务器进程就退出了,参见第六章的习题7,这个问题在使用poll重写该服务器时得到了解决。


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