- 浏览: 209648 次
- 性别:
- 来自: 哈尔滨
文章分类
- 全部博客 (267)
- java.lang (8)
- 问题汇总 (21)
- 异常记录 (20)
- 功能实现 (19)
- 面试总结 (25)
- 技巧总结 (8)
- 常用代码 (4)
- 编程习惯 (3)
- 编码规则 (3)
- java.util (10)
- java.io (1)
- JavaWeb (9)
- MySQL (16)
- SVN (3)
- MyBatis (11)
- Velocity (7)
- 其他知识 (10)
- 人生哲理 (1)
- 人生故事 (1)
- 自我感悟 (1)
- shiro (3)
- 基础知识 (0)
- 问题总结 (1)
- Spring 标签 (1)
- Spring (3)
- 点滴生活 (1)
- DOS (1)
- CAS (4)
- Linux (9)
- Storm (6)
- Shell (1)
- regex (1)
- Collection (4)
- poi (1)
- 经典语句 (1)
- NIO (5)
- concurrent (14)
- RPC (1)
- zookeeper (3)
- 待整理 (2)
- Hadoop (9)
- RabbitMq (2)
- flume (1)
- hive (7)
- hbase (4)
- kafka (1)
- scala (1)
- GC (0)
- java.util.concurrent.atomic (1)
- java.lang.ref (6)
- JVM (2)
- algorithm (1)
- conception (1)
- java key word (1)
- sun.misc (1)
最新评论
一、
ServerSocketChannel
对比 NIO与BIO的区别,
connect accept read write 四种阻塞情况
1.accept
服务端启动,等待 accept ,若无客户端请求,程序正常执行
参考:
JDK1.6中文文档
ServerSocketChannel 的 accept() 方法
如果此通道处于非阻塞模式,那么在不存在挂起的连接时,此方法将直接返回 null。否则,在新的连接可用或者发生 I/O 错误之前会无限期地阻塞它。
不管此通道的阻塞模式如何,此方法返回的套接字通道(如果有)将处于阻塞模式。
故,可设置阻塞与非阻塞
运行,程序可正常执行完毕,且处于等待状态
2.connect
客户端发起连接请求 connect ,而服务端未启动
3.read
服务端读取客户端发送的数据,而客户端发起连接但不发送数据,程序正常,未阻塞
4.write
客户端写入数据 write,但服务端不读取,未发生阻塞
客户端输出:
字节数:1
..
字节数:0
...
字节数的变化原因:客户端不停写入,但服务端不取,数据会放入pipe管道中,但管道也是有容量大小的,达到极限则不写入。
二、channel通信
1.
客户端发送数据,服务端接收并输出客户端发送的数据
服务端
客户端:
运行结果:服务端输出正常
2.粘包问题
由于TCP传输是一种可靠的连续的数据传输,如果两次传输的数据时间间隔比较短,数据的接收方可能很难判断出两次数据的边界在哪里,感觉就好像两个数据黏着在了一次,无法区分。
TPC/IP协议:IP分发数据时,按包发送,小于包容量的多个内容会合在一起发送;大于包容量的内容拆分成多个包发送;
如何规定边界
解决方式:
1.客户端
发送数据时带上此次发送数据的长度并用\r\n与真正需要发送的内容分割
格式为: str.length+\r\n+str
2.服务端
使用大小为1的缓冲区接收并解析头部信息,获取长度
根据长度设置内容缓冲区,读取指定内容
服务端
客户端
三、DataGramChannel
发送:
接收:
四、FileChannel
阻塞式的,比BIO 中 File提供更多的方法
1.txt
abcdefghijklmnopqrstuvwxyz
ServerSocketChannel
对比 NIO与BIO的区别,
connect accept read write 四种阻塞情况
1.accept
服务端启动,等待 accept ,若无客户端请求,程序正常执行
public static void main(String[] args) throws IOException { // 创建 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); // 绑定端口 serverSocketChannel.socket().bind(new InetSocketAddress(9999)); // 等待客户端连接 // 程序运行到此,无法执行下去 SocketChannel socketChannel = serverSocketChannel.accept(); }
参考:
JDK1.6中文文档
ServerSocketChannel 的 accept() 方法
如果此通道处于非阻塞模式,那么在不存在挂起的连接时,此方法将直接返回 null。否则,在新的连接可用或者发生 I/O 错误之前会无限期地阻塞它。
不管此通道的阻塞模式如何,此方法返回的套接字通道(如果有)将处于阻塞模式。
故,可设置阻塞与非阻塞
public static void main(String[] args) throws IOException { // 创建 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); // 设置通道为非阻塞模式 // 若不加,则处于阻塞模式,一直停留在accept // 该值默认为ture,若不进行设置,则为阻塞模式 serverSocketChannel.configureBlocking(false); // 绑定端口 serverSocketChannel.socket().bind(new InetSocketAddress(9999)); // 等待客户端连接 SocketChannel socketChannel = serverSocketChannel.accept(); // 如果未接收到连接请求 while (socketChannel == null){ socketChannel = serverSocketChannel.accept(); } }
运行,程序可正常执行完毕,且处于等待状态
2.connect
客户端发起连接请求 connect ,而服务端未启动
public static void main(String[] args) throws IOException, InterruptedException { SocketChannel socketChannel = SocketChannel.open(); socketChannel.configureBlocking(false); // 开始连接 socketChannel.connect(new InetSocketAddress("127.0.0.1", 9999)); // 完成连接 socketChannel.finishConnect(); while(!socketChannel.finishConnect()){ Thread.sleep(10); } while(true){ // 服务不停止 } }
3.read
服务端读取客户端发送的数据,而客户端发起连接但不发送数据,程序正常,未阻塞
public class ServerSocketChannelDemo { public static void main(String[] args) throws IOException { // 创建 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); // 设置通道为非阻塞模式 // 若不加,则处于阻塞模式,一直停留在accept serverSocketChannel.configureBlocking(false); // 绑定端口 serverSocketChannel.socket().bind(new InetSocketAddress(9999)); // 等待客户端连接 SocketChannel socketChannel = serverSocketChannel.accept(); while (socketChannel == null){ socketChannel = serverSocketChannel.accept(); } socketChannel.configureBlocking(false); // ByteBuffer byteBuffer = ByteBuffer.allocate(1024); socketChannel.read(byteBuffer); } }
4.write
客户端写入数据 write,但服务端不读取,未发生阻塞
public class SocketChannelDemo { public static void main(String[] args) throws IOException, InterruptedException { SocketChannel socketChannel = SocketChannel.open(); socketChannel.configureBlocking(false); // 开始连接 socketChannel.connect(new InetSocketAddress("127.0.0.1", 9999)); // 完成连接 // socketChannel.finishConnect(); while(!socketChannel.finishConnect()){ Thread.sleep(10); } // while(true){ // // 服务不停止 // } // 向缓冲区中写入数据 for(int i = 0 ; i< Integer.MAX_VALUE ; i++){ int outputDataCount = socketChannel.write(ByteBuffer.wrap("a".getBytes())); System.out.println("第"+i+"次输出,字节数:"+outputDataCount); } } }
客户端输出:
字节数:1
..
字节数:0
...
字节数的变化原因:客户端不停写入,但服务端不取,数据会放入pipe管道中,但管道也是有容量大小的,达到极限则不写入。
二、channel通信
1.
客户端发送数据,服务端接收并输出客户端发送的数据
服务端
public class ServerSocketChannelDemo { public static void main(String[] args) throws IOException { // 创建 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); // 设置通道为非阻塞模式 // 若不加,则处于阻塞模式,一直停留在accept serverSocketChannel.configureBlocking(false); // 绑定端口 serverSocketChannel.socket().bind(new InetSocketAddress(9999)); // 等待客户端连接 SocketChannel socketChannel = serverSocketChannel.accept(); while (socketChannel == null){ socketChannel = serverSocketChannel.accept(); } socketChannel.configureBlocking(false); // 非阻塞的,可能只读入了一部分数据 ByteBuffer byteBuffer = ByteBuffer.allocate(30);//改为客户端发送的字符长度,若改为10或低于30,会丢失接收内容; 如何确认传递内容的长度--粘包问题 // 30 为定值根据客户端输入的数据决定,不灵活 while(byteBuffer.hasRemaining()){ socketChannel.read(byteBuffer); } String clientSendContent = new String(byteBuffer.toString()); System.out.println("服务端接收客户端发送的数据:"+clientSendContent); while(true){ // 让服务端服务不停止 } } }
客户端:
public class SocketChannelDemo { public static void main(String[] args) throws IOException, InterruptedException { SocketChannel socketChannel = SocketChannel.open(); socketChannel.configureBlocking(false); // 开始连接 socketChannel.connect(new InetSocketAddress("127.0.0.1", 9999)); // 完成连接 // socketChannel.finishConnect(); while(!socketChannel.finishConnect()){ Thread.sleep(10); } // 客户端发送数据 // 不保证数据一定发送成功 String str = "hello nio , i am a boy , do you like me ?" ; ByteBuffer byteBuffer = ByteBuffer.wrap(str.getBytes()); while(byteBuffer.hasRemaining()){ // 保证数据全部写入通道 socketChannel.write(byteBuffer); } while(true){ } // 向缓冲区中写入数据 // for(int i = 0 ; i< Integer.MAX_VALUE ; i++){ // int outputDataCount = socketChannel.write(ByteBuffer.wrap("a".getBytes())); // System.out.println("第"+i+"次输出,字节数:"+outputDataCount); // } } }
运行结果:服务端输出正常
2.粘包问题
由于TCP传输是一种可靠的连续的数据传输,如果两次传输的数据时间间隔比较短,数据的接收方可能很难判断出两次数据的边界在哪里,感觉就好像两个数据黏着在了一次,无法区分。
TPC/IP协议:IP分发数据时,按包发送,小于包容量的多个内容会合在一起发送;大于包容量的内容拆分成多个包发送;
如何规定边界
方法 | 描述 | 缺点 |
发送定长的数据 | 若长度不足,空格补位 | 不灵活,浪费空间资源 |
发送指定的分割符 | 如规定#为分隔符 | 若发送内容包含#,还需要转译处理 |
带着发送数据的长度 | 发送内容前先将长度发送过去 | 发送内容变长 |
解决方式:
1.客户端
发送数据时带上此次发送数据的长度并用\r\n与真正需要发送的内容分割
格式为: str.length+\r\n+str
2.服务端
使用大小为1的缓冲区接收并解析头部信息,获取长度
根据长度设置内容缓冲区,读取指定内容
服务端
public class ServerSocketChannelDemo { public static void main(String[] args) throws IOException { // 创建 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); // 设置通道为非阻塞模式 // 若不加,则处于阻塞模式,一直停留在accept serverSocketChannel.configureBlocking(false); // 绑定端口 serverSocketChannel.socket().bind(new InetSocketAddress(9999)); // 等待客户端连接 SocketChannel socketChannel = serverSocketChannel.accept(); while (socketChannel == null){ socketChannel = serverSocketChannel.accept(); } socketChannel.configureBlocking(false); // 计算客户端传送的内容的长度 // 设置缓冲区长度为1,读取一个字符拼接一次,然后清空缓冲区,继续读取下一个 String head = ""; ByteBuffer temp = ByteBuffer.allocate(1); while(!head.endsWith("\r\n")){ socketChannel.read(temp); head += new String(temp.array()); temp.clear(); } // 如:124\r\n 去掉字符串后的分割符号 Integer len = Integer.valueOf(head.substring(0, head.length()-2)); System.out.println("客户端发送的数据内容的长度:"+len); // 非阻塞的,可能只读入了一部分数据 ByteBuffer byteBuffer = ByteBuffer.allocate(len.intValue());//改为客户端发送的字符长度 while(byteBuffer.hasRemaining()){ socketChannel.read(byteBuffer); } String clientSendContent = new String(byteBuffer.array()); System.out.println("服务端接收客户端发送的数据:"+clientSendContent); while(true){ // 让服务端服务不停止 } } }
客户端
public class SocketChannelDemo { public static void main(String[] args) throws IOException, InterruptedException { SocketChannel socketChannel = SocketChannel.open(); socketChannel.configureBlocking(false); // 开始连接 socketChannel.connect(new InetSocketAddress("127.0.0.1", 9999)); // 完成连接;一定要先确认连接完成,否则,执行到write时会异常 // socketChannel.finishConnect(); while(!socketChannel.finishConnect()){ Thread.sleep(10); } // 客户端发送数据 // 不保证数据一定发送成功 String str = "hello" ; StringBuffer sb = new StringBuffer(); sb.append(str.length() + "\r\n").append(str); ByteBuffer byteBuffer = ByteBuffer.wrap(sb.toString().getBytes()); while(byteBuffer.hasRemaining()){ // 保证数据全部写入通道 socketChannel.write(byteBuffer); } while(true){ } // 向缓冲区中写入数据 // for(int i = 0 ; i< Integer.MAX_VALUE ; i++){ // int outputDataCount = socketChannel.write(ByteBuffer.wrap("a".getBytes())); // System.out.println("第"+i+"次输出,字节数:"+outputDataCount); // } } }
三、DataGramChannel
- DataGramChannel 基于UDP
- SocketChannel 基于TCP
TCP | 传输慢 | 信息准确 |
UDP | 传输块 | 信息不准确 |
发送:
/** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { DatagramChannel server = DatagramChannel.open(); String content = "hello , UDP SEND " ; server.send(ByteBuffer.wrap(content.getBytes()), new InetSocketAddress("127.0.0.1",8899)); }
接收:
public static void main(String[] args) throws IOException { DatagramChannel client = DatagramChannel.open(); client.socket().bind(new InetSocketAddress(8899)); ByteBuffer byteBuffer = ByteBuffer.allocate(1024); client.receive(byteBuffer); String content = new String(byteBuffer.array(),0,byteBuffer.position()); System.out.println(content); }
四、FileChannel
阻塞式的,比BIO 中 File提供更多的方法
import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; /** * 阻塞式 * 比BIO提供更多的方法 */ public class FileChannelDemo { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { write(); read(); } /** * 读取第三个位置起得三个字符 * @throws IOException */ public static void read() throws IOException { FileInputStream in = new FileInputStream(new File("1.txt")); FileChannel channel = in.getChannel(); channel.position(3); ByteBuffer byteBuffer = ByteBuffer.allocate(3); channel.read(byteBuffer); String contentString = new String(byteBuffer.array()); System.out.println(contentString); channel.close(); } /** * 修改第三个位置起的三个字符 * @throws IOException */ public static void write() throws IOException{ // 使用FileOutPutStream 会覆盖原有信息 // FileOutputStream file = new FileOutputStream(new File("1.txt")); RandomAccessFile file = new RandomAccessFile(new File("1.txt"), "rw"); FileChannel channel = file.getChannel(); ByteBuffer byteBuffer = ByteBuffer.wrap("xyz".toString().getBytes()); channel.position(3); while(byteBuffer.hasRemaining()){ channel.write(byteBuffer); } channel.close(); file.close(); } }
1.txt
abcdefghijklmnopqrstuvwxyz
相关推荐
14-Java NIO-Buffer-三个属性和类型.mp4 17-Java NIO-Buffer-缓冲区分片.mp4 18-Java NIO-Buffer-只读缓冲区.mp4 19-Java NIO-Buffer-直接缓冲区.mp4 21-Java NIO-Selector-概述.mp4 23-Java NIO-Selector-示例代码...
NIO的核心概念包括通道(Channel)、缓冲区(Buffer)和选择器(Selector)。通道可以读写数据,缓冲区用于临时存储数据,选择器则用于监听多个通道的事件,实现了多路复用,从而实现非阻塞I/O。 3. **HttpCore NIO...
Java NIO (Non-blocking Input/Output) 是Java平台中用于高效处理I/O操作的一种机制,它与传统的IO模型( Blocking I/O)相比,提供了更高级别的抽象,允许应用程序以非阻塞的方式读写数据,提高了并发性能。...
- **Channel读写**:演示如何使用FileChannel读取和写入文件,以及如何通过SocketChannel进行网络通信。 - **Buffer操作**:展示如何创建、填充、清空和读取缓冲区,以及如何使用`flip()`、`clear()`和`compact()`...
1. **Channel**:通道是数据传输的双向通道,可以读取和写入数据。 2. **Selector**:选择器允许单个线程监控多个通道,当通道准备好进行读写操作时,选择器会通知我们。 3. **Buffer**:缓冲区用于存储数据,NIO中...
在Socket通信中,NIO主要通过Selector和Channel两个核心组件来实现。Selector负责监听多个Channel的读写事件,而Channel则代表了与操作系统进行I/O操作的通道。当数据准备好时,Selector会返回一个包含就绪通道的...
Java NIO-Buffer-三个属性和类型 - **主要内容**:详细解析Buffer的三个核心属性(容量、位置、限制)以及不同类型Buffer的特点。 - **学习目标**:掌握Buffer的内部机制和不同类型的Buffer。 #### 15. Java NIO-...
与传统的I/O模型(BIO)不同,NIO不依赖于线程池来处理并发连接,而是通过选择器(Selector)来轮询和监听通道(Channel)上的事件。这使得NIO在资源利用率和性能上有了显著提升。 在"socket-nio-single-reactor....
Java NIO 主要包括三个核心组成部分:**Channels**、**Buffers** 和 **Selectors**。 - **Channels**:在 Java NIO 中,所有的 I/O 操作都是从 Channel 开始的。Channel 类似于流,但是它们提供了比传统 Java IO 流...
NIO基于通道(Channel)和缓冲区(Buffer)进行数据传输,且提供了选择器(Selector)来监听多个通道的事件,实现了一对多的模型。这样,服务器端可以同时处理多个连接,提高了系统资源利用率。 压缩包中的`demo3.txt`...
Cresco-Agent-NIOChannel-插件是针对Java平台设计的一款扩展工具,主要目的是为了优化和增强Java NIO(Non-blocking I/O)通道的功能。在Java编程中,NIO是一种重要的I/O模型,它允许程序在处理多个输入/输出流时...
在Java NIO中,Selector与Channel、Buffer等组件一起,构建了一个强大的非阻塞I/O框架,为开发高性能的网络应用提供了强大的支持。 本教程“Java基础第27天-03.NIO-Selector.avi”应该会详细讲解这些概念,并通过...
相比之下,NIO通过选择器(Selector)和通道(Channel)提供了非阻塞的数据读写,允许单个线程处理多个连接,显著提高了性能。NIO-EPOOL模型是NIO模型的一种优化,通常结合ExecutorPool(执行器池)来管理线程,...
Java NIO(非阻塞I/O)是Java标准库中的一种I/O模型,与传统的BIO(阻塞I/O)模型相比,它提供了更高效、更灵活的I/O操作方式。Selector是Java NIO框架中的核心组件,它使得单个线程能够管理多个通道(Channels),...
6. **通道(Channel)**: 通道是数据进出的路径,如FileChannel、SocketChannel、ServerSocketChannel等。它们提供了读写数据的方法,并且可以与Selector配合工作,实现高效的并发I/O操作。 7. **选择器模式...
NIO基于通道(Channel)和缓冲区(Buffer),而非传统的流。通道可以从源头获取数据并将其写入目标,而缓冲区则作为数据的临时存储区域。NIO支持阻塞和非阻塞两种模式。在非阻塞模式下,用户线程可以不等待数据准备...
Java NIO 中的 Channel(通道)是连接到实体如硬件设备、文件、网络套接字等的开放连接,它能够执行多种 I/O 操作,如读取和写入。相较于传统的 Stream,Channel 提供了一些显著的优势,比如支持异步操作和双向数据...
1. **通道(Channel)**:通道类似于流,但它们可以同时进行读写操作,并且可以与多个缓冲区进行交互。 2. **缓冲区(Buffer)**:缓冲区是数据存储的主要结构,它提供了更有效的方式管理数据。 3. **选择器...
而NIO通过使用选择器(Selector)和通道(Channel)改变了这种模式,允许单个线程管理多个通道,从而实现了更高的并发性能。 在这个多用户聊天项目中,关键组件可能包括以下几个部分: 1. **服务器端**:服务器...