之前写过缓冲区Buffer和通道Channel: Java NIO之Buffer、Java NIO之Channel 。今天主要结合Selector,一起使用NIO实现简单的对话式聊天,分为客户端和服务端:
无论是服务端,还是客户端,思路都是利用Selector,获取准备就绪的通道集合,然后进行读写操作。
服务端,分为两步:
(1)启动服务,准备接收连接
(2)利用Selector,获取准备就绪的通道集合,然后进行读写操作
public class NIOServer {
private int port = 8888;
private Selector selector;
private ByteBuffer readBuf = ByteBuffer.allocate(1024);
private ByteBuffer writeBuf = ByteBuffer.allocate(1024);
private Scanner scanner = new Scanner(System.in);
public NIOServer(){
try {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 通道设置为非阻塞
serverSocketChannel.configureBlocking(false);
ServerSocket socket = serverSocketChannel.socket();
selector = Selector.open();
socket.bind(new InetSocketAddress(port));
// 准备接收请求
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("服务端启动成功");
}catch (IOException e){
System.out.println("服务端启动失败了...");
e.printStackTrace();
}
}
private void listen(){
while (true){
try {
// 阻塞,直到有准备就绪的通道
selector.select();
// 获取准备就绪的通道集合
Set<SelectionKey> selectionKeys = selector.selectedKeys();
for (SelectionKey selectionKey : selectionKeys){
if (selectionKey.isAcceptable()){
// 接收就绪,有客户端请求连接
ServerSocketChannel server = (ServerSocketChannel) selectionKey.channel();
SocketChannel client = server.accept();
client.configureBlocking(false);
client.register(selector,SelectionKey.OP_READ);
System.out.println("连接成功: "+client.getLocalAddress());
}else if (selectionKey.isReadable()){
// 读就绪,客户端发消息给服务端
System.out.println("服务端,读就绪...");
SocketChannel client = (SocketChannel)selectionKey.channel();
StringBuffer msg = new StringBuffer("<<<< 收到客户端消息: ");
// 读之前先清空缓存
readBuf.clear();
// 将客户端的数据读到缓存中
while (client.read(readBuf) > 0){
// 切换为读模式
readBuf.flip();
msg.append(new String(readBuf.array(),0,readBuf.limit()));
}
System.out.println(msg.toString());
client.register(selector,SelectionKey.OP_WRITE);
}else if (selectionKey.isWritable()){
// 写就绪, 发送数据给客户端
System.out.println("服务端,写就绪...");
SocketChannel client = (SocketChannel)selectionKey.channel();
String msg = scanner.nextLine();
// 写之前,先清空缓存
writeBuf.clear();
writeBuf.put(msg.getBytes());
// 切换为读模式
writeBuf.flip();
client.write(writeBuf);
client.register(selector,SelectionKey.OP_READ);
System.out.println("服务端发送的数据是: "+msg);
}
}
// 清除处理过的事件
selectionKeys.clear();
} catch (IOException e) {
e.printStackTrace();
}finally {
// scanner.close();
}
}
}
public static void main(String[] args) {
new NIOServer().listen();
}
}
客户端也是两步:
(1)请求连接
(2)利用Selector,获取准备就绪的通道集合,然后进行读写操作
public class NIOClient {
private String ip = "localhost";
private int port = 8888;
private Selector selector;
private ByteBuffer readBuf = ByteBuffer.allocate(1024);
private ByteBuffer writeBuf = ByteBuffer.allocate(1024);
private Scanner scanner = new Scanner(System.in);
public NIOClient(){
try {
SocketChannel socketChannel = SocketChannel.open();
// 通道设置为非阻塞
socketChannel.configureBlocking(false);
selector = Selector.open();
socketChannel.register(selector, SelectionKey.OP_CONNECT);
socketChannel.connect(new InetSocketAddress(ip,port));
start();
}catch (IOException e){
System.out.println("客户端启动失败了...");
e.printStackTrace();
}
}
private void start(){
while (true){
try {
selector.select();
Set<SelectionKey> selectionKeys = selector.selectedKeys();
for (SelectionKey selectionKey : selectionKeys){
if (selectionKey.isConnectable()){
// 连接就绪
SocketChannel 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("客户端,读就绪...");
SocketChannel client = (SocketChannel)selectionKey.channel();
StringBuffer msg = new StringBuffer(">>> 收到服务端消息: ");
// 读之前先清空缓存
readBuf.clear();
// 将客户端的数据读到缓存中
while (client.read(readBuf) > 0){
// 切换为读模式
readBuf.flip();
msg.append(new String(readBuf.array(),0,readBuf.limit()));
}
System.out.println(msg.toString());
client.register(selector,SelectionKey.OP_WRITE);
}else if (selectionKey.isWritable()){
System.out.println("客户端,写就绪...");
// 写就绪, 发送数据给服务端
SocketChannel client = (SocketChannel)selectionKey.channel();
String msg = scanner.nextLine();
// 写之前,先清空缓存
writeBuf.clear();
writeBuf.put(msg.getBytes());
// 切换为读模式
writeBuf.flip();
client.write(writeBuf);
client.register(selector,SelectionKey.OP_READ);
System.out.println("客户端发送的数据是: "+msg);
}
}
// 清除处理过的事件
selectionKeys.clear();
} catch (IOException e) {
e.printStackTrace();
}finally {
// scanner.close();
}
}
}
public static void main(String[] args) {
new NIOClient();
}
}