上一讲中我们正确处理了僵尸子进程,使得这个简单的服务器更加健壮。不幸的是,这个程序仍然有问题。想象一下,如果一个客户正在和一个服务器子进程连接建立完毕正在通话,而服务器子进程意外终止(比如kill),服务器TCP向客户TCP发送一个FIN,但客户端正在调用fgets函数等待用户输入字符而得不到这个FIN,直到套接字读为止(可能过了很长时间)。

1、来看看问题是什么

首先启动服务器与客户,建立连接:

客户输入nihao后服务器正确回射,然后kill掉服务器子进程后客户输入and...,输出错误:

服务器端重启(上一讲中解决的问题)。

这个过程发生了什么?

(1)当kill掉服务器子进程后,服务器父进程正确处理了SIGCHLD信号。服务器也向客户发送一个FIN,客户回应一个ACK,TCP连接终止的前半部分完成;

(2)客户上没有发生任何特殊的事,客户TCP接收到服务器的FIN后回应一个ACK,但问题是客户正阻塞在fgets上,等待用户输入;

(3)在客户上再输入and...,导致错误发生:str_cli调用write,客户TCP接着把数据发送给服务器,但是客户FIN的接收并没有告知客户TCP服务器进程已经终止。当服务器TCP接收到客户的数据时,发送一个RST;

(4)但是客户进程看不到这个RST,因为调用write后直接调用readline,并且由于之前接收的FIN,导致readline直接返回0(表示EOF)。但客户没有预期收到EOF,于是出错;

(5)客户终止,所有打开的描述符关闭;

问题的根本在于,当FIN到达套接字时,客户正阻塞在fgets调用上。客户实际上在应对两个描述符:套接字和用户输入,它不能单纯阻塞在某个特定的源上。select函数可以解决这个问题。

2、select函数

有些进程需要一种预先告知内核的能力,使得内核一旦发现进程指定的一个或多个I/O条件就绪时,它就通知进程。这个能力就是I/O复用。

select函数允许进程指示内核等待多个事件中的任何一个发生,并只在有一个或多个事件发生或经历一段指定的时间后才唤醒它。

下面的例子指出select可以告知内核在什么条件下发生时返回:

  • 集合{1,4,5}中的任何描述符准备好读;
  • 集合{2,7}中的任何描述符准备好写;
  • 集合{1,4}中的任何描述符由异常条件待处理;
  • 已经过了12.8秒;
也就是说,调用select函数告知内核对哪些描述符(就读、写或异常条件)感兴趣以及等待多长时间。描述符不局限于套接字,任何描述符都可以使用select。
函数定义如下,包含在<sys/select.h>头文件中:
#include <sys/select.h>
#include <sys/time.h>
int select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set * exceptset,const struct timeval *timeout);

函数如果有描述符就绪就返回数量,超时返回0,出错返回-1。

参数含义如下:
maxfdp1:指定待测试描述符的个数,通常是待测试的最大描述符加1;
中间三个参数指定内核测试读、写和异常条件的描述符,这里设置成NULL即可;
timeval:告知内核等待所指定的时间,有三种情况:
(1)永远等待:设置为NULL;
(2)等待固定时间:在有一个描述符就绪后返回,但不超过参数所指定的时间;
(3)根本不等待:检查描述符后立刻返回;
3、重写客户端中的str_cli函数
客户的套接字上的三个条件:
(1)如果对端TCP发送数据,那么该套接字变为可读,并且read返回一个大于0的值(即读入数据的字节数);
(2)如果对端TCP发送一个FIN,那么该套接字变为可读,read返回0(EOF);
(3)如果对端TCP发送一个RST(对端主机崩溃并重启),那么该套接字变为可读,并且read返回-1,errno中有错误码;
下面是修改后的str_cli函数:
void str_cli(FILE *fp,int sockfd)
{int maxfdp1;fd_set rset;char sendline[MAXLINE],recvline[MAXLINE];FD_ZERO(&rset);int n;for(;;){FD_SET(fileno(fp),&rset);FD_SET(sockfd,&rset);maxfdp1=max(fileno(fp),sockfd)+1;if((n=select(maxfdp1,&rset,NULL,NULL,NULL))==0){printf("select timeout\n");return;}else if(n<0){printf("select error\n");return;}if(FD_ISSET(sockfd,&rset)){if(readline(sockfd,recvline,MAXLINE)==0){printf("str_cli:server terminated prematurely\n");return;}fputs(recvline,stdout);}if(FD_ISSET(fileno(fp),&rset)){if(fgets(sendline,MAXLINE,fp)==NULL)return;write(sockfd,sendline,strlen(sendline));}}
}

函数首先检查套接字,如果套接字是可读的,那么就用readline读入回射文本并输出;如果标准输入是可读的,就调用fgets读入一行,然后写入套接字。

