非堵塞IO的读与写的回射客户端对比分析
来源:互联网 发布:夏老师c语言视频教学 编辑:程序博客网 时间:2024/04/29 20:34
一、非堵塞读与写分析
1、老版本1分析--- 基于tcp的堵塞io模型
是read和write,结合起来处理的io模型,因为如果服务器没有数据过来,那么read将会一直堵塞下去。因为堵塞将会导致程序的效率不够高效。
伪代码如下如实:
while(fgets(buf, maxline,stdin) != NULL){write(sockfd, buf, strlen(buf));read(sockfd, buf, maxline);}
分析堵塞条件:
1).write当中如果套接字发送缓冲区已满,write调用将会堵塞
2).read当中如果套接字接受缓冲区没有数据, 那么read将会一直堵塞下去,直到收到了来之服务器的数据。
2、老版本2分析---基于tcp的select中堵塞的io复用模型
在select来判断, 是否能读取数据, 可以排除老版本一当中的读取数据时候而造成的程序堵塞情况, 很大程度的提高了程序的执行效率。
伪代码如下所示:
for(;;){FD_SET(fileno(stdin), &rset);FD_SET(sockfd, &rset);select(maxfd + 1, &rset, NULL, NULL, NULL);if(FD_ISSET(fileno(stdin), &rset)){相关处理过程}if(FD_ISSET(sockfd, &rset)){相关处理过程 }}分析堵塞条件:
1).然而当调用write函数的时候,也会应为套接字发送缓冲区已满,而导致堵塞。
2).如果标准输出比网络慢, 那么进程也有可能堵塞与后续的write调用。
3、非堵塞IO的读与写在select下的复用模型
我们需要在应用层维护两个缓冲区:to缓冲区是标准输入到服务器去的数据, fr缓冲区是容纳来自服务器到标准输出的数据
to缓冲区:tooptr指向缓冲区未发送到服务器的数据的头子节。toiptr指向标准输入读入的数据可以存放的下一个字节。有(toiptr - tooptr)个数据可以被发送到套接字。
可以从标准输入读入的字节数&to[MAXLINE] - toiptr。当tooptr = toiptr的时候, 这两个指针就恢复到缓冲区开始的位置。
fr缓冲区与to缓冲区有相同的道理。
模型图如下所示:
详细实现代码如下所示:
dg_cli_nonblockio.c
#include "unp.h"void str_cli(FILE *fp, int sockfd){ int maxfdp1, flag, stdineof; ssize_t n, nwritten; fd_set wset, rset; char to[MAXLINE], fr[MAXLINE]; char *toiptr, *tooptr, *friptr, *froptr; //重点一:把标准输入、标准输出、连接服务器的套接字都设置成非堵塞条件 flag = Fcntl(sockfd, F_GETFL, 0); Fcntl(sockfd, F_SETFL, flag | O_NONBLOCK); flag = Fcntl(STDIN_FILENO, F_GETFL, 0); Fcntl(STDIN_FILENO, F_GETFL, flag | O_NONBLOCK); flag = Fcntl(STDOUT_FILENO, F_GETFL, 0); Fcntl(STDOUT_FILENO, F_SETFL, flag | O_NONBLOCK); toiptr = tooptr = to; friptr = froptr = fr; stdineof = 0; maxfdp1 = max(max(STDIN_FILENO, STDOUT_FILENO), sockfd); for(;;){ FD_ZERO(&rset); FD_ZERO(&wset); //标准输出未读到eof的时候, 并且to的空闲缓冲区至少在一个字节字节以上, 打开读描述符集中的标准输入位 if(stdineof == 0 && toiptr < &to[MAXLINE]){ FD_SET(STDIN_FILENO, &rset); /* read from stdin*/ } //当fr缓冲区有空闲时候, 则设置描述符集中的标准输出位 if(friptr < &fr[MAXLINE]){ FD_SET(sockfd, &rset); /*read from socket*/ } //tooptr一直小于等于toiptr的值,当不等于的时候,则表示to缓冲区有数据可以输出到socket if(tooptr != toiptr){ FD_SET(sockfd, &wset); /*write to socket*/ }// froptr一直小于等于friptr的值,当不等于的时候,则表示fr缓冲区有数据可以标准输出 if(froptr != friptr){ FD_SET(STDOUT_FILENO, &wset); /* write to stdout*/ } Select(maxfdp1, &rset, &wset, NULL, NULL); if(FD_ISSET(STDIN_FILENO, &rset)){ if((n = read(STDIN_FILENO, toiptr, &to[MAXLINE] - toiptr)) < 0){ if(errno != EWOULDBLOCK){ //当读取失败的时候有情况,第一非堵塞条件下没有成功读取数据, //errno置为EWOULDBLOCK,这种错误可以忽略。另一种情况就是读取错误fprintf(stdout, "read error from stdin\n"); } }else if(n == 0){//read返回为0,标准输入结束,设置stdineof标志。当to缓冲区不在有数据发送的时候 stdineof = 1;//那么发送FIN分节, 如果有数据发送,则推迟FIN分节的发送 fprintf(stderr, "%s, EOF on stdin\n", gf_time()); /* gf_time() is a function that we finished by ourselves.*/ if(toiptr == tooptr) Shutdown(sockfd, SHUT_WR); /* send FIN*/ }else{ toiptr += n; fprintf(stderr, "%s: read %d bytes from stdin\n", gf_time(), n); /*just read*/ FD_SET(sockfd, &wset); //try and write to sockfd below } } if(FD_ISSET(sockfd, &rset)){ if((n = read(sockfd, friptr, (size_t)(&fr[MAXLINE] - friptr))) < 0){ if(errno != EWOULDBLOCK) fprintf(stderr, "read error from sockfd\n");/*read error from socket*/ }else if(n == 0){ fprintf(stderr, "%s:EOF on sockfd\n", gf_time()); if(stdineof == 1) return; /*normal termination*/ }else{ fprintf(stderr, "%s: read %d bytes from sockfd\n", gf_time(), n); /*just read*/ friptr += n; FD_SET(STDOUT_FILENO, &wset); /*try and write below*/ } } //标准输出的条件是标准输出描述符号可写,并且fr缓冲区有数据可以输出 if(FD_ISSET(STDOUT_FILENO, &wset) && ( (n = friptr - froptr) > 0 )){ // 判断的同时并且也获得了fr缓冲区中为数据的bytes大小 if((nwritten = write(STDOUT_FILENO, friptr, n)) < 0){//输出失败时候,EWOULDBLOCK错误忽略 if(errno != EWOULDBLOCK) fprintf(stderr, "write error to stdout\n"); }else{ fprintf(stderr, "%s: wrote %d bytes to stdout\n", gf_time(), nwritten); friptr += nwritten; /**just written*/ //当输出指针(froptr)追上输入指针(friptr)的时候,指向fr缓冲区开始位置 if(friptr == froptr) friptr = froptr = fr; /*back to begining of buffer*/ } } if(FD_ISSET(sockfd, &wset) && ( (n = toiptr - tooptr) > 0)){ if((nwritten = write(sockfd, friptr, n)) < 0){ if(errno != EWOULDBLOCK) fprintf(stderr, "write error to socket\n"); }else{ fprintf(stderr, "%s: wrote %d bytes to socket\n", gf_time(), nwritten); toiptr += nwritten; /*just written*/ if(toiptr == tooptr){ toiptr = tooptr = to; /*back to begining of buffer*/ if(stdineof) Shutdown(sockfd, SHUT_WR); /* send FIN */ //当stdin已经关闭, 并且to缓冲的数据都已经发送到了服务器, 那么接发送FIN分节到服务器。 } } } }}详细分析如代码中的中文注释可得:
二、非堵塞IO问题分析
套接字默认状态是堵塞的。这也就说当我们发送出一个io请求的时候,如果不能及时的完成,那么进程将会进入睡眠状态,等待操作的进行。
(1)、输入操作,包括read, readv,recv,recvmsg,recvfrom五个函数。非堵塞的情况下如果不能被满足(对于tcp至少有一个数据可读,对于UDP套接字即有一个完整的数据报可读),相应的调用返回-1,并且errno将会返回EWOULDBLOCK错误
(2)、输出操作,包括write,writev、send、sendmsg、sendto五个函数。对与tcp而言,在堵塞套接字中,内核将会把数据从应用层缓冲区的数据复制到套接字缓冲区,如果套接字缓冲区空间不够哪儿内核将会进入休眠的状态,在非堵塞套接字中,如果套接字缓冲区的空间没有,那么将会立刻返回EWOULDBLOCK错误,若是有部分空间,则是返回该部分空间的字节数。对于UDP而言, UDP套接字不存在正真的缓冲区,内核直接将数据从应用层缓冲区换衣UDP首部和IP首部从协议栈往下传递
- 非堵塞IO的读与写的回射客户端对比分析
- c语言 设置堵塞和非堵塞io的方法
- 堵塞与非堵塞IO模式详解
- TCP非堵塞IO的connect连接处理模型
- 网络IO模型解析:同步IO和异步IO,阻塞IO和非阻塞IO的对比分析
- 网络IO模型解析:同步IO和异步IO,阻塞IO和非阻塞IO的对比分析
- nodejs中的阻塞与非堵塞的区别
- SQLServer的一次堵塞分析
- dubbo源码分析-客户端DubboInvoker调用服务端体会Netty的非阻塞IO使用
- (statistic)主成分分析数据的标准化与非标准化的对比分析
- java 的nio与io对比
- BufferedInputStream与FileInputStream的IO效率对比
- 同步、异步、堵塞、非堵塞和函数调用及I/O之间的组合概念
- 格式化io与非格式化io得对比理解
- read\write 堵塞与非堵塞读取串口数据
- read\write 堵塞与非堵塞读取串口数据
- IO模式设置,阻塞与非阻塞IO的比较
- 非阻塞IO与异步IO的区别
- Android--利用Handler消息转发机制实现倒计时(内含防止内存泄露处理)
- python find()函数
- 单例模式
- JS的十大经典算法排序
- [python每日一练]--0008:找出html中正文
- 非堵塞IO的读与写的回射客户端对比分析
- “插件(application/x-vlc-plugin)不受支持”NPAPI和PPAPI的问题
- 使用window.location.href和document.referrer
- SQL之CASE,WHEN,THEN
- 液压阀工作原理详解
- HADOOP生态圈以及各组成部分的简介
- Spring xml注入与注解小例子
- HDU:畅通工程再续
- 用C和C++编写并输出乘法口诀表