`
lisaiori
  • 浏览: 15453 次
  • 性别: 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方法的阻塞特性

    阻塞I/O是指当调用`read`方法时,如果数据没有准备好,那么该方法会暂停执行,直到有数据可读才会继续。这就像我们在等待一个门打开,门没开时我们无法通过,只能在那里等待。这种方式简单直观,但可能导致程序在...

    java阻塞i/o与非阻塞i/o控制

    阻塞I/O模型是最常见的I/O模式,在Java中主要体现在`InputStream`和`OutputStream`等基本I/O类上。当一个线程调用read或write方法时,如果数据尚未准备好,那么这个线程会被挂起,即进入阻塞状态,直到数据准备就绪...

    Java输入与输出(I、O).

    另外,Java NIO(New IO)是Java 1.4引入的,提供了非阻塞I/O、通道(Channel)、选择器(Selector)等高级特性,适用于高性能服务器端编程。NIO的Buffer类和Channel类提供了更高效的数据传输方式。 总的来说,Java...

    Java中的I/O学习总结

    ### Java中的I/O学习总结 #### 一、Java I/O概述 Java的I/O(Input/Output)系统是一套用于处理输入输出操作的框架。它主要用于处理数据的读取和写入,支持多种数据源,如文件、网络连接等。 #### 二、I/O基本...

    android开发网络请求的几种方式

    2. **使用子线程处理网络请求**:将耗时的网络请求操作放到子线程中执行,以避免阻塞主线程。接下来我们将详细介绍如何使用`AsyncTask`来实现这一点。 #### 三、使用AsyncTask进行网络请求 `AsyncTask`是Android...

    Java课程设计报告书-学生版-1_I/O流与文件课程设计_

    Java NIO(New Input/Output)库提供了非阻塞I/O和通道(Channels)等高级特性,适用于高并发的I/O操作。`java.nio.file` 包提供了更现代的文件操作API,如 `Files.copy()`,`Files.lines()` 等方法。 通过本次课程...

    io和newio的用法和比较 socket 实例

    例如,当你使用Socket的InputStream.read()方法时,如果数据没有到达,该方法会一直阻塞直到数据到来。I/O API适用于小规模的数据传输和简单的并发场景。 然后,New I/O(NIO)是在Java 1.4引入的,其核心是Channel...

    jdk6.0从入门到精通-----chapter5网络编程 新I/O(含源码下载)

    在JDK 6.0之前,Java主要使用传统的I/O(InputStream和OutputStream)模型,这种模型基于阻塞I/O,即当一个线程等待数据读取或写入时,会被阻塞,无法执行其他任务,这在处理大量并发连接时效率较低。 新I/O(NIO)...

    android 快速的搜索手机文件引擎 java I/O的应用

    - Java I/O库提供了大量的类和接口,如InputStream、OutputStream、Reader、Writer等,用于处理不同类型的输入和输出操作。 - FileInputStream和FileOutputStream用于与文件进行字节流通信,而 FileReader和...

    Javaio流思维导图

    还有FileChannel和Selector等NIO(非阻塞I/O)组件,它们在高并发场景下提高了性能,通过选择器可以选择多个通道进行读写,而无需轮询。 最后,别忘了关闭流的重要性。每当完成I/O操作后,都应调用close()方法释放...

    Android应用源码之Android应用源码安卓与PC的Socket通信项目C#版+Java版_应用.zip

    - 对于性能优化,可以考虑使用NIO(非阻塞I/O)或Netty框架来提高并发处理能力。 - 错误处理和断线重连机制是必不可少的,确保在网络不稳定时仍能保持通信。 5. 实战应用: 这个项目可以应用于多种场景,如智能...

    输入输出概念演示PPT图表.rar

    9. **编程接口**:在软件层面,开发者通常使用系统调用来实现I/O操作,例如在C语言中使用read()和write()函数,或者在Java中使用InputStream和OutputStream类。 10. **并发处理**:在多任务环境中,有效的I/O管理是...

    Java如何中断一个正在运行的线程[整理].pdf

    当线程在某些阻塞操作(如`Thread.sleep()`, `Object.wait()`, `SocketInputStream.read()`, 等)中时,检测到中断标志并抛出`InterruptedException`,这样线程就可以捕获异常并响应中断请求。但在非阻塞代码段,如...

    android4.0 socket 例程 例子 源代码

    当客户端连接时,这个方法会阻塞直到连接建立,并返回一个新的`Socket`对象。 - 使用`Socket`的`getInputStream()`和`getOutputStream()`方法获取输入/输出流,进行数据读写。 2. **客户端**: - 创建`Socket`...

    深入分析 Java IO 的工作机制1

    本篇文章将深入探讨Java I/O的工作原理,包括基本架构、磁盘I/O、网络I/O(特别是Java Socket的工作方式)、NIO(非阻塞I/O)以及优化技巧。 首先,Java的I/O类库主要分布在`java.io`包下,这些类大致分为四类:...

    url.zip_Java URL实现文件下载

    如果我们要下载的是图片文件,可以使用`ImageIO.read()`方法。以下是一个下载图片的例子: ```java import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.File; import java.io....

    用JAVA实现缓冲多线程无阻塞读取远程文件.pdf

    在Java中实现缓冲多线程无阻塞读取远程文件的知识点主要包括Java I/O操作、多线程编程、网络编程以及缓冲机制的使用。以下是根据所提供的文件内容整理的知识点: 1. Java I/O操作基础 - `java.io.InputStream`: 一...

    第三次作业1

    while ((len = inputStream.read(bytes)) != -1) { fos.write(bytes, 0, len); fos.flush(); } inputStream.close(); fos.close(); } catch (IOException e) { e.printStackTrace(); } } } ``` Java中的...

    Android-使用基础的网络请求方式使用Service下载网络apk文件

    IntentService提供了一个工作线程来处理异步任务,避免阻塞主线程。 ```java public class UpdateApkService extends IntentService { //... } ``` 在服务内部,我们需要重写onHandleIntent()方法,这是...

    javaNiO.doc

    传统I/O模型基于字节流或字符流,主要通过`InputStream`、`OutputStream`、`Reader`和`Writer`等类实现。这类模型的核心特点是同步阻塞式编程,即程序在执行I/O操作时会处于等待状态,直至操作完成。这种方式在低...

Global site tag (gtag.js) - Google Analytics