NIO实现IO多路复用,不用再为每个IO链接创建一个进程。
来看一下Server和Client端创建一个NIO连接并通信时各自需要做的步骤。
Server端:
1.创建NIO channels,由于是服务器端,用的是ServerSocketChannel。
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
这样创建一个ServerSocketChannel对象,并设置blocking属性。
NIO Channel有很多种,但先不在这里一一解释。NIO channels种类有:
- java.nio.channels.Channel
- java.nio.channels.FileChannel
- java.nio.channels.SocketChannel
- java.nio.channels.ServerSocketChannel
2.利用ServerSocketChannel.socket()方法获取一个ServerSocket。然后用bind方法绑定服务器地址给SocketServer。
ServerSocket serverSocket = serverSocketChannel.socket();
serverSocket.bind(new InetSocketAddress(port));
这一步之后ServerSocketChannel就已经有了自己的ServerSocket了
3.还需要把一个selector注册到ServerSocketChannel中去
Selector selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);//OP_ACCEPT这个表示对接收连接感兴趣。
这里的SelectionKey表示Selector和被注册的channel之间关系,一份凭证。SelectionKey保存channel感兴趣的事件。
4.Selector.select()阻塞等待。有消息的话,就更新所有SelectionKey的状态。
selector.select(); //等待有状态变更
Set<SelectionKey> selectionKeys = selector.selectedKeys();//已经更新了selector里边的所有SelectionKey。这里返回其集合,并利用下面的迭代器轮询所有更新的SelectionKey。
Iterator<SelectionKey> iterator = selectionKeys.iterator();
5.
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next(); //提取已经被更新的SelectionKey
iterator.remove();//这里将上面返回的SelectionKey从迭代器中删除!!不删除下次取出来还是这个~~
if (selectionKey.isAcceptable()) {//看这个SelectionKey的时间是不是OP_ACCEPT。Client第一次连接就会是OP_ACCEPT。
System.out.println("Server: selectionKey isAcceptable");
server = (ServerSocketChannel) selectionKey.channel();//这个ServerSocketChannel就是上面定义的serverSocketChannel。其实可以用一个类成员,就不用这样重新取出来了。
client = server.accept(); //ServerSocketChannel接收一个连接,并返回一个SocketChannel。不管有多少个连接过来,Server这边都只有一个SocketChannel!!。我们需要把这个SocketChannel重新注册到selector中,用这个检查READ事件。所以这里很清楚了,就是ServerSocketChannel只用来检查是否有Client连接来。来了之后就从accetp获得一个SocketChannel重新注册给selector。然后在这里检查是否有字符发过来!!。
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ);//注册连接的SocketChannel给selector,检查是否有发过来东西。
} else if (selectionKey.isReadable()) {
System.out.println("Server: selectionKey isReadable");
client = (SocketChannel) selectionKey.channel();
receivebuffer.clear();//receivebuffer是ByteBuffer.allocate(2048)分配的一个接收用的buffer
int count = client.read(receivebuffer);
receiveText = new String( receivebuffer.array(),0,count);
System.out.println("服务器端接受客户端数据--:"+receiveText);
} else if (selectionKey.isWritable()) {
System.out.println("Server: selectionKey isWritable");
}
}
Client端:
1.初始化一个SocketChannel,设置block属性等。这个和服务器端的第一步对应。
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
2.初始化一个selector,并注册SocketChannel。这个和服务器的第三步对应
Selector selector = Selector.open();
socketChannel.register(selector, SelectionKey.OP_CONNECT);
3.连接服务器。
InetSocketAddress SERVER_ADDRESS = new InetSocketAddress(
"localhost", 8080);
socketChannel.connect(SERVER_ADDRESS);
//这个和服务器端serverSocketChannel.socket()来获取serversocket以及bind相对应。没有这个就不能有连接
4.
selector.select();
Set<SelectionKey> selectionKeys;
Iterator<SelectionKey> iterator;
selectionKeys = selector.selectedKeys();
iterator = selectionKeys.iterator();
5.
while (iterator.hasNext()) {
selectionKey = iterator.next();
if (selectionKey.isConnectable()) {
System.out.println("Client:selectionKey isConnectable");
client = (SocketChannel) selectionKey.channel();
// 判断此通道上是否正在进行连接操作。
// 完成套接字通道的连接过程.
if (client.isConnectionPending()) {
client.finishConnect();
System.out.println("完成连接!");
}
client.register(selector, SelectionKey.OP_WRITE);
} else if (selectionKey.isReadable()) {
System.out.println("Client:selectionKey isReadable");
} else if (selectionKey.isWritable()) {
sendbuffer.clear();
client = (SocketChannel) selectionKey.channel();
readbuffer = in.readLine();
sendbuffer.put(readbuffer.getBytes());
//将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位
sendbuffer.flip();
client.write(sendbuffer);
System.out.println("客户端向服务器端发送数据--:"+readbuffer);
//client.register(selector, SelectionKey.OP_READ);
}
}
例子
Server端:
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.Iterator;
import java.util.Set;
public class java_nio_keyinputtransmit_server {
private Selector selector;
private int BLOCK = 4096;
private ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK);
private ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK);
public java_nio_keyinputtransmit_server(int port) throws IOException{
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
ServerSocket serverSocket = serverSocketChannel.socket();
serverSocket.bind(new InetSocketAddress(port));
selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("Server Start----8080:");
}
private void listen() throws IOException {
System.out.println("Server: listen started");
ServerSocketChannel server = null;
SocketChannel client = null;
String receiveText;
String sendText;
while (true) {
selector.select();
System.out.println("Server: lisen selector");
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
iterator.remove();
if (selectionKey.isAcceptable()) {
System.out.println("Server: selectionKey isAcceptable");
server = (ServerSocketChannel) selectionKey.channel();
client = server.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ);
} else if (selectionKey.isReadable()) {
System.out.println("Server: selectionKey isReadable");
client = (SocketChannel) selectionKey.channel();
receivebuffer.clear();
int count = client.read(receivebuffer);
receiveText = new String( receivebuffer.array(),0,count);
System.out.println("服务器端接受客户端数据--:"+receiveText);
//client.register(selector, SelectionKey.OP_WRITE);
} else if (selectionKey.isWritable()) {
System.out.println("Server: selectionKey isWritable");
}
//handleKey(selectionKey);
}
}
}
public static void main(String args[]) throws IOException {
int port = 8080;
java_nio_keyinputtransmit_server server = new
java_nio_keyinputtransmit_server(port);
server.listen();
}
}
Client端
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
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 java_nio_keyinputtransmit_client{
private static int flag = 0;
private static int BLOCK = 4096;
private static ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK);
private static ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK);
private final static InetSocketAddress SERVER_ADDRESS = new InetSocketAddress(
"localhost", 8080);
public static void main(String args[]) throws IOException {
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
Selector selector = Selector.open();
socketChannel.register(selector, SelectionKey.OP_CONNECT);
socketChannel.connect(SERVER_ADDRESS);//连接之后,服务期select.selector()阻塞解除,开始下面的流程
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String readbuffer = null;
//readbuffer = in.readLine();
//while(!readbuffer.equals("END")){
while(true) {
//System.out.println("readbuffer is "+readbuffer );
//System.out.println("Client:selector select begin");
selector.select();
//System.out.println("Client:selector select end");
Set<SelectionKey> selectionKeys;
Iterator<SelectionKey> iterator;
SelectionKey selectionKey;
SocketChannel client;
String receiveText;
String sendText;
selectionKeys = selector.selectedKeys();
iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
selectionKey = iterator.next();
if (selectionKey.isConnectable()) {
System.out.println("Client:selectionKey isConnectable");
client = (SocketChannel) selectionKey.channel();
// 判断此通道上是否正在进行连接操作。
// 完成套接字通道的连接过程。
if (client.isConnectionPending()) {
client.finishConnect();
System.out.println("完成连接!");
sendbuffer.clear();
sendbuffer.put("Hello,Server".getBytes());
sendbuffer.flip();
client.write(sendbuffer);
}
client.register(selector, SelectionKey.OP_WRITE);
} else if (selectionKey.isReadable()) {
System.out.println("Client:selectionKey isReadable");
} else if (selectionKey.isWritable()) {
sendbuffer.clear();
client = (SocketChannel) selectionKey.channel();
readbuffer = in.readLine();
sendbuffer.put(readbuffer.getBytes());
//将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位
sendbuffer.flip();
client.write(sendbuffer);
System.out.println("客户端向服务器端发送数据--:"+readbuffer);
//client.register(selector, SelectionKey.OP_READ);
}
}
selectionKeys.clear();
//readbuffer = in.readLine();
}
//System.out.println("Client End");
/*BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String readbuffer = null;
try {
readbuffer = in.readLine();
System.out.println(readbuffer);
while(!readbuffer.equals("END")) {
readbuffer = in.readLine();
System.out.println(readbuffer);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}*/
}
}
Server端的log输出
Server Start----8080:
Server: listen started
Server: lisen selector
Server: selectionKey isAcceptable
Server: lisen selector
Server: selectionKey isReadable
服务器端接受客户端数据--:Hello,Server
Server: lisen selector
Server: selectionKey isReadable
服务器端接受客户端数据--:agadsf
Client端log输出
Client:selectionKey isConnectable
完成连接!
agadsf
客户端向服务器端发送数据--:agadsf