java NIO 学习笔记

项目组是做IM产品的,服务端当然用的是NIO技术做通信底层。但是一直都是对NIO有些理论的了解,没有实践,最近有空了,就实践了下NIO。

NIO,新IO,也称之为非阻塞IO。非阻塞是它跟传统IO的最重要的区别之一。传统IO用Socket进行通信,NIO则用channel进行消息交互。channel必须注册到selector上,把它感兴趣的事件告诉selector。这是个观察者模式的实现。可以这样描述channel和selector的关系,channel是火车轨道,selector是火车调度室。多个channel可以注册到同一个selector上,在一个线程了对这些channel进行调度。然而传统IO需要为每一个socket创建一个线程。这也是NIO的优势之一,不需要太多的线程。

NIO版本的服务端代码:

package cn.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

public class NioServer {
	private Selector	serverSelector;

	public NioServer initServer(int port) throws IOException {
		ServerSocketChannel serverChannel = ServerSocketChannel.open();
		// 非阻塞模式
		serverChannel.configureBlocking(false);
		// 绑定端口号
		serverChannel.socket().bind(new InetSocketAddress(port));
		this.serverSelector = Selector.open();
		serverChannel.register(serverSelector, SelectionKey.OP_ACCEPT);
		return this;
	}

	public void startListening() throws IOException {
		System.out.println("服务端启动成功!");
		while (true) {
			// 当注册的事件到达时,方法返回;否则,该方法会一直阻塞
			serverSelector.select();
			Iterator<SelectionKey> iterator = this.serverSelector.selectedKeys().iterator();
			while (iterator.hasNext()) {
				SelectionKey key = (SelectionKey) iterator.next();
				// 必须删除,否则下次遍历时还会遍历旧的key
				iterator.remove();
				if (key.isAcceptable()) {
					accept(key);
				} else if (key.isReadable()) {
					read(key);
				}
			}
		}
	}

	// 接受客户端的连接请求
	private void accept(SelectionKey key) throws IOException {
		ServerSocketChannel server = (ServerSocketChannel) key.channel();
		// 获得和客户端连接的通道
		SocketChannel channel = server.accept();
		// 设置成非阻塞
		channel.configureBlocking(false);
		channel.write(ByteBuffer.wrap(new String("客户端连接成功\n").getBytes()));
		// 注册
		channel.register(this.serverSelector, SelectionKey.OP_READ);
	}

	private void read(SelectionKey key) {
		try {
			SocketChannel channel = (SocketChannel) key.channel();
			// 创建读取的缓冲区
			ByteBuffer buffer = ByteBuffer.allocate(1024);
			channel.read(buffer);
			String msg = new String(buffer.array()).trim();
			System.out.println("服务端:" + msg);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) throws IOException {
		new NioServer().initServer(8080).startListening();
	}
}

NIO版本的客户端代码:

package cn.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

public class NioClient {

	private Selector	selector;

	public NioClient initClient(String ip, int port) throws IOException {
		SocketChannel channel = SocketChannel.open();
		// 非阻塞
		channel.configureBlocking(false);
		this.selector = Selector.open();

		// connect方法的注释:此方法返回 false,并且必须在以后通过调用 finishConnect 方法来完成该连接操作。
		boolean result = channel.connect(new InetSocketAddress(ip, port));
		System.out.println(result); // 返回false
		// 注册
		channel.register(selector, SelectionKey.OP_CONNECT);
		return this;
	}

	public void startListening() throws IOException {
		while (true) {
			selector.select();
			Iterator<SelectionKey> iterator = this.selector.selectedKeys().iterator();
			while (iterator.hasNext()) {
				SelectionKey key = (SelectionKey) iterator.next();
				// 必须删除,否则下次遍历时还会遍历旧的key
				iterator.remove();
				if (key.isConnectable()) {
					connect(key);
				} else if (key.isReadable()) {
					read(key);
				}
			}
		}
	}

	private void connect(SelectionKey key) throws IOException, ClosedChannelException {
		SocketChannel channel = (SocketChannel) key.channel();
		// 如果正在连接,则完成连接
		if (channel.isConnectionPending()) {
			channel.finishConnect();
		}
		channel.configureBlocking(false);
		channel.write(ByteBuffer.wrap(new String("客户端连接成功").getBytes()));
		// 注册
		channel.register(this.selector, SelectionKey.OP_READ);
	}

	private void read(SelectionKey key) throws IOException {
		// 服务器可读取消息:得到事件发生的Socket通道
		SocketChannel channel = (SocketChannel) key.channel();
		// 创建读取的缓冲区
		ByteBuffer buffer = ByteBuffer.allocate(1024);
		channel.read(buffer);
		String msg = new String(buffer.array()).trim();
		System.out.println("客户端收到信息:" + msg);
	}

	/**
	 * 启动客户端测试
	 *
	 * @throws IOException
	 */
	public static void main(String[] args) throws IOException {
		new NioClient().initClient("localhost", 8080).startListening();
	}

}

考虑到其他语言不一定有NIO技术,我们的客户端都是C++的。那么一个NIO服务端是否可以和一个非NIO的客户端进行通信呢?做了一个传统IO的客户端,做测试。可行!

package cn.nio;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

public class NormalClient {

	Socket	clientSocket	= null;

