文件I/O方式比较

1.阻塞式文件I/O

进程从调用函数开始,直到返回这段时间都处于阻塞状态。

2.非阻塞式文件I/O

如果当前没有数据可操作,将不阻塞当前进程,而是立即返回一个错误信息。需要反复尝试。

3.多路复用I/O

仍然是阻塞方式等待,但是可以同时等待多个文件描述符。

4.信号驱动I/O

异步方式,等到数据准备好后通知处理进程,不需要重复询问,效率高。

I/O阻塞与非阻塞操作

阻塞方式:默认情况下read/write和 把flag设为0的recv/send

非阻塞方式:如果没有数据,立刻返回-1表示失败,并修改系统全局变量errno的值为EAGAIN,表示数据未准备好。

      通过设置recv的MSG_DONTWAIT标志可以实现。如果设置socket的文件描述符的属性为非阻塞,将导致后续所有针对该文件描述符的操作都为非阻塞。

例子:

服务器端:接收非阻塞,发送阻塞。 可以连续发送多条。如果对方发送的很多数据过来,也会一次性接收。

客户端:发送、接收都阻塞。这个例子里面,接收端一定是收一条、发一条这样交替着的。如果服务器发送了多条,则会分开接收到。

奇怪:这个例子里面,接收端也绑定了自己的地址,而之前的例子里却没有绑定。两者都实现了通信,为什么呢?

服务器代码:

#include<sys/types.h>
#include<sys/socket.h>
#include<stdio.h>
#include<string.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<stdlib.h>
#include<errno.h>
#define BUFSIZE 128 int main(int argc, char *argv[])
{
int server_sockfd, client_sockfd;
int server_len, client_len;
struct sockaddr_in server_address;
struct sockaddr_in client_address;
int i, byte;
char char_send[BUFSIZE];
//创建socket对象 阻塞
server_sockfd = socket(AF_INET, SOCK_STREAM, );
server_address.sin_family = AF_INET;
//从argv[1]提取IP地址
if(inet_aton(argv[],(struct in_addr*)&server_address.sin_addr.s_addr) == )
{
perror(argv[]);
exit(EXIT_FAILURE);
}
server_address.sin_port = htons(); //使用特定端口
server_len = sizeof(server_address);
//绑定IP信息
bind(server_sockfd, (struct sockaddr *)&server_address, server_len);
//监听网络
listen(server_sockfd, );
printf("server waiting for connect\n");
client_len = sizeof(client_address);
//等待连接
client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_address,(socklen_t *)&client_len);
for(i = ; i < ; i++)
{
memset(char_send, '\0', BUFSIZE);
printf("input message to send:");
fgets(char_send, BUFSIZE, stdin); //阻塞在终端,接收用户输入数据
//发送
if((byte = send(client_sockfd, char_send, strlen(char_send), )) == -)
{
perror("send");
exit(EXIT_FAILURE);
}
memset(char_send, '\0', BUFSIZE);
//非阻塞接收
byte = recv(client_sockfd, char_send, BUFSIZE, MSG_DONTWAIT);
if(byte > )
{
printf("get %d message:%s", byte, char_send);
byte = ;
}
else if(byte < )
{
if(errno == EAGAIN)
{
errno = ;
continue;
}
else
{
perror("recv");
exit(EXIT_FAILURE);
}
}
}
//关闭socket对象
shutdown(client_sockfd, );
shutdown(server_sockfd, );
}

客户端代码:

