1、NIO工作方式
用NIO方式处理IO使用多路复用器Selector来轮询每个通道Channel,当通道中有事件时就通知处理。比较重要的是弄懂Selector和Channel概念
2、服务端代码
package com.guosen.nio;
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 NioServer {
/**
* 缓冲区大小
*/
private int BLOCK = 4096;
/**
* Selector
*/
private Selector selector;
/**
* 发送数据缓冲区
*/
private ByteBuffer sendBuffer = ByteBuffer.allocate(BLOCK);
/**
* 接收数据缓冲区
*/
private ByteBuffer receiveBuffer = ByteBuffer.allocate(BLOCK);
public NioServer(int port) throws Exception{
/*
* 打开服务器套接字通道
*/
ServerSocketChannel ssc = ServerSocketChannel.open();
/*
* 配置为非阻塞
*/
ssc.configureBlocking(false);
/*
* 检索与通道相关的服务器套接字
*/
ServerSocket ss = ssc.socket();
/*
* 将服务和地址绑定
*/
ss.bind(new InetSocketAddress("127.0.0.1", port));
/*
* 通过open方法打开一个Selector
*/
selector = Selector.open();
/*
* 注册到Selector等待连接
*/
ssc.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("server start on " + port);
}
private void listen() throws Exception{
while (true) {
selector.select();
Set<SelectionKey> set = selector.selectedKeys();
Iterator<SelectionKey> iterator = set.iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
iterator.remove();
/**
* 处理事件
*/
try {
handleKey(selectionKey);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
private void handleKey(SelectionKey selectionKey) throws Exception{
ServerSocketChannel server = null;
SocketChannel client = null;
String receiveText = null;
String sendText = null;
int count = 0;
/*
* 如果连接已经无效,则主动抛出异常结束该连接
*/
if (!selectionKey.isValid()) {
client = (SocketChannel) selectionKey.channel();
throw new Exception("客户已经主动关闭");
}
if (selectionKey.isAcceptable()) {
/*
* 获取通道
*/
server = (ServerSocketChannel) selectionKey.channel();
/*
* 从通道接收一个连接
*/
client = server.accept();
/*
* 设置为非堵塞方式
*/
client.configureBlocking(false);
/*
* 将通道注册为读操作
*/
client.register(selector, SelectionKey.OP_READ);
} else if (selectionKey.isReadable()) {
/*
* 获取到通道
*/
client = (SocketChannel) selectionKey.channel();
/*
* 清空缓冲区
*/
receiveBuffer.clear();
/*
* 从缓冲区读数据
*/
count = client.read(receiveBuffer);
if (count > 0) {
receiveText = new String(receiveBuffer.array(), 0, count);
System.out.println("服务器接收到数据==>" + receiveText);
/*
* 将通道注册为写操作
*/
client.register(selector, SelectionKey.OP_WRITE);
}
} else if (selectionKey.isWritable()) {
/*
* 将缓冲区清空
* 将position设置为0
* 将limit设置为capital
*/
sendBuffer.clear();
/*
* 获取到通道
*/
client = (SocketChannel) selectionKey.channel();
sendText = "message is received on time[" + System.currentTimeMillis() + "]";
/*
* 向缓冲区中放入数据
*/
sendBuffer.put(sendText.getBytes());
/*
* 将limit置为position
* 将position置为0
*/
sendBuffer.flip();
/*
* 将缓冲区的数据发送给客户端
*/
client.write(sendBuffer);
System.out.println("服务器已经向客户端发送数据==>" + sendText);
/*
* 将通道注册为读操作
*/
client.register(selector, SelectionKey.OP_READ);
}
}
public static void main(String[] args) throws Exception{
NioServer ns = new NioServer(8080);
ns.listen();
}
}
3、客户端代码
package com.guosen.nio;
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 NioClient {
/**
* 缓冲区大小
*/
private static int BLOCK = 4096;
/**
* 发送数据缓冲区
*/
private static ByteBuffer sendBuffer = ByteBuffer.allocate(BLOCK);
/**
* 接收数据缓冲区
*/
private static ByteBuffer receiveBuffer = ByteBuffer.allocate(BLOCK);
public static void main(String[] args) throws Exception{
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
Selector selector = Selector.open();
socketChannel.register(selector, SelectionKey.OP_CONNECT);
socketChannel.connect(new InetSocketAddress("127.0.0.1", 8080));
Set<SelectionKey> selectKeys = null;
SocketChannel client;
String receiveText;
String sendText;
int count = 0;
while (true) {
selector.select();
selectKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
if (key.isConnectable()) {
client = (SocketChannel) key.channel();
if (client.isConnectionPending()) {
client.finishConnect();
sendBuffer.clear();
sendBuffer.put("Hello, Server".getBytes());
sendBuffer.flip();
client.write(sendBuffer);
}
client.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
client = (SocketChannel) key.channel();
receiveBuffer.clear();
count = client.read(receiveBuffer);
if (count > 0) {
receiveText = new String(receiveBuffer.array(), 0, count);
System.out.println("客户端接收到服务器的数据==>" + receiveText);
client.register(selector, SelectionKey.OP_WRITE);
}
} else if (key.isWritable()) {
sendBuffer.clear();
client = (SocketChannel) key.channel();
sendText = "message from cliet, time is[" + System.currentTimeMillis() + "]";
sendBuffer.put(sendText.getBytes());
sendBuffer.flip();
client.write(sendBuffer);
System.out.println("客户端向服务器端发送数据==>" + sendText);
client.register(selector, SelectionKey.OP_READ);
}
selectKeys.clear();
}
}
}
}
也可以用telnet 127.0.0.1 8080来测试服务端程序