	public NormalClient initClient(String ip, int port) {
		try {
			clientSocket = new Socket(ip, port);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return this;
	}

	public void read() throws IOException {
		PrintWriter pw = new PrintWriter((clientSocket.getOutputStream()));
		BufferedReader br = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
		String line = null;
		while (true) {
			pw.println("客户端发来的消息");
			pw.flush();
			line = br.readLine();
			System.out.println(line);
		}
	}

	public static void main(String[] args) throws IOException {
		new NormalClient().initClient("127.0.0.1", 8000).read();
	}
}
时间: 2024-10-07 15:24:14

java NIO 学习笔记的相关文章

java nio学习笔记(一)

位置保留,待用 java nio学习笔记(一),布布扣,bubuko.com

Java NIO学习笔记(一)

文章目录: 1.什么是IO 2.什么是Java NIO 3.I/O常见概念 4.为什么使用NIO 5.IO VS NIO 一.什么是IO I/O 或者输入/输出 , 指的是计算机与外部世界或者一个程序与计算机的其余部分的之间的接口.它对于任何计算机系统都非常关键,因而所有 I/O 的主体实际上是内置在操作系统中的.单独的程序一般是让系统为它们完成大部分的工作.在 Java 编程中,直到最近一直使用 流 的方式完成 I/O.所有 I/O 都被视为单个的字节的移动,通过一个称为 Stream 的对象

Java NIO学习笔记

NIO非堵塞应用通常适用用在I/O读写等方面,我们知道,系统运行的性能瓶颈通常在I/O读写,包括对端口和文件的操作上,过去,在打开一个I/O通道后,read()将一直等待在端口一边读取字节内容,如果没有内容进来,read()也是傻傻的等,这会影响我们程序继续做其他事情,那么改进做法就是开设线程,让线程去等待,但是这样做也是相当耗费资源的. Java NIO非堵塞技术实际是采取Reactor模式,或者说是Observer模式为我们监察I/O端口,如果有内容进来,会自动通知我们,这样,我们就不必开启

Java NIO学习笔记八 DatagramChannel

Java NIO DatagramChannel Java NIO DatagramChannel是可以发送和接收UDP数据包的通道.由于UDP是一种无连接网络协议,因此您不能默认读取和写入DatagramChannel其他通道.而是发送和接收数据包. 打开DatagramChannel 打开一个DatagramChannel代码: DatagramChannel channel = DatagramChannel.open(); channel.socket().bind(new InetSo

Java NIO学习笔记七 Non-blocking Server

Java NIO:Non-blocking Server 即使你了解了Java NIO非阻塞功能的工作(怎么样Selector,Channel, Buffer等等),设计一个无阻塞服务器仍然很难.非阻塞IO包含了相比阻塞IO的要有难度.本章非阻塞服务器教程将讨论非阻塞服务器的主要挑战,并为他们描述一些潜在的解决方案. 本教程中描述的想法是围绕Java NIO设计的.但是,我认为,只要有这样的构造,这些想法可以用其他语言重复使用Selector.据我所知,这样的结构是由底层操作系统提供的,所以很有

java NIO 学习笔记(一)

相关概念:缓冲区和通道 解释:标准的IO流是基于字节流和字符流的而NIO是基于通道和缓冲区的,数据总是从通道读取到缓冲区或者从缓冲区读取到通道的. 相关概念:非阻塞IO 解释:当线程从通道读取数据到缓冲区时线程同事还可以进行其他的事情. 相关概念:selectors(选择器) 解释:单个线程可以监听多个通信通道.

NIO学习笔记1

NIO引入了三个概念: Buffer 缓冲区 Channel 通道 selector 选择器 1.java.io优化建议 操作系统与Java基于流的I/O模型有些不匹配.操作系统要移动的是大块数据(缓冲区),这往往是在硬件直接存储器存取(DMA)的协助下完成的.I/O类喜欢操作小块数据——单个字节.几行文本.结果,操作系统送来整缓冲区的数据,java.io的流数据类再花大量时间把它们拆成小块,往往拷贝一个小块就要往返于几层对象.操作系统喜欢整卡车地运来数据,java.io类则喜欢一铲子一铲子地加

JAVA基础学习笔记(2)

看了几天的视频了,都没时间来写下学习笔记,今天来写下第二次的学习笔记,前几天看的给忘记了,就写最新看到的吧 主要内容:1.类的变量与函数(方法) 2.对象的存储方式 3.新建一个对象及对象的赋值与调用 4.空对象 5.匿名对象 1.类的变量与函数(方法) class Dog      //类名 { String name;  //变量的声明 int age; String color; void bark()   //方法的定义(返回值为空,不带参数) { System.out.println(

java排序学习笔记

前面写了js的排序实现,总得玩玩java的哈. 同样,冒泡.选择.快速(这三个之前实现过也写过文章).堆排序,然后做比较. 主要遇到的难点: - -||想轻松点写个封装计时的逻辑,不想每调用一个排序就要写一个计时代码.想想,还是javascript写起来方便: java的话,我想到的方法是写一个抽象类:抽象出排序方法,实现一个排序计时方法(该方法调用了抽象排序,但在先后排序时加入计时代码[感觉像是aop操作]): 接着所有排序类都继承这个抽象类,并实现排序方法,调用的时候直接调用继承的排序计时方