#include<stdio.h>
#include<string.h>
#include<errno.h>
#include<sys/socket.h>
#include<resolv.h>
#include<stdlib.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<fcntl.h>
#define MAXBUF 128
int main(int argc, char **argv)
{
int sockfd, ret, i;
struct sockaddr_in dest, mine;
char buffer[MAXBUF + ];
//创建socket对象
if((sockfd = socket(AF_INET, SOCK_STREAM, )) < )
{
perror("Socket");
exit(EXIT_FAILURE);
}
bzero(&dest, sizeof(dest));
dest.sin_family = AF_INET;
dest.sin_port = htons(); //服务器的特定端口,与服务器端设置一致
//获取服务器IP地址,由argv[1]指定
if(inet_aton(argv[], (struct in_addr *)&dest.sin_addr.s_addr) == )
{
perror(argv[]);
exit(EXIT_FAILURE);
}
bzero(&mine, sizeof(mine));
mine.sin_family = AF_INET;
mine.sin_port = htons(); //本地端口
//本地IP地址,由argv[2]指定
if(inet_aton(argv[], (struct in_addr *)&mine.sin_addr.s_addr) == )
{
perror(argv[]);
exit(EXIT_FAILURE);
}
//绑定自己的IP地址信息
if(bind(sockfd, (struct sockaddr *)&mine, sizeof(struct sockaddr)) == -)
{
perror("bind");
exit(EXIT_FAILURE);
}
//发起连接
if(connect(sockfd, (struct sockaddr *)&dest, sizeof(dest)) != )
{
perror("Connect");
exit(EXIT_FAILURE);
}
//设置sockfd描述符为非阻塞
if(fcntl(sockfd, F_SETFL, O_NONBLOCK) == -)
{
perror("fcntl");
exit(EXIT_FAILURE);
}
while()
{
bzero(buffer, MAXBUF + );
//接收
ret = recv(sockfd, buffer, MAXBUF, ); //因为设置socket非阻塞,故此操作非阻塞
if(ret > )
{
printf("get %d message:%s", ret, buffer);
ret = ;
}
else if(ret < )
{
if(errno == EAGAIN)
{
errno = ;
continue;
}
else
{
perror("recv");
exit(EXIT_FAILURE);
}
}
memset(buffer, '\0', MAXBUF + );
printf("input message to send:");
fgets(buffer, MAXBUF, stdin); //在接收到数据后阻塞在终端,向对方发
if((ret = send(sockfd, buffer, strlen(buffer), )) == -) //发送数据
{
perror("send");
exit(EXIT_FAILURE);
}
}
close(sockfd);
return ;
}

我在同一台虚拟机的不同终端做实验

服务器端结果:

客户端结果:

分析一下这个代码:

服务器端

for循环只有5次,且发送数据阻塞,意味着一定是发送一次消息后,i才会加1,故一共能发送5条消息。

接收是非阻塞,且在发送后面。即如果有客户端发来的的信息,在服务器发送消息后,能够接收到客户端信息。但是如果没有,服务器端直接continue。即接收的消息会少于5条。

其实非阻塞的意思就是代码直接返回错误,具体后面怎么处理,是我们自己写代码决定的。

客户端

接收数据非阻塞,但是接收数据在发送数据前面,且接收数据失败后会直接continue, 这导致如果接收数据失败则在当前循环下无法发送数据。故客户端的效果一定是接收一条,发送一条这样交替的。

发送数据非阻塞,但是因为fgets会阻塞在终端,所以看不出发送的非阻塞效果。

