`
lisaiori
  • 浏览: 15106 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

I/O 的 InputStream.read 方法的阻塞

 
阅读更多

近期打算研究下基于NIO的Netty框架,先来回顾一下I/O的基础。

 

JAVA里的IO 目前有两种,一种是早期发布的I/O模型,也就是所谓的BIO(Blocking I/O);另一种是JDK1.4里发布的基于 多路复用实现的NIO。

 

阻塞型 I/O,主要阻塞在两个地方:

第一:在调用InutStream.read 方法是阻塞的,它会一直等到数据到来时(或超时)才会返回;第二:在调用ServerSocket.accept()方法时,也会一直阻塞到有客户端连接才会返回;

目前大部分的客户端服务端的网络应用软件的早期版本的I/O都是使用阻塞型的I/O实现。处理模型参考:

阻塞型的I/O 存在以下几点问题:

首先,InputStream.read()方法在其缓存区未满时,会造成阻塞,只有一定的数据填满了缓存区或者客户端关闭了套接字,方法才会返回。
其次,会产生大量的垃圾,BufferedReader创建了缓存区来从套接字中读入数据,但是同样创建了一些字符串存储这些数据。这些String很快变成垃圾需要回收。
类似的,读写操作被阻塞而且向流中一次写入一个字符会造成效率低下,所以应该使用缓存区,但一旦使用缓存,流又会产生更多是垃圾。
另外,通常在JAVA中处理阻塞I/O要用到线程(大量的线程),一般是实现一个线程池来处理请求。线程使得服务器可以处理多个连接,但是他们同样也引发了许多问题。每个线程拥有
自己的栈空间并且占用一些CPU时间,耗费很大,而且很多时间是浪费了阻塞I/O操作上,没有有效利用CPU.
 

下面,来看一下阻塞I/O的具体的阻塞情况点:

首先来看一下JAVA文档中的 InputStream 的签名

public abstract class InputStream extends Object implements Closeable

此抽象类是表示字节输入流的所有类的超类。

需要定义 InputStream 的子类的应用程序必须始终提供返回下一个输入字节的方法。

 

个人理解,这种对象的概念有点像需要数据传输双方之间的一个通道,这个通道负责接收数据(与之对应还有OutPutStream 负责发送数据)。

 

到目前为止,我所接触到的I/O主要是 File I/O 和 Socket I/O。

 

InputStream 中的read方法用于读取数据,方法有3个重载。

abstract  int read()
          从输入流读取下一个数据字节。
 int read(byte[] b)
          从输入流中读取一定数量的字节并将其存储在缓冲区数组 b 中。
 int read(byte[] b, int off, int len)
          将输入流中最多 len 个数据字节读入字节数组。

 

其中InputStream.read()方法,这个方法是从流里每次只读取读取一个字节,效率会非常低。    

更好的方法是用InputStream.read(byte[] b)或者InputStream.read(byte[] b,int off,int len)方法,一次读取多个字节。

这里有一点需要特别注意:read 方法在输入数据可用、检测到文件末尾或者抛出异常前,此方法一直阻塞。

这是什么意思呢?

我们来看一个简单的Socket通信的例子:

 

 【Client】                                                                【ServerSocket】

                                                                                   1、accept()

                                                                                   服务端阻塞,至接收到客户端的请求

 

   2、new Socket("address",port);                                          

   建立一个和服务端的socket连接                               接收到客户端连接,accept 阻塞结束

 

                                                                                   3、socket.getInputStream().read();

                                                                                   从socket请求获取输入流,读取流中的数据。

                                                                                   这个时候问题来了:虽然和客户端的连接好了

                                                                                   但是服务端不知道客户端什么时候会发来数据,

                                                                                   另外,因为网络传输的原因,数据还可能被分

                                                                                   多次到达。结合上面的说明:也就好理解了。

                                                                                   read需要等待输入和输入的到达。

---------------------------------------------------------------------------------------------------------------------------

Socket流这里还存在另外一个问题,socket流和文件流不太一样,文件流很容易知道文件末尾,到了文件末尾,直接就把流close掉就OK了。但是socket 流不一样,你无法知道它什么时候到末尾,所以连接一直保持着,流也一直保持阻塞状态。即使用了带参数的read方法,返回了有效数据,但其实流仍然没有关闭,处于阻塞状态。
针对这种请情况,一般就需要通信的双方约定数据传输的协议了。比如,约定消息的头部首先明确此次传输数据的大小。这样服务端就可以有目的性的读取数据。

 

---------------------------------------------------------------------------------------------------------------------------

总结一下:

首先,Socket I/O时,发送方如果不将输出流进行关闭,接收方就会认为输入流没有结束,直到超时.
其次,我们判断一个信息是否已经完全的读取完毕,除了使用输入流结束这种办法,还可以自行封装一层协议,用于信息的交互.

 

这里其实是可以借鉴TCP长连接的实现的:Java当中的Socket类,其实是使用TCP协议进行传输的.

一般情况下,我们会在TCP的基础上再封装一层协议,用户长连接的传输.协议的信息包,也分包头和包体两个部分.
包体,主要就是我们要传输的信息.(维持连接的信息包,包体可为空)
包头,一般分为三个部分.第一部分是信息包的长度(长度一般是指整个信息包的长度);第二部分是包体信息的类型(在这里指出是否是维持连接包);第三部分是信息包的序列号,一般情况下,这个序列号要确保在传输过程中唯一标识该信息包.
如果为了安全起见,还可以在包体后添加包尾,包尾数据用于对包体数据的验证)
这样,通信双方就可以根据包长来判断一次接收的操作是否结束了.                                                                               

  

  

 

 

 

  • 大小: 138.8 KB
分享到:
评论

相关推荐

    Socket中InputStream的read方法的阻塞特性

    NULL 博文链接:https://superherosk123.iteye.com/blog/611933

    strust文件上传

    //写入buffer数组的一部分,从buf[0]开始写入并写入bytesRead个字节,这个write方法将发生阻塞直至字节写入完成。 streamOut.write(buffer, 0, bytesRead); } // 关闭输出输入流,销毁File流。 streamOut.close...

    socketdemo.rar

    Java Socket通信,Demo是通过Java ServerSocket 和 Socket 通信实现客户端发送消息和发送文件到服务器,服务器接收到消息和文件,并且实现解决inputStream.read()的阻塞问题思路。

    file-buffer:将大数据缓冲到磁盘,提供java InputStreams和OutputStreams

    该实现可确保阅读不会超过写作,从而保留了java.io.InputStream的阻塞语义。 +--------------+ | | OutputStream.write -> | FileBuffer | -> InputStream.read | | +--------------+ FileBuffer旨在允许并发...

    nio_stream_parse_json:在NIO的环境里如何解析json

    场景: 在一些nio的网络框架, 比如netty, mina等, 读取数据时异步的, 也就是不能通过InputStream.read来以阻塞的方式读取数据. 而大多数json的库, 如FastJSON, jackson等都只能解析一个阻塞式的InputStream. 在nio...

    java7hashmap源码-network_program:学习java网络编程

    非阻塞I/O @since 2020.12.01 IO Stream read() 和 write() public abstract int read(int) throws IOException public abstract void write(int) throws IOException 这两个抽象方法是由 inputStream 和 ...

    Expect工具Expectit.zip

    简单易用和可扩展,全新编写无需依赖第三方库,使用管道和非堵塞 API 基于 NIO 实现。 Maven:  <groupId>net.sf.expectit</groupId>  <artifactId>expectit-core</artifactId>  <version>0.3.0</version> </...

    java经典面试2010集锦100题(不看你后悔)

    C) 抽象类中至少需要有一个方法被定义为抽象方法,在该方法前加abstract,把该方法定义为抽象方法。 D) 抽象类中可以没有抽象方法,即所有的方法全部都有实现体。但是依旧不能为抽象类定义对象。 题目18:b 下面...

    IO体系.java

    read方法是阻塞式的。 | |--ByteArrayInputStream/:字节数组输入流。操作的都是内存中的数组,所以不需要关闭。把数组封装到流中,可以提供更多的方法操作数组。 | |--SequenceInputStream/:序列流。将多个读取...

    JAVA上百实例源码以及开源项目

    在有状态SessionBean中,用累加器,以对话状态存储起来,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用完毕,从内存中清除…… Java Socket 聊天...

    JAVA上百实例源码以及开源项目源代码

    在有状态SessionBean中,用累加器,以对话状态存储起来,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用完毕,从内存中清除…… Java Socket 聊天...

Global site tag (gtag.js) - Google Analytics