`

(三)NIO-Channel

    博客分类:
  • NIO
阅读更多
一、
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

分享到:
评论

相关推荐

    Java NIO实战开发多人聊天室

    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-示例代码...

    httpcore-nio-4.3.jar包

    NIO的核心概念包括通道(Channel)、缓冲区(Buffer)和选择器(Selector)。通道可以读写数据,缓冲区用于临时存储数据,选择器则用于监听多个通道的事件,实现了多路复用,从而实现非阻塞I/O。 3. **HttpCore NIO...

    Java-NIO-Netty框架学习

    Java NIO (Non-blocking Input/Output) 是Java平台中用于高效处理I/O操作的一种机制,它与传统的IO模型( Blocking I/O)相比,提供了更高级别的抽象,允许应用程序以非阻塞的方式读写数据,提高了并发性能。...

    JAVA-NIO-DEMO

    - **Channel读写**:演示如何使用FileChannel读取和写入文件,以及如何通过SocketChannel进行网络通信。 - **Buffer操作**:展示如何创建、填充、清空和读取缓冲区,以及如何使用`flip()`、`clear()`和`compact()`...

    基于matrix-nio和nio-template构建的管理员和个人协助.zip

    1. **Channel**:通道是数据传输的双向通道,可以读取和写入数据。 2. **Selector**:选择器允许单个线程监控多个通道,当通道准备好进行读写操作时,选择器会通知我们。 3. **Buffer**:缓冲区用于存储数据,NIO中...

    android-socket-nio-master.zip

    在Socket通信中,NIO主要通过Selector和Channel两个核心组件来实现。Selector负责监听多个Channel的读写事件,而Channel则代表了与操作系统进行I/O操作的通道。当数据准备好时,Selector会返回一个包含就绪通道的...

    java网络编程NIO视频教程

    Java NIO-Buffer-三个属性和类型 - **主要内容**:详细解析Buffer的三个核心属性(容量、位置、限制)以及不同类型Buffer的特点。 - **学习目标**:掌握Buffer的内部机制和不同类型的Buffer。 #### 15. Java NIO-...

    socket-nio-single-reactor.zip

    与传统的I/O模型(BIO)不同,NIO不依赖于线程池来处理并发连接,而是通过选择器(Selector)来轮询和监听通道(Channel)上的事件。这使得NIO在资源利用率和性能上有了显著提升。 在"socket-nio-single-reactor....

    Java-NIO-系列教程

    Java NIO 主要包括三个核心组成部分:**Channels**、**Buffers** 和 **Selectors**。 - **Channels**:在 Java NIO 中,所有的 I/O 操作都是从 Channel 开始的。Channel 类似于流,但是它们提供了比传统 Java IO 流...

    bio-nio-aio.zip

    NIO基于通道(Channel)和缓冲区(Buffer)进行数据传输,且提供了选择器(Selector)来监听多个通道的事件,实现了一对多的模型。这样,服务器端可以同时处理多个连接,提高了系统资源利用率。 压缩包中的`demo3.txt`...

    Cresco-Agent-NIOChannel-Plugin:Cresco-Agent-NIOChannel-插件

    Cresco-Agent-NIOChannel-插件是针对Java平台设计的一款扩展工具,主要目的是为了优化和增强Java NIO(Non-blocking I/O)通道的功能。在Java编程中,NIO是一种重要的I/O模型,它允许程序在处理多个输入/输出流时...

    【IT十八掌徐培成】Java基础第27天-03.NIO-Selector.zip

    在Java NIO中,Selector与Channel、Buffer等组件一起,构建了一个强大的非阻塞I/O框架,为开发高性能的网络应用提供了强大的支持。 本教程“Java基础第27天-03.NIO-Selector.avi”应该会详细讲解这些概念,并通过...

    基于NIO-EPOOL模型netty实现的具备一致性哈希算法的NAT端口映射器

    相比之下,NIO通过选择器(Selector)和通道(Channel)提供了非阻塞的数据读写,允许单个线程处理多个连接,显著提高了性能。NIO-EPOOL模型是NIO模型的一种优化,通常结合ExecutorPool(执行器池)来管理线程,...

    swift-nio-ssl:对 SwiftNIO 的 TLS 支持,基于 BoringSSL

    这些中的每一个都可以添加到Channel以保护该Channel上的通信。 此外,我们提供了许多用于配置 TLS 连接的低级原语。 这些将在下面显示。 为了保护服务器连接,您需要一个文件中的 X.509 证书链(PEM 或 DER,但 PEM...

    Java_NIO-Selector.rar_java nio_selector

    Java NIO(非阻塞I/O)是Java标准库中的一种I/O模型,与传统的BIO(阻塞I/O)模型相比,它提供了更高效、更灵活的I/O操作方式。Selector是Java NIO框架中的核心组件,它使得单个线程能够管理多个通道(Channels),...

    【IT十八掌徐培成】Java基础第27天-02.NIO-ServerSocketChannel-SocketChannel.zip

    6. **通道(Channel)**: 通道是数据进出的路径,如FileChannel、SocketChannel、ServerSocketChannel等。它们提供了读写数据的方法,并且可以与Selector配合工作,实现高效的并发I/O操作。 7. **选择器模式...

    Java中的IO与NIO-jiava求职面试-15题,答案

    NIO基于通道(Channel)和缓冲区(Buffer),而非传统的流。通道可以从源头获取数据并将其写入目标,而缓冲区则作为数据的临时存储区域。NIO支持阻塞和非阻塞两种模式。在非阻塞模式下,用户线程可以不等待数据准备...

    Java-NIO之Channel(通道).doc

    Java NIO 中的 Channel(通道)是连接到实体如硬件设备、文件、网络套接字等的开放连接,它能够执行多种 I/O 操作,如读取和写入。相较于传统的 Stream,Channel 提供了一些显著的优势,比如支持异步操作和双向数据...

    JAVA IO and NIO

    1. **通道(Channel)**:通道类似于流,但它们可以同时进行读写操作,并且可以与多个缓冲区进行交互。 2. **缓冲区(Buffer)**:缓冲区是数据存储的主要结构,它提供了更有效的方式管理数据。 3. **选择器...

Global site tag (gtag.js) - Google Analytics