NIO搭建服务器端-ServerSocketChannel
如果你看完了上一篇日志,相信你已经万事俱备,搞明白通道Channel、数据包装Buffer和选择器Selector。那么就来用上这些组件,搭建一个非阻塞式的,更高性能的服务器端和客户端吧!
首先来看看最核心的服务器部分:
package com.justin.nioclient;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class NIOclient implements Runnable {
private Selector selector;
protected void initClient(int port, InetAddress ipAddress) throws IOException {
// 配置要连接的远程服务器地址和端口号
InetSocketAddress address = new InetSocketAddress(ipAddress, port);
SocketChannel channel = SocketChannel.open(); //打开通道
channel.configureBlocking(false); // 设置为非阻塞式
channel.connect(address); // connect()方法连接远程服务器
this.selector = Selector.open(); // 创建选择器
channel.register(selector, SelectionKey.OP_CONNECT); // 注册连接事件
}
protected void connectServer() throws IOException {
while (true) {
if(selector.isOpen() == false) {
break;
}
selector.select(); //查找连接事件的通道
Set connectKeySet = selector.selectedKeys();
Iterator iterator = connectKeySet.iterator(); //迭代器遍历通道集合
while (iterator.hasNext()) {
SelectionKey availableKey = (SelectionKey) iterator.next();
iterator.remove();
if(availableKey.isConnectable()) {
System.out.println("Search connectable channel success!");
new clientConnect().Connect(selector, availableKey);
} else if(availableKey.isValid() && availableKey.isWritable()) {
System.out.println("Search writable channel success!");
new clientWrite().Write(selector, availableKey);
} else if(availableKey.isValid() && availableKey.isReadable()) {
System.out.println("Search readable channel ssuccess!");
new clientRead().Read(selector, availableKey);
}
}
}
}
@Override
public void run() {
System.out.println("Start client.");
NIOclient client = new NIOclient();
try {
client.initClient(5000, InetAddress.getLocalHost());
client.connectServer();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws UnknownHostException, IOException {
Thread clientThread = new Thread(new NIOclient());
clientThread.start();
}
}
代码第7行,打开一个ServerSocketChannel,这是一个可以监听TCP连接的通道,相当于IO中的ServerSocket,在第8行设置这个通道为非阻塞式后,第12行我们会给它绑定主机地址和端口号。最后在第14行为该通道Channel注册接收客户端连接事件,并让selector来管理这个通道,可以看到,register()的返回值是一个SelectionKey对象,上一篇日志说了,对象里面存放了通道,被管理的选择器和通道的注册事件等。
接着就可以不断监听这个端口,等待新的客户端连接进来。和ServerSocket一样,ServerSocketChannel也有一个accept()方法,调用accept()方法后,不同的是,ServerSocket的accept()方法会一直阻塞,知道有新的连接请求,而ServerSocketChannel可以设置成非阻塞式的,上面我们已经设置了。非阻塞式的ServerScketChannel调用accept()方法,会立刻返回,如果没有新的连接请求,则返回null。
监听到客户端的请求,之后第17行就可以调用Selector.select()方法,来返回获得那些对服务器端或客户端想要执行的事件已就绪的通道,不过要注意的是