一前言
以往服务端在读写文件时,都是对某个文件进行独占资源进行读取,如果这个操作没有完成,其他服务和请求无法对资源访问,阻塞其他请求和服务执行,显而易见,严重影响了服务器的处理,NIO中Seletor,channel,FileLock的出现,为服务器读取文件资源的性能提供了很大的改变。接下介绍Selector。
二 理论
以往的服务端I/O是通过读取本地文件,阻塞式访问。自从出现NIO后,可以建立服务器套接字连接通道,设置为非阻塞 传输模式,通过服务端套接字绑定服务的地址,服务器套接字通道设置选择其键值。在设置后,启动服务器端口,获取所有服务器端口的选择器的键,然后对每个端口的所有键值(已连接,已接受,可读,可写)进行迭代,在每个端口获取对应的键值状态后,进行相应操作服务区。调用SocketChannel.read()。该方法将数据从SocketChannel 读到Buffer中。read()方法返回的int值表示读了多少字节进Buffer里。如果返回的是-1,表示已经读到了流的末尾(连接关闭了)。
package com.myd.cn.Nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
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.Date;
import java.util.Iterator;
import java.util.Set;
public class ServerSocketChannelDemo {
public static void main(String[] args) throws Exception {
//设置服务器通道(初始化:端口,通道非阻塞,地址,设定seletor属性
//指定端口
int serverPorts[] = {8000,8009,8090};
//通过selector.open()获取selector
Selector selector = Selector.open();
for (int i = 0; i < serverPorts.length; i++) {
//设置服务器通道实例
ServerSocketChannel initServer = null;
//打开服务器通道
initServer = ServerSocketChannel.open();
//设置服务器通道为非阻塞,就可用异步模式调用read(),write(),connect()
initServer.configureBlocking(false);
//设置服务器端套接字,用来下方的服务器与连接服务的绑定
ServerSocket serverSocket = new ServerSocket();
//实力化绑定IP,端口地址
InetSocketAddress inaAddress = new InetSocketAddress(serverPorts[i]);
//进行服务绑定,对应特定连接服务
serverSocket.bind(inaAddress);
//设置selector键值为接受连接
initServer.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("服务器运行,在" + serverPorts[i] + "端口监听。") ;
}
//判断各个服务器端口是否已被选择,获取selector的键值,并判断他们的键值(是否已接受,已连接,可读的,可写的)
//进而进行详细的通过服务器通道写入服务端操作
int keyAdd = 0;
//判断各个服务器端口是否已被选择
while ((keyAdd=selector.select())>0) {
Set<SelectionKey> keys = selector.keys();
//迭代器键值集合,进行对应服务的处理(接受,连接,读写)
Iterator<SelectionKey> iterator = keys.iterator();
while (iterator.hasNext()) {
//已被选择获取selector的键值,并判断他们的键值(是否已接受,已连接,可读的,可写的)
SelectionKey key = iterator.next();
if(key.isAcceptable()){
//如果已连接,则进行相应的操作
//通过key获取服务器套接字通道
ServerSocketChannel server = (ServerSocketChannel) key.channel();
//创建客户端套接字连接,亦为客户端连接实例 接受新连接,在返回套接字通道之前,会一直阻塞,一直到有新的连接达到
SocketChannel client = server.accept();
//设置通道为非阻塞
server.configureBlocking(false);
//进行写入操作,缓存区
ByteBuffer buf = ByteBuffer.allocateDirect(1024); //设置直接缓存区,进行快速读写
//放入数据
buf.put(("当前时间为:"+new Date()).getBytes()); //I/O以字节处理
//flip()重置操作,重新指定position(放入数据后,postion==limit ,limit<=capacity)
buf.flip();
//输出并写入内容 至客户端
client.write(buf);
//关闭缓存
buf.clear();
client.close();
}
}
keys.clear(); //清楚selector键值
}
}
}
客户端向服务器端的连接,可以自己写应用程序,也可以通过Telnet。
telnet localhost:8080
三总结
总的来说,选择器的使用,提高了服务器处理业务的性能,由以往阻塞式,本地文件读取转完成非阻塞式,缓存读取文件内容的转换,明显的提高了服务器的I/O处理性能。