使用java NIO实现nio客户端连接nio服务端发送消息
public static void main(String[] args) {
server();
}
public static void server() {
// 创建选择器和socket channel
try (Selector selector = Selector.open();
ServerSocketChannel ssc = ServerSocketChannel.open()) {
// 绑定端口
ssc.socket().bind(new InetSocketAddress(8080));
// 设置不阻塞
ssc.configureBlocking(false);
// socket注册选择器和监听链接事件
ssc.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("服务启动");
while (true) {
// 选择器会阻塞到有对应的事件发生
selector.select();
// 取出发生的事件
Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
while (iter.hasNext()) {
SelectionKey key = iter.next();
try {
// 连接事件
if (key.isAcceptable()) {
// 处理连接
handleAccept(key);
}
// 读事件
if (key.isReadable()) {
// 处理读
handleRead(key);
}
// 写事件且key有效的时候
if (key.isWritable() && key.isValid()) {
// 处理写
handleWrite(key);
}
} catch (Exception e) {
e.printStackTrace();
// 发生异常关闭channel
key.channel().close();
}
// 处理完成后移除该事件
iter.remove();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void handleAccept(SelectionKey key) throws IOException {
System.out.println("accept 就绪");
ServerSocketChannel channel = (ServerSocketChannel) key.channel();
// 建立连接
SocketChannel sc = channel.accept();
// 设置不阻塞
sc.configureBlocking(false);
// 注册读事件,和添加一个处理数据的ReadWriteBuffer缓存
sc.register(key.selector(), SelectionKey.OP_READ, new ReadWriteBuffer());
System.out.println(sc.getRemoteAddress() + "连接成功");
}
public static void handleRead(SelectionKey key) throws IOException {
System.out.println("read 就绪");
SocketChannel channel = (SocketChannel) key.channel();
// 取出建立连接的时候添加的缓存
ReadWriteBuffer readWriteBuffer = (ReadWriteBuffer) key.attachment();
// 读取数据
ByteBuffer readBuffer = readWriteBuffer.readBuffer;
int read;
StringBuilder sb = new StringBuilder();
while ((read = channel.read(readBuffer)) > 0) {
readBuffer.flip();
byte[] readByte = new byte[read];
readBuffer.get(readByte);
sb.append(new String(readByte));
readBuffer.clear();
}
System.out.println(sb);
// 添加写事件
key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
// 随便处理的回复客户端的消息
String result = sb.toString().replace("?", "!");
result = result.charAt(result.length() - 1) != '!' ? result + "!" : result;
// 往buffer中写入数据
readWriteBuffer.writeBuffer.put(result.getBytes());
// 读取不到数据后把channel关闭
if (read == -1) {
channel.close();
}
}
public static void handleWrite(SelectionKey key) throws IOException {
System.out.println("write 就绪");
// 取出buffer进行读取
ReadWriteBuffer readWriteBuffer = (ReadWriteBuffer) key.attachment();
readWriteBuffer.writeBuffer.flip();
SocketChannel sc = (SocketChannel) key.channel();
// 有数据的时候将数据写入socket中
if (readWriteBuffer.writeBuffer.hasRemaining()) {
sc.write(readWriteBuffer.writeBuffer);
} else {
// 注销写事件
key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
}
// 将buffer中没有用到的数据进行迁移
readWriteBuffer.writeBuffer.compact();
}
// 申请的buffer
static class ReadWriteBuffer {
// 处理读操作
ByteBuffer readBuffer = ByteBuffer.allocateDirect(1024);
// 处理写操作
ByteBuffer writeBuffer = ByteBuffer.allocateDirect(1024);
}
public static void main(String[] args) {
client();
}
public static void client() {
// 单线程池
ExecutorService single = Executors.newSingleThreadExecutor();
ByteBuffer readBuffer = ByteBuffer.allocateDirect(1024);
ByteBuffer writeBuffer = ByteBuffer.allocateDirect(1024);
// 开启选择器 socket scanner(用来发送消息)
try (Selector selector = Selector.open();
SocketChannel sc = SocketChannel.open();
Scanner scanner = new Scanner(System.in)) {
sc.configureBlocking(false);
// 建立连接的地址
sc.connect(new InetSocketAddress("localhost", 8080));
// 注册连接事件
sc.register(selector, SelectionKey.OP_CONNECT);
while (true) {
selector.select();
Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
while (keys.hasNext()) {
SelectionKey key = keys.next();
// 连接事件且连接完成后,注册读事件和写事件
if (key.isConnectable()) {
if (sc.finishConnect()) {
sc.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
}
}
// 读事件
if (key.isReadable()) {
// 读取服务端数据
readBuffer.clear();
int read = sc.read(readBuffer);
byte[] readByte = new byte[read];
readBuffer.flip();
readBuffer.get(readByte);
System.out.println(new String(readByte));
// 重新注册写事件
key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
}
// 写事件
if (key.isWritable()) {
// 使用线程写数据到服务端
threadWrite(single, writeBuffer, sc, scanner);
// 取消注册写事件(不取消会造成上一次数据可能还没发送,下次进来还会继续执行)
key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
}
// 移除key
keys.remove();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static void threadWrite(ExecutorService single, ByteBuffer writeBuffer, SocketChannel sc, Scanner scanner) {
single.execute(() -> {
try {
// 休眠
TimeUnit.SECONDS.sleep(1);
// 使用scanner读取控制台输入
System.out.println(Thread.currentThread().getName() + "请输入:");
String line = scanner.nextLine();
writeBuffer.put(line.getBytes());
writeBuffer.flip();
// 写入数据
while (writeBuffer.hasRemaining()) {
sc.write(writeBuffer);
}
writeBuffer.compact();
} catch (Exception e) {
e.printStackTrace();
}
});
}