Java进阶作业四:使用事件驱动IO写一个EchoServer
写在开头
本文学习使用Java原生的NIO API提供的事件驱动IO模型来编写一个EchoServer,所谓EchoServer就是,客户端发送给服务器一段消息,服务器接收到之后将消息原样返回给客户端。
示例代码没有处理Channel的关闭和如何关闭服务器,重点在于学习如何使用NIO的API。
服务端
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;
public class EventBasedServer {
public void start(int port) throws IOException {
ServerSocketChannel server = ServerSocketChannel.open();
server.bind(new InetSocketAddress(port));
server.configureBlocking(false);
Selector selector = Selector.open();
ByteBuffer buffer = ByteBuffer.allocate(1024);
SocketChannel client;
String msg = "NULL";
System.out.println("server starting...");
// 服务器的channel关心accept操作,所以注册accept事件
server.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
// 阻塞选择所有的key
selector.select();
// 取出可处理的key
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if (key.isAcceptable()) {
client = ((ServerSocketChannel) key.channel()).accept();
client.configureBlocking(false);
// 收到了客户端的连接,接下来关心读操作,所以注册读事件
client.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
client = (SocketChannel) key.channel();
int len = client.read(buffer);
msg = readStringFromByteBuffer(buffer, len);
System.out.println("receive from client:" + msg);
// 读取数据之后,接下来关心写操作,所以注册写事件
client.register(selector, SelectionKey.OP_WRITE);
} else if (key.isWritable()) {
client = (SocketChannel) key.channel();
buffer.put(msg.getBytes());
buffer.flip();
writeDataToChannel(client, buffer);
// 写完数据之后,接下来关心读操作,所以注册读事件
client.register(selector, SelectionKey.OP_READ);
} else {
System.out.println("do nothing");
}
keyIterator.remove();
}
}
}
/**
* 将buffer中指定数量的字节转换为字符串
*/
private String readStringFromByteBuffer(ByteBuffer buffer, int count) {
byte[] msgBytes = new byte[count];
for (int i = 0; i < count; i++) {
msgBytes[i] = buffer.get(i);
}
buffer.clear();
return new String(msgBytes);
}
/**
* 将buffer中的数据写入到Channel
*/
public void writeDataToChannel(SocketChannel channel, ByteBuffer buffer) throws IOException {
while (buffer.hasRemaining()) {
channel.write(buffer);
}
buffer.clear();
}
public static void main(String[] args) throws IOException {
EventBasedServer server = new EventBasedServer();
server.start(8888);
}
}
客户端
import java.io.IOException;
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.Scanner;
import java.util.Set;
public class EventBasedClient {
public void connect(String ip, int port) throws IOException {
SocketChannel client = SocketChannel.open(new InetSocketAddress(ip, port));
client.configureBlocking(false);
Selector selector = Selector.open();
// 客户端连接上服务器之后,关心写操作,因此注册写事件
client.register(selector, SelectionKey.OP_WRITE);
ByteBuffer buffer = ByteBuffer.allocate(1024);
String msg;
Scanner scanner = new Scanner(System.in);
while (true) {
selector.select();
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectionKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if (key.isWritable()) {
System.out.println("client ready! please input msg");
msg = scanner.nextLine();
buffer.put(msg.getBytes());
buffer.flip();
writeDataToChannel(client, buffer);
// 写操作完成之后,关心读操作,因此注册读事件
client.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
client = (SocketChannel) key.channel();
int len = client.read(buffer);
msg = readStringFromByteBuffer(buffer, len);
System.out.println("server response:" + msg);
// 读操作完成之后,关心写操作,因此注册写事件
client.register(selector, SelectionKey.OP_WRITE);
} else {
System.out.println("do nothing");
}
keyIterator.remove();
}
}
}
public static void main(String[] args) throws IOException {
EventBasedClient client = new EventBasedClient();
client.connect("127.0.0.1", 8888);
}
/**
* 将buffer中指定数量的字节转换为字符串
*/
private String readStringFromByteBuffer(ByteBuffer buffer, int count) {
byte[] msgBytes = new byte[count];
for (int i = 0; i < count; i++) {
msgBytes[i] = buffer.get(i);
}
buffer.clear();
return new String(msgBytes);
}
/**
* 将buffer中的数据写入到Channel
*/
public void writeDataToChannel(SocketChannel channel, ByteBuffer buffer) throws IOException {
while (buffer.hasRemaining()) {
channel.write(buffer);
}
buffer.clear();
}
}