Java NIO学习笔记(三)

版权声明:本文为博主原创文章,转载请注明出处! https://blog.csdn.net/q1406689423/article/details/87981243

之前两篇文章写了关于NIO的博客,本来想一一口气写完这三篇的,但是由于前段时间项目特别赶,就把这篇给放下了,现在算是有些时间了,把第三篇写了。

前两篇一篇是写了Buffer、一篇写了Channel,下面写NIO的第三个很重要的部分Selector。

什么是Selector

在nio出现之前,也就是jdk1.4以前要实现网络通信的话使用socket进行,使用socket进行网络通信就会出现网络通信中IO阻塞的情况,显然这时候要让服务器面对很多连接同时进行会很吃力。NIO中的Selector就是针对这种情况推出的一种新的、非阻塞的IO方式。

常用概念

Selector有以下常用方法

  • public static Selector open():打开一个选择器
  • public abstract int select():选择一组Key,说明channel已经做好了IO的准备
  • public abstract Set selectedKeys:返回已选中的Key

在进行阻塞式IO的时候是通过Socket获得IO流,在创建非阻塞的时候则是使用Selector创建上一篇写的Channel了。具体用到的类为ServerSocketChannel和SocketChannel。这两个类的父类是SelectableChannel,该类可以用来声明阻塞的模式,以及用来注册Selector的方法。
ServerSocketChannel的常用方法

  • public final SelectableChannel configureBlocking(boolean block):这个方法用来配置阻塞模式,true为阻塞,false为非阻塞
  • public final SelectionKey register(Selector sel, int ops):这个方法用来向选择器注册通道,并返回一个SelectionKey对象
  • public static ServerSocketChannel open():这个方法用来打开一个服务器Chanel并返回
  • public abstract ServerSocket socket():这个方法用来返回与通道相关的Socket

在Socket的网络通信方式中,我们使用的是accept()和connect()这样的方法来声明socket接收或者连接。Selector使用了不同的方式,它是在调用register()方法时,通过传递的第二个参数来判断进行哪种操作的,这第二个参数是int类型,可以把它叫做域,Selector域包含了一下几种:

  • OP_ACCEPT:接收状态,相当于socket调用accept()方法
  • OP_CONNECT:连接状态,相当于调用connect()方法
  • OP_READ:进行读操作
  • OP_WRITE:进行写操作

示例

下面写一个非阻塞的服务端实例来看一下

public static void main(String[] args) throws IOException {
	int[] ports = { 8081, 8082, 8083, 8084, 8085 };// 列出一系列端口号,都可以用来接收请求
	Selector selector = Selector.open();// 创建新的Selector
	for (int i = 0; i < ports.length; i++) {
		ServerSocketChannel ssc = null;// 声明ServerSocketChannel
		ssc = ServerSocketChannel.open();// 通过ServerSocketChannel的open方法打开服务器套接字通道
		ssc.configureBlocking(false);// 配置服务器为非阻塞状态
		ServerSocket serverSocket = ssc.socket();// 检索与通道相关联的服务器套接字
		InetSocketAddress inetSocketAddress = new InetSocketAddress(
					"127.0.0.2", ports[i]);// 实例化绑定地址
		serverSocket.bind(inetSocketAddress);// 套接字与网络地址进行绑定
		ssc.register(selector, SelectionKey.OP_WRITE);// 注册选择器,类似Socket的accept()方法
		System.out.println("服务器在" + ports[i]);
	}
}

这样运行之后就可以看到控制台打印出来以下消息

服务器在8081
服务器在8082
服务器在8083
服务器在8084
服务器在8085

也就是说明在上面几个端口都在监听请求了。
服务器要想和客户端进行通信还需要用到另一个类SelectionKey,这个类可以用来判断连接状态以及发送消息。
它有以下常用方法:

  • public abstract SelectableChannel channel():返回一个用来通信的Channel
  • public final boolean isAcceptable():判断是否可接收新连接
  • public final boolean isConnectable():判断是否连接已完成
  • public final boolean isReadable():判断Channel是否已经处于可读状态
  • public final boolean isWritable():判断Channel是否已经处于可写状态

直接使用代码来说明,再之前的方法里面加上以下代码

while (iter.hasNext()) {
	SelectionKey key = iter.next();// 获得SelectionKey实例
	if (key.isAcceptable()) {// 如果SelectionKey的状态处于接收状态
		ServerSocketChannel server = (ServerSocketChannel) key
				.channel();// 使用key的channel()方法获得服务器套接字通道
		SocketChannel client = server.accept();// 通过调用ServerSocketChannel的accept()方法,获得套接字通道SocketChannel对象
		client.configureBlocking(false);// 设置套接字通道为非阻塞形式
		ByteBuffer outBuf = ByteBuffer.allocate(1024);// 为缓冲区分配大小为1024
		outBuf.put(("现在是:" + new SimpleDateFormat("yyyy-MM-dd")
				.format(new Date())).getBytes());// 将当前日期存放到缓冲区
		outBuf.flip();// 重设缓冲区指针
		client.write(outBuf);// 通过套接字通道将缓冲区的输入写入
		client.close();// 关闭通道
	}
}

这样再运行代码,当有请求发来时,就向请求的客户端发送一个当前的时间。再新建一个类,在main方法里写下如下代码来验证一下

public static void main(String[] args) throws UnknownHostException,
			IOException {
	Socket client = new Socket("127.0.0.2", 8082);
	BufferedReader buf = null;
	buf = new BufferedReader(new InputStreamReader(client.getInputStream()));
	String str = buf.readLine();
	System.out.println("服务器端输出内容:" + str);
	client.close();
	buf.close();
}

控制台打印结果为

服务器端输出内容:现在是:2019-02-27

这样就证明创建的非阻塞服务器的可以使用了,并且进行相应的通信了。NIO的几个重要概念和使用方法也就介绍完了!

猜你喜欢

转载自blog.csdn.net/q1406689423/article/details/87981243