运行结果:
(1)服务器进程终止后客户端直接终止:
(2)服务器端:
可以看到,程序运行良好。

UNIX网络编程笔记(6):I/O复用之select函数相关推荐

  1. 进程fork和exec ---Unix网络编程笔记

    进程fork和exec ---Unix网络编程笔记 fork 一次调用,两次返回 fork的两个典型用法 最简单的并发服务器---fork子进程 exec -Unix网络编程笔记) fork #inc ...

  2. UNIX网络编程笔记(2):一个简单的时间获取程序

    这一讲通过一个简单的时间获取程序简单介绍套接字编程. 1.套接字API 1.1.套接字地址结构 上一讲中介绍了TCP的一些内容,知道了一个套接字对唯一标识了网络中的一个TCP连接,而一个套接字标识了一 ...

  3. UNIX网络编程笔记(7):回射程序的UDP版本

    1.UDP简介 UDP是一个简单的传输层协议,应用进程往一个UDP套接字写入数据,随后被封装到一个UDP数据报,进而又被封装到一个IP数据报,然后发送到目的地.UDP不保证UDP数据报会最终到达目的地 ...

  4. UNIX网络编程笔记(3):简单的并发服务器

    上一讲中的简单时间获取服务器是一个迭代服务器,对于获取时间来说够用了.迭代服务器有这样的特点:同一时间只能给一个客户服务.也就是说,如果某一时刻服务器与某个客户正在连接,其它客户必须等到上一个客户与服 ...

  5. UNIX网络编程笔记(1):TCP简介

    1.简介 TCP(Transmission Control Protocol),即传输控制协议,是一种面向连接的.可靠的.基于字节流的传输层通信协议.TCP协议有以下几个特点: TCP提供客户与服务器 ...

  6. UNIX网络编程笔记(4):简单的回射程序

    上一讲中我们通过调用fork函数实现了一个简单的并发时间获取服务器.这是一个简单的并发服务器框架,然而这里使用这个框架实现一个简单的回射服务器会出现一个问题,这个问题就是僵尸子进程. 1.回射程序 下 ...

  7. UNIX网络编程--读书笔记

    会集中这段时间写UNIX网络编程这本书的读书笔记,准备读三本,这一系类的文章会不断更新,一直会持续一个月多,每篇的前半部分是书中讲述的内容,每篇文章的后半部分是自己的心得体会,文章中的红色内容是很重要 ...

  8. Unix网络编程卷一第三章笔记

    前言 这篇文章主要是Unix网络编程卷一第三章的个人笔记 1.POSIX 规范的三个字段 sin_family sin_addr sin_port 2.IPV4 套接字结构 五个套接字结构 IPV4( ...

  9. UNIX网络编程学习笔记(代码超详细解析)(持续更新)

    1. 其他函数准备 1. TCP 回射服务器程序: str_echo 函数 #include "unp.h"void str_echo(int sockfd) {ssize_t n ...

最新文章

  1. 机器学习的5个成功案例
  2. python 多维 条件获取
  3. CF 287(div 2) B Amr and Pins
  4. linux 比较新的设备树 详解 device tree
  5. 页面静态化2 --- 使用PHP缓存机制来完成页面静态化(上)(ob_flush和flush函数区别用法)...
  6. Python Json存储与读取
  7. flask的第一个hello word 程序
  8. 初试Ajax.Net !
  9. android中bmob云存储,我在将Bmob作为云进行数据存储!但不知道如何把img内的文件上传到bmob云中...
  10. 项目疑难杂症记录(二):焦点移动不了
  11. 架构思维成长系列教程(十)- 电商搜索引擎架构设计
  12. Premiere Pro Guru: 3D Titling for Video Editors Premiere Pro 大师教程之 3D字幕条制作教程 Lynda课程中文字幕
  13. 洛谷P1322 logo语言
  14. 01.CCNA 200-301 题库_1-50
  15. 基于SSM框架和JSP的房屋租赁、合同签订系统
  16. 现代 opengl 的驱动安装
  17. Lessonnbsp;6nbsp;Percynbsp;Bu…
  18. 华为路由交换由浅入深系列(二):静态路由、浮动路由、默认路由配置以及华为路由协议优先级总结
  19. 像差分析(更新。。。)
  20. 阿里云网站备案-注销备案的办法

热门文章

  1. Python补充06 Python之道
  2. SQL SERVER作业的Schedules浅析
  3. windows 7资源管理器崩溃解决方法
  4. Web Service 缓存
  5. linux如何解除密码,如何在Linux下解除PDF文件的密码?
  6. tf 如何进行svd_Tensorflow快餐教程(6) - 矩阵分解
  7. python下载显示文件丢失_Microsoft.PythonTools.resources.dll
  8. 巡回沙龙_美浮特全国巡回沙龙第一期结束撒花!
  9. postman使用_postman如何使用集合断言?
  10. OJ RuntimeError常见原因