近期打算研究下基于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的基础上再封装一层协议,用户长连接的传输.协议的信息包,也分包头和包体两个部分.
包体,主要就是我们要传输的信息.(维持连接的信息包,包体可为空)
包头,一般分为三个部分.第一部分是信息包的长度(长度一般是指整个信息包的长度);第二部分是包体信息的类型(在这里指出是否是维持连接包);第三部分是信息包的序列号,一般情况下,这个序列号要确保在传输过程中唯一标识该信息包.
如果为了安全起见,还可以在包体后添加包尾,包尾数据用于对包体数据的验证)
这样,通信双方就可以根据包长来判断一次接收的操作是否结束了.
相关推荐
阻塞I/O是指当调用`read`方法时,如果数据没有准备好,那么该方法会暂停执行,直到有数据可读才会继续。这就像我们在等待一个门打开,门没开时我们无法通过,只能在那里等待。这种方式简单直观,但可能导致程序在...
阻塞I/O模型是最常见的I/O模式,在Java中主要体现在`InputStream`和`OutputStream`等基本I/O类上。当一个线程调用read或write方法时,如果数据尚未准备好,那么这个线程会被挂起,即进入阻塞状态,直到数据准备就绪...
另外,Java NIO(New IO)是Java 1.4引入的,提供了非阻塞I/O、通道(Channel)、选择器(Selector)等高级特性,适用于高性能服务器端编程。NIO的Buffer类和Channel类提供了更高效的数据传输方式。 总的来说,Java...
### Java中的I/O学习总结 #### 一、Java I/O概述 Java的I/O(Input/Output)系统是一套用于处理输入输出操作的框架。它主要用于处理数据的读取和写入,支持多种数据源,如文件、网络连接等。 #### 二、I/O基本...
2. **使用子线程处理网络请求**:将耗时的网络请求操作放到子线程中执行,以避免阻塞主线程。接下来我们将详细介绍如何使用`AsyncTask`来实现这一点。 #### 三、使用AsyncTask进行网络请求 `AsyncTask`是Android...
Java NIO(New Input/Output)库提供了非阻塞I/O和通道(Channels)等高级特性,适用于高并发的I/O操作。`java.nio.file` 包提供了更现代的文件操作API,如 `Files.copy()`,`Files.lines()` 等方法。 通过本次课程...
例如,当你使用Socket的InputStream.read()方法时,如果数据没有到达,该方法会一直阻塞直到数据到来。I/O API适用于小规模的数据传输和简单的并发场景。 然后,New I/O(NIO)是在Java 1.4引入的,其核心是Channel...
在JDK 6.0之前,Java主要使用传统的I/O(InputStream和OutputStream)模型,这种模型基于阻塞I/O,即当一个线程等待数据读取或写入时,会被阻塞,无法执行其他任务,这在处理大量并发连接时效率较低。 新I/O(NIO)...
- Java I/O库提供了大量的类和接口,如InputStream、OutputStream、Reader、Writer等,用于处理不同类型的输入和输出操作。 - FileInputStream和FileOutputStream用于与文件进行字节流通信,而 FileReader和...
还有FileChannel和Selector等NIO(非阻塞I/O)组件,它们在高并发场景下提高了性能,通过选择器可以选择多个通道进行读写,而无需轮询。 最后,别忘了关闭流的重要性。每当完成I/O操作后,都应调用close()方法释放...
- 对于性能优化,可以考虑使用NIO(非阻塞I/O)或Netty框架来提高并发处理能力。 - 错误处理和断线重连机制是必不可少的,确保在网络不稳定时仍能保持通信。 5. 实战应用: 这个项目可以应用于多种场景,如智能...
9. **编程接口**:在软件层面,开发者通常使用系统调用来实现I/O操作,例如在C语言中使用read()和write()函数,或者在Java中使用InputStream和OutputStream类。 10. **并发处理**:在多任务环境中,有效的I/O管理是...
当线程在某些阻塞操作(如`Thread.sleep()`, `Object.wait()`, `SocketInputStream.read()`, 等)中时,检测到中断标志并抛出`InterruptedException`,这样线程就可以捕获异常并响应中断请求。但在非阻塞代码段,如...
当客户端连接时,这个方法会阻塞直到连接建立,并返回一个新的`Socket`对象。 - 使用`Socket`的`getInputStream()`和`getOutputStream()`方法获取输入/输出流,进行数据读写。 2. **客户端**: - 创建`Socket`...
本篇文章将深入探讨Java I/O的工作原理,包括基本架构、磁盘I/O、网络I/O(特别是Java Socket的工作方式)、NIO(非阻塞I/O)以及优化技巧。 首先,Java的I/O类库主要分布在`java.io`包下,这些类大致分为四类:...
如果我们要下载的是图片文件,可以使用`ImageIO.read()`方法。以下是一个下载图片的例子: ```java import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.File; import java.io....
在Java中实现缓冲多线程无阻塞读取远程文件的知识点主要包括Java I/O操作、多线程编程、网络编程以及缓冲机制的使用。以下是根据所提供的文件内容整理的知识点: 1. Java I/O操作基础 - `java.io.InputStream`: 一...
while ((len = inputStream.read(bytes)) != -1) { fos.write(bytes, 0, len); fos.flush(); } inputStream.close(); fos.close(); } catch (IOException e) { e.printStackTrace(); } } } ``` Java中的...
IntentService提供了一个工作线程来处理异步任务,避免阻塞主线程。 ```java public class UpdateApkService extends IntentService { //... } ``` 在服务内部,我们需要重写onHandleIntent()方法,这是...
传统I/O模型基于字节流或字符流,主要通过`InputStream`、`OutputStream`、`Reader`和`Writer`等类实现。这类模型的核心特点是同步阻塞式编程,即程序在执行I/O操作时会处于等待状态,直至操作完成。这种方式在低...