文件和Stream
I/O和文件
输入/输出(I/O)就是在内存和外部设备之间复制数据的过程。输入(input)就是从I/O设备复制数据到内存,输出(output)就是从内存复制数据到I/O设备。
一个文件可以理解成一串字节序列。所有的I/O设备,如网络、磁盘和终端,都被抽象为文件。所有的输入和输出都可以简化地抽象为对相应文件的读或写。对文件的操作包括:
- 打开文件。一个应用程序可以请求内核”打开“一个文件(内核再请求硬件去访问某个I/O设备),内核会返回一个叫做描述符(descriptor)的非负整数,用来标识此文件。应用程序可以拿着这个描述符对此文件继续做其他操作。
- 改变当前的文件位置。内核会记录每一个打开的文件的“文件位置” k,这个k指向字节序列中的某个字节,每次读写都会相应地改变k的值,初始为文件的开头(k=0)。应用程序也可以使用seek函数来显示设置当前文件位置。
- 读写文件。读就是从文件中复制n个字节到内存,从当前的文件位置k开始,然后将k设为k + n。当读到文件的末尾时,会触发end-of-file (EOF)。类似地,写就是从内存中复制n个字节到文件,也是当前的文件位置k开始,然后将k设为k + n。
- 关闭文件。当应用程序不再需要一个文件时,它会请求内核关闭这个文件,然后内核会释放所有与这个文件相关的数据结构对象(打开文件时创建的数据结构对象)。当一个应用程序终止时,内核会关闭所有它打开的文件。
目录和缓冲区
文件分为普通文件和目录( directory),以及其他类型,如socket。一个目录就是一个包含了一组链接(link)的文件,每个链接都是一个文件名,这个文件名指向一个文件(当然可能也是一个目录)。每个目录都包含.和..这两个链接,其中.指向这个目录本身,..指向这个目录的父目录。每个进程的上下文中都保存着一个叫做当前工作目录(current working directory)的状态。相对路径就是从当前工作目录开始,到某个文件的路径。
假设我们需要从一个ASCII文本文件中一行一行地读取,该如何做?一种办法是一次读取一个字节,然后判断一下这个字节是不是换行符。这种办法的缺点是效率不是很高,因为每次读取一个字节都需要切换到内核模式。一种更好的办法是先将一定数量的字节从文件中读到一个内部的缓冲区(buffer)中,然后再从这个缓冲区中一个字节一个字节地读取,当这个缓冲区被读完时,就自动再从文件中读一些字节过来。
Stream
高级语言中的I/O操作的API往往包含stream的概念。stream可以理解为对文件的抽象,但内部往往使用了缓冲区(作用同样是为了减少切换到内核模式的开销)。stream封装了操作系统提供的I/O模型,也就是我们不需要关心内核级别的函数,只需要对一个stream进行操作即可。类似文件,我们可以对一个Stream进行的操作包括读或写等等。
对于操作系统来说,EOF(end-of-file)是一个会被内核检测到的状态,对于磁盘文件,EOF会在当前文件位置超过此文件长度时发生,对于一次网络连接,EOF会在另一端关闭连接时发生。而Stream是对文件的高层抽象,也可以有自己的“EOF”(其实称作end of stream更合适)。以C#为例,StreamReader.ReadToEnd会一直读取Stream直到遇到EOF才返回,若stream是一个socket,则直到另一端的socket关闭了这次连接之前,StreamReader.ReadToEnd都不会返回,因为只有另一端关闭连接才会触发这一端的EOF。
var tcpClient = new TcpClient(host, port);
var connectionStream = tcpClient.GetStream();
using (StreamReader reader = new StreamReader(connectionStream))
{
await reader.ReadToEndAsync(); // 直到另一端关闭连接之前,会一直“阻塞”在这里
}
但是下面的代码,即使是有Connection: Keep-Alive的HTTP持久连接,也不会有问题:
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
HttpWebResponse response = (HttpWebResponse)(await request.GetResponseAsync());
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
await reader.ReadToEndAsync(); // 即使另一边还没有关闭这次连接,也不会“阻塞”在这里
}
原因在于HttpWebResponse.GetResponseStream返回的并不是NetworkStream,而是一个特殊的Stream,叫作ConnectStream,专门用于读取一次HTTP响应的body。我们知道,HTTP是建立于TCP之上的,在一次TCP连接中,任何一端都可以对其socket无限制地读或写,理论上客户端可以无限地从socket中读取或等待读取数据,所以HTTP才会有Content-Length,来告诉客户端这一次响应有多大,该读取多少数据就够了。对于这个ConnectStream来说,当从底层的NetworkStream中读取的字节数达到指定的Content-Length大小时,就算是EOF。
所以,Stream相当于提供了一个抽象接口,不同的实现有不同的行为,但对外提供的功能是一致的。另外,Stream的读操作和操作系统提供的函数很类似,都会返回本次读取了多少字节,每次真正读取的字节数不一定等于输入参数中要求读取的字节数(特别是在网络编程时),有时候很容易忽视这一点。
参考
《深入理解计算机系统》第三版 第十章 System-Level I/O
文件和Stream的更多相关文章
- C# 流与文件(Stream & File & byte[])
原文:https://www.cnblogs.com/long-gengyun/archive/2010/03/28/1698681.html 文件概述 文件在操作时表现为流,即流是从一些输入中读取 ...
- 通过文件流stream下载文件
public ActionResult ShowLocalizedXML(int id) { string orderName = ""; string xmlString = G ...
- .net c# 提交包含文件file 的form表单 获得文件的Stream流
1.前台html代码 要写一个有id的form,可是不能有runat="server"属性.由于一个页面中,有这个属性的form表单仅仅能有一个. 再要有一个有name的ifram ...
- C# 将文件转换为 Stream
public Stream FileToStream(string fileName) { // 打开文件 FileStream fileStream = new FileStream(fileNam ...
- Java中的文件和stream流的操作代码
1.Java中FileRead方法的运用代码及详解 package example2;import java.io.FileReader;import java.io.IOException;clas ...
- linux下载网页上的文件夹以及删除文件(stream)
wget -nd -r -l1 --no-parent http://www.cs.virginia.edu/stream/FTP/Code/ 注:-nd 不创建目录:-r 递归下载:-l1只下载当前 ...
- 14、Java文件操作stream、File、IO
1.文件操作涉及到的基本概念 File File类 是文件操作的主要对象中文意义就是 文件 顾名思意 万物皆文件,在计算上看到的所有东西都是文件保存,不管是你的图片.视频.数据库数据等等都是按照基本的 ...
- C#下载文件,Stream 和 byte[] 之间的转换
stream byte 等各类转换 http://www.cnblogs.com/warioland/archive/2012/03/06/2381355.html using (System.Net ...
- C#:文件、byte[]、Stream相互转换
一.byte[] 和 Stream /// <summary> /// byte[]转换成Stream /// </summary> /// <param name=&q ...
随机推荐
- maven引用本地jar,并打包部署
由于项目需要的一个jar在maven仓库里没有,又不想把jar手动导入仓库.然而百度的几个处理方式都是一样的方法,我却没有一个成功的.于是就请教了大神,大神也是各种测试,终于成功实现了,实现如下: 新 ...
- Postgresql 大小版本升级
文章结构如下: Postgresql是一个非常活跃的社区开源项目,更新速度很快,每一次版本的更新都会积极的修复旧版本的BUG,性能上也会有不同幅度的提升.10之前的版本由三部分组成,10开始只有两部分 ...
- oa_mvc_easyui_删除(6)
1.删除列,添加a标签,绑定参数 <a href="javascript:void(0)" class="delete" ids="@newli ...
- WPF ListView多行显示
//前台 <ListView Margin="14,152,12,74" Name="lvList" SelectionMode="Multip ...
- O014、云计算与OpenStack
参考https://www.cnblogs.com/CloudMan6/p/5334760.html 云计算 基本概念 所有的新事物都不是突然冒出来的,都有前世和今生.云计算也是IT技术不断发 ...
- IOS手机伪类a:active失效
IOS手机伪类a:active失效:点击更改颜色,松开恢复 解决方案:OS系统的移动设备中,需要在按钮元素或body/html上绑定一个touchstart事件才能激活:active状态 docume ...
- 第十八篇 JS传参数
JS传参数 参数,这是个什么东西呢?简单的说吧,我们去玩别人的网站,一般来个登录,有用户名和密码,当我们输入正确之后,那么这用户名和密码里面的值,就是参数的值,它将这个值传给“参数”,然后提交到后 ...
- spring cloud 入门
某种程度上 软硬件 殊途同归了 (软件模仿硬件 总线设计, 资源定位 (寻址) ) spring 是什么 EDA ( Event-driven architecture ) (SOA , SOAP , ...
- docker 网络 实现
最近在学习docker网络相关的知识,关于网络这块儿记下来,以便review dokcer安装完成之后默认提供三种网络 bridge host none docker默认使用bridge brid ...
- jq选择CheckBox进行排序
<!DOCTYPE html><html><head> <meta charset="utf-8"> <meta name=& ...