《UNIX网络编程 卷1》 笔记: I/O复用 select函数
来源:互联网 发布:淘宝网毛呢女装中长款 编辑:程序博客网 时间:2024/06/18 11:58
上节我们实现的TCP回射客户程序有缺陷,代码如下:
void str_cli(FILE *fp, int sockfd) { char sendline[MAXLINE], recvline[MAXLINE]; while (Fgets(sendline, MAXLINE, fp) != NULL) { /*从标准输入读取一行文本*/ Writen(sockfd, sendline, strlen(sendline)); /*发送到服务器*/ if (Readline(sockfd, recvline, MAXLINE) == 0) /*从服务器读取一行文本*/ err_quit("str_cli: server terminated prematurely"); Fputs(recvline, stdout); /*输出到标准输出*/ } }缺陷在于它仅仅阻塞在标准输入上,只有先处理完标准输入之后才能处理套接字的输入,而不能同时处理两者的输入。有了select函数,我们就能同时监听两个描述符解决这个问题。参考select函数的用法,我们修改了str_cli函数,代码如下:
void str_cli(FILE *fp, int sockfd){int maxfdp1;fd_set rset;char sendline[MAXLINE], recvline[MAXLINE];FD_ZERO(&rset);for ( ; ; ) {FD_SET(fileno(fp), &rset);FD_SET(sockfd, &rset);maxfdp1 = max(fileno(fp), sockfd) + 1;Select(maxfdp1, &rset, NULL, NULL, NULL);if (FD_ISSET(sockfd, &rset)) { /*套接字输入*//*!!!!*/if (Readline(sockfd, recvline, MAXLINE) == 0)err_quit("str_cli: server terminated prematurely");Fputs(recvline, stdout);}if (FD_ISSET(fileno(fp), &rset)) { /*标准输入*//*!!!!*/if (Fgets(sendline, MAXLINE, fp) == NULL)return;Writen(sockfd, sendline, strlen(sendline));}}}很不幸的是,修改之后的代码是不正确的。理由如下:
1. readline函数使用的是自己的缓冲区,fgets函数使用的是标准I/O(stdio)缓冲区。而select函数判读描述符是否可读是从read系统调用的角度来看的,它并不知道readline函数和fgets函数使用了缓冲区。这样就会出现当数据已经在缓冲区时,而select函数并不知道的情况。所以使用标准I/O库的函数(fgets等)不能和select函数混合使用!!!
2. 在批量方式下(将标准输入重定向到一个输入文件,将标准输出重定向到一个输出文件),我们会发现输出文件总是小于输入文件。原因在于在标准输入中遇到EOF时,str_cli函数就会返回,客户进程调用exit函数退出,随后关闭套接字描述符时会同时关闭接收端和发送端,这样在管道中等待接收的数据就被丢弃了。
为了解决第一个问题,我们取消以文本行为中心的操作,不使用标准I/O缓冲区,而使用read函数,改为单个字节的操作方式。
为了解决第二个问题,我们使用shutdown函数,执行TCP的半关闭操作,使得TCP连接的发送端被关闭的同时接收端仍能接收数据。
修改后的代码如下:
void str_cli(FILE *fp, int sockfd){int maxfdp1, stdineof;fd_set rset;char buf[MAXLINE];int n;stdineof = 0; /*标准输入遇到EOF时置1*/FD_ZERO(&rset);for ( ; ; ) {if (stdineof == 0)FD_SET(fileno(fp), &rset);FD_SET(sockfd, &rset);maxfdp1 = max(fileno(fp), sockfd) + 1;Select(maxfdp1, &rset, NULL, NULL, NULL);if (FD_ISSET(sockfd, &rset)) { /*套接字可读*//*当我们在套接字遇到EOF时,如果我们已在标准输入遇到EOF,那么服务器就是正常的终止,函数返回。否则服务器就是异常的终止。*/if ((n = Read(sockfd, buf, MAXLINE)) == 0) {if (stdineof == 1)return;elseerr_quit("str_cli: server terminated prematurely");}Write(fileno(stdout), buf, n);}if (FD_ISSET(fileno(fp), &rset)) { /*标准输入可读*/if ((n = Read(fileno(fp), buf, MAXLINE)) == 0) { /*标准输入遇到EOF*/stdineof = 1;Shutdown(sockfd, SHUT_WR); /*关闭连接的发送端*/FD_CLR(fileno(fp), &rset);continue;}Writen(sockfd, buf, n);}}}
阅读全文
0 0
- 《UNIX网络编程 卷1》 笔记: I/O复用 select函数
- 《UNIX网络编程 卷1》 笔记: 高级I/O函数
- 《UNIX网络编程卷1》读书笔记--第六章I/O复用:select和poll函数
- Unix网络编程学习笔记之第6章 I/O复用:select和poll函数
- 【unix网络编程第三版】阅读笔记(五):I/O复用:select和poll函数
- UNIX网络编程卷一:第六章 I/O 复用 select, poll
- 【Unix 网络编程】服务器网络编程模型——I/O复用:select 函数
- select和poll函数《UNIX网络编程卷一》笔记
- UNIX网络编程卷一 笔记 第六章 第6章 I/O复用
- 《UNIX网络编程 卷1》 笔记: 非阻塞式I/O
- 《UNIX网络编程 卷1》 笔记: 信号驱动式I/O
- UNIX网络编程笔记(6):I/O复用之select函数
- UNIX 网络编程 卷一:套接字联网API(第3版) 读书笔记(6) 第六章 I/O复用:select和poll函数
- UNIX网络编程--I/O复用:select函数和poll函数讲解(六)
- UNIX网络编程--I/O复用:select函数和poll函数讲解(六)
- Unix网络编程代码 第6章 I/O复用:select、poll和epoll函数
- UNIX网络编程——I/O复用:select和poll函数
- UNIX网络编程——I/O复用:select和poll函数
- JAVA_基础之面向对象的概念
- GridView实现checkbox效果(单选效果)
- C语言函数的参数能允许几个
- Miktex 添加宏包
- pandas replace函数使用小结
- 《UNIX网络编程 卷1》 笔记: I/O复用 select函数
- TabLayout踩坑之IllegalArgumentException: Tab belongs to a different TabLayout.
- IMWeb提升营Day1 | 训练题6: 旋转数组的最小数字
- Access restriction: The type JPEGImageEncoder is not accessible due to restriction
- [LeetCode]AddTwoNumbers
- 信息安全概论
- 实训周五
- objdump选项说明
- 面具