UNIX网络编程笔记(6):I/O复用之select函数
上一讲中我们正确处理了僵尸子进程,使得这个简单的服务器更加健壮。不幸的是,这个程序仍然有问题。想象一下,如果一个客户正在和一个服务器子进程连接建立完毕正在通话,而服务器子进程意外终止(比如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秒;
#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。
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读入一行,然后写入套接字。
UNIX网络编程笔记(6):I/O复用之select函数相关推荐
- 进程fork和exec ---Unix网络编程笔记
进程fork和exec ---Unix网络编程笔记 fork 一次调用,两次返回 fork的两个典型用法 最简单的并发服务器---fork子进程 exec -Unix网络编程笔记) fork #inc ...
- UNIX网络编程笔记(2):一个简单的时间获取程序
这一讲通过一个简单的时间获取程序简单介绍套接字编程. 1.套接字API 1.1.套接字地址结构 上一讲中介绍了TCP的一些内容,知道了一个套接字对唯一标识了网络中的一个TCP连接,而一个套接字标识了一 ...
- UNIX网络编程笔记(7):回射程序的UDP版本
1.UDP简介 UDP是一个简单的传输层协议,应用进程往一个UDP套接字写入数据,随后被封装到一个UDP数据报,进而又被封装到一个IP数据报,然后发送到目的地.UDP不保证UDP数据报会最终到达目的地 ...
- UNIX网络编程笔记(3):简单的并发服务器
上一讲中的简单时间获取服务器是一个迭代服务器,对于获取时间来说够用了.迭代服务器有这样的特点:同一时间只能给一个客户服务.也就是说,如果某一时刻服务器与某个客户正在连接,其它客户必须等到上一个客户与服 ...
- UNIX网络编程笔记(1):TCP简介
1.简介 TCP(Transmission Control Protocol),即传输控制协议,是一种面向连接的.可靠的.基于字节流的传输层通信协议.TCP协议有以下几个特点: TCP提供客户与服务器 ...
- UNIX网络编程笔记(4):简单的回射程序
上一讲中我们通过调用fork函数实现了一个简单的并发时间获取服务器.这是一个简单的并发服务器框架,然而这里使用这个框架实现一个简单的回射服务器会出现一个问题,这个问题就是僵尸子进程. 1.回射程序 下 ...
- UNIX网络编程--读书笔记
会集中这段时间写UNIX网络编程这本书的读书笔记,准备读三本,这一系类的文章会不断更新,一直会持续一个月多,每篇的前半部分是书中讲述的内容,每篇文章的后半部分是自己的心得体会,文章中的红色内容是很重要 ...
- Unix网络编程卷一第三章笔记
前言 这篇文章主要是Unix网络编程卷一第三章的个人笔记 1.POSIX 规范的三个字段 sin_family sin_addr sin_port 2.IPV4 套接字结构 五个套接字结构 IPV4( ...
- UNIX网络编程学习笔记(代码超详细解析)(持续更新)
1. 其他函数准备 1. TCP 回射服务器程序: str_echo 函数 #include "unp.h"void str_echo(int sockfd) {ssize_t n ...
最新文章
- 机器学习的5个成功案例
- python 多维 条件获取
- CF 287(div 2) B Amr and Pins
- linux 比较新的设备树 详解 device tree
- 页面静态化2 --- 使用PHP缓存机制来完成页面静态化(上)(ob_flush和flush函数区别用法)...
- Python Json存储与读取
- flask的第一个hello word 程序
- 初试Ajax.Net !
- android中bmob云存储,我在将Bmob作为云进行数据存储!但不知道如何把img内的文件上传到bmob云中...
- 项目疑难杂症记录(二):焦点移动不了
- 架构思维成长系列教程(十)- 电商搜索引擎架构设计
- Premiere Pro Guru: 3D Titling for Video Editors Premiere Pro 大师教程之 3D字幕条制作教程 Lynda课程中文字幕
- 洛谷P1322 logo语言
- 01.CCNA 200-301 题库_1-50
- 基于SSM框架和JSP的房屋租赁、合同签订系统
- 现代 opengl 的驱动安装
- Lessonnbsp;6nbsp;Percynbsp;Bu…
- 华为路由交换由浅入深系列(二):静态路由、浮动路由、默认路由配置以及华为路由协议优先级总结
- 像差分析(更新。。。)
- 阿里云网站备案-注销备案的办法
热门文章
- Python补充06 Python之道
- SQL SERVER作业的Schedules浅析
- windows 7资源管理器崩溃解决方法
- Web Service 缓存
- linux如何解除密码,如何在Linux下解除PDF文件的密码?
- tf 如何进行svd_Tensorflow快餐教程(6) - 矩阵分解
- python下载显示文件丢失_Microsoft.PythonTools.resources.dll
- 巡回沙龙_美浮特全国巡回沙龙第一期结束撒花!
- postman使用_postman如何使用集合断言?
- OJ RuntimeError常见原因