1. 服务端处理器关注读事件
package org.example;
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.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Iterator;
import java.util.Set;
public class NioServerHandle implements Runnable {
private volatile boolean start;
private ServerSocketChannel serverSocketChannel;
private Selector selector;
public NioServerHandle(int port) {
try {
selector = Selector.open();
serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.socket().bind(new InetSocketAddress(port));
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
start = true;
System.out.println("服务端已启动,端口号:" + port);
} catch (IOException e) {
e.printStackTrace();
}
}
private void handleInput(SelectionKey key) throws IOException {
if (key.isValid()) {
if (key.isAcceptable()) {
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
SocketChannel accept = ssc.accept();
System.out.println("建立连接");
accept.configureBlocking(false);
accept.register(selector, SelectionKey.OP_READ);
}
if (key.isReadable()) {
SocketChannel sc = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int readBytes = sc.read(buffer);
if (readBytes > 0) {
buffer.flip();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
String message = new String(bytes, StandardCharsets.UTF_8);
System.out.println("服务器接收到的消息:" + message);
String result = "Hello " + message + ", now is " + new Date();
doWrite(sc, result);
}
else if (readBytes < 0) {
key.cancel();
sc.close();
}
}
}
}
private void doWrite(SocketChannel socketChannel, String response) throws IOException {
byte[] bytes = response.getBytes();
ByteBuffer buffer = ByteBuffer.allocate(bytes.length);
buffer.put(bytes);
buffer.flip();
socketChannel.write(buffer);
}
@Override
public void run() {
while (start) {
try {
selector.select(1000);
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
handleInput(selectionKey);
iterator.remove();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
2. 服务端启动类
package org.example;
public class NioServer {
private static NioServerHandle nioServerHandle;
public static void main(String[] args) {
nioServerHandle = new NioServerHandle(9001);
new Thread(nioServerHandle).start();
}
}
3. 客户端处理器
package org.example;
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.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Set;
public class NioClientHandle implements Runnable {
private String host;
private int port;
private volatile boolean started;
private Selector selector;
private SocketChannel socketChannel;
public NioClientHandle(String ip, int port) {
this.host = ip;
this.port = port;
try {
selector = Selector.open();
socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
started = true;
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
try {
doConnect();
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
while (started) {
try {
selector.select(1000);
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
SelectionKey key = null;
while (iterator.hasNext()) {
key = iterator.next();
try {
handleInput(key);
iterator.remove();
} catch (Exception e) {
if (key != null) {
key.cancel();
if (key.channel() != null) {
key.channel().close();
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
}
if (selector != null) {
try {
selector.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void handleInput(SelectionKey key) throws IOException {
if (key.isValid()) {
SocketChannel sc = (SocketChannel) key.channel();
if (key.isConnectable()) {
if (sc.finishConnect()) {
sc.register(selector, SelectionKey.OP_READ);
} else {
System.exit(1);
}
}
if (key.isReadable()) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
int readBytes = sc.read(buffer);
if (readBytes > 0) {
buffer.flip();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
String message = new String(bytes, StandardCharsets.UTF_8);
System.out.println("客户器接收到的消息:" + message);
}
else if (readBytes < 0) {
key.cancel();
sc.close();
}
}
}
}
private void doWrite(SocketChannel channel, String request)
throws IOException {
byte[] bytes = request.getBytes();
ByteBuffer buffer = ByteBuffer.allocate(bytes.length);
buffer.put(bytes);
buffer.flip();
channel.write(buffer);
}
private void doConnect() throws IOException {
if (socketChannel.connect(new InetSocketAddress(host, port))) {
socketChannel.register(selector, SelectionKey.OP_READ);
}
else {
socketChannel.register(selector, SelectionKey.OP_CONNECT);
}
}
public void sendMsg(String msg) throws Exception {
doWrite(socketChannel, msg);
}
}
4. 客户端启动类
package org.example;
import java.util.Scanner;
public class NioClient {
private static NioClientHandle nioClientHandle;
public static void start() {
nioClientHandle = new NioClientHandle("127.0.0.1", 9001);
new Thread(nioClientHandle, "Server").start();
}
public static boolean sendMsg(String msg) throws Exception {
nioClientHandle.sendMsg(msg);
return true;
}
public static void main(String[] args) throws Exception {
start();
Scanner scanner = new Scanner(System.in);
while (NioClient.sendMsg(scanner.next())) ;
}
}
5. 先启动服务端,再启动客户端
6. 新建一个服务端处理器,同时关注读事件和写事件
package org.example;
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.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Iterator;
import java.util.Set;
public class NioServerHandleWrite implements Runnable {
private volatile boolean start;
private ServerSocketChannel serverSocketChannel;
private Selector selector;
public NioServerHandleWrite(int port) {
try {
selector = Selector.open();
serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.socket().bind(new InetSocketAddress(port));
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
start = true;
System.out.println("服务端已启动,端口号:" + port);
} catch (IOException e) {
e.printStackTrace();
}
}
private void handleInput(SelectionKey key) throws IOException {
System.out.println("当前通道内的事件:" + key.interestOps());
if (key.isValid()) {
if (key.isAcceptable()) {
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
SocketChannel accept = ssc.accept();
System.out.println("建立连接");
accept.configureBlocking(false);
accept.register(selector, SelectionKey.OP_READ);
}
if (key.isReadable()) {
SocketChannel sc = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int readBytes = sc.read(buffer);
if (readBytes > 0) {
buffer.flip();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
String message = new String(bytes, StandardCharsets.UTF_8);
System.out.println("服务器接收到的消息:" + message);
String result = "Hello " + message + ", now is " + new Date();
doWrite(sc, result);
}
else if (readBytes < 0) {
key.cancel();
sc.close();
}
}
if (key.isWritable()) {
SocketChannel sc = (SocketChannel) key.channel();
ByteBuffer buffer = (ByteBuffer) key.attachment();
if (buffer.hasRemaining()) {
int write = sc.write(buffer);
System.out.println("write :" + write + " byte, remaining :" + buffer.hasRemaining());
} else {
key.interestOps(SelectionKey.OP_READ);
}
}
System.out.println("当前通道内的事件2:" + key.interestOps());
}
}
private void doWrite(SocketChannel socketChannel, String response) throws IOException {
byte[] bytes = response.getBytes();
ByteBuffer buffer = ByteBuffer.allocate(bytes.length);
buffer.put(bytes);
buffer.flip();
socketChannel.register(selector,
SelectionKey.OP_WRITE | SelectionKey.OP_READ, buffer);
}
@Override
public void run() {
while (start) {
try {
selector.select(1000);
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
iterator.remove();
handleInput(selectionKey);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
7. 启动类
package org.example;
public class NioServerWrite {
private static NioServerHandleWrite nioServerHandleWrite;
public static void main(String[] args) {
nioServerHandleWrite = new NioServerHandleWrite(9001);
new Thread(nioServerHandleWrite).start();
}
}
8. 先启动服务端,再启动客户端
- 这时候selector中只有接收事件OP_ACCEPT,1左移4位就是2的4次方,等于16
当前通道内的事件1:1
服务器接收到的消息:fisher
当前通道内的事件2:5
当前通道内的事件1:5
write :49 byte, remaining :false
当前通道内的事件2:5
当前通道内的事件1:5
当前通道内的事件2:1
- 这里必须要有取消写事件的注册,不然只要buffer中没有填满,程序就会不停出发写事件操作
- 这里注释掉 key.interestOps(SelectionKey.OP_READ);这行代码,再发消息看服务端打印,程序会一直出发写事件,进到if (key.isWritable()) {}这个代码块中,但是又没有数据可以写
代码下载地址
https://gitee.com/fisher3652/netWork.git