【linux高级程序设计】(第十四章)TCP高级应用的更多相关文章

  1. 读书笔记 - js高级程序设计 - 第十五章 使用Canvas绘图

    读书笔记 - js高级程序设计 - 第十三章 事件   canvas 具备绘图能力的2D上下文 及文本API 很多浏览器对WebGL的3D上下文支持还不够好   有时候即使浏览器支持,操作系统如果缺缺 ...

  2. linux高级管理第十四章--kvm虚拟化

    案例 安装kvm所需软件 验证 注:虚拟机要开启虚拟引擎 开启服务 环境准备 安装相关软件包 启动 创建网桥 重启,reboot 安装虚拟机 完成.

  3. 第十四章:高级I/O

    14.1:引言 本章内容包括非阻塞I/O.记录锁.系统V流机制.I/O多路转接(select和poll函数).readv和writev函数以及存储映射I/O(mmap),这些都称为高级I/O. 14. ...

  4. 鸟哥的Linux私房菜——第十四章:Bash Shell

    视频链接:http://www.bilibili.com/video/av10094012/ 本章目录: 1. Bash shell1.1 什么是 shell ? (我们通过shell与Kernel核 ...

  5. 【TCP/IP详解 卷一:协议】第二十四章 TCP的未来与性能

    来到了TCP的最后一个章节,未来与性能.在当时(1991年)的未来,如今已经部分变为现实,部分就只是历史中的实验. 主要内容: 路径MTU的发现与TCP的结合. 长肥管道 和 高速千兆比网络. 窗口扩 ...

  6. 【读书笔记】C#高级编程 第二十四章 文件和注册表操作

    (一)文件和注册表 对于文件系统操作,相关的类几乎都在System.IO名称空间中,而注册表操作由System.Win32名称空间中的类来处理. (二)管理文件系统 System.MarshalByR ...

  7. 《javascript高级程序设计》第四章 Variables,scope,and memory

    4.1 基本类型和引用类型的值 primitive and reference values 4.1.1 动态的属性 dynamic properties 4.1.2 复制变量值 copying va ...

  8. JavaScript高级程序设计:第四章

    变量.作用域和内存问题 1.ECMAScript变量可能包含两种不同数据类型的值:基本类型值和引用类型值.基本类型值指的是简单的数据段,引用类型值指的是有多个值构成的对象. 2.动态的属性:定义一个基 ...

  9. 《JAVASCRIPT高级程序设计》第四章

    javascript变量是松散类型,它只是在特定时间表示特定值的一个名字而已:变量的值以及类型,可以在脚本的生命周期内改变.变量的类型,分为基本类型和引用类型两种,具体介绍如下图所示: 执行环境是Ja ...

  10. 《JavaScript 高级程序设计》第四章:变量、作用域和内存问题

    目录 变量的引用 执行环境及作用域 作用域链延长 块级作用域 垃圾回收机制 变量的引用 当一个变量保存了基本数据类型时,此时对于变量的操作(赋值,运算)就是操作这个基本数据的本身,就算是赋值操作,赋值 ...

随机推荐

  1. TouTiao开源项目 分析笔记14 段子评论

    1.段子页面详情 1.1.先看看预览界面吧 左边的页面已经实现了,现在的目的就是要实现点击左侧的每一个item 然后跳转到右边相应的段子详情页面. 1.2.首先肯定有右侧这个活动==>JokeC ...

  2. 使用Matrix-tree与它的行列式来解决生成树计数问题

    我又把Matrix写错啦 这东西讲课的时候竟然一笔带过了,淦 好吧这东西我不会证 那我们来愉快的看结论吧 啦啦啦 预备工作 你有一个 $ n $ 个点的图 比如说 5 /|\ / | \ 2--1-- ...

  3. 剑指Offer - 九度1389 - 变态跳台阶

    剑指Offer - 九度1389 - 变态跳台阶2013-11-24 04:20 题目描述: 一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级.求该青蛙跳上一个n级的台阶总共有多少种跳 ...

  4. 【Perceptron Learning Algorithm】林轩田机器学习基石

    直接跳过第一讲.从第二讲Perceptron开始,记录这一讲中几个印象深的点: 1. 之前自己的直觉一直对这种图理解的不好,老按照x.y去理解. a) 这种图的每个坐标代表的是features:fea ...

  5. python-成员修饰符

    python的面相对象中,拥有3个成员,字段.方法.属性 class Foo: def __init__(self,name): #公有字段name在类中与类外均能调用 self.name = nam ...

  6. Metadata 的概念

    https://www.ibm.com/developerworks/cn/cloud/library/1509_liukg_openstackmeta/ http://mathslinux.org/ ...

  7. 课时21:函数:lambda表达式

    目录: 一.lambda表达式 二.介绍两个BIF:filter()和map() 三.课时21课后习题及答案 ********************* 一.lambda表达式 *********** ...

  8. JavaScript里面的正则以及eval

    1.eval JavaScript中的eval是Python中eval和exec的合集,既可以编译代码也可以获取返回值. eval() EvalError   执行字符串中的JavaScript代码 ...

  9. 更换checkbox的原有样式

    通常情况下,各个浏览器对的样式不一致,并且不那么美观.所以有时候设计需要我们更换原有的样式: html: <span><input type="checkbox" ...

  10. iPhone:iOS界面,本地生成随机验证码

    本文博客,模仿杰瑞教育的一篇博文,并在它的基础上,进行了些许更改.同时在重写的过程中,对自己忽略的地方,进行了重新认识,受益匪浅.文章来源:http://www.cnblogs.com/jerehed ...