经过前面的分析,大概理解了NIO的实现原理,接下来代码实现一下:
参考:
https://blog.csdn.net/billluffy/article/details/78036998
http://www.360doc.com/content/12/0902/17/495229_233773276.shtml
https://www.cnblogs.com/caca/archive/2012/03/06/3585295.html
https://www.cnblogs.com/kooker/p/9559040.html
水平不行,光看源码还是没法全部了解透。
下边是参考了上面引用地址的代码
客户端:
package netty;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
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;
/**
* @Description:
* @author:xuyaqi
* @date:2018年6月6日
*/
public class Client implements Runnable {
private int i = -1;
public Client() {
}
public Client(int i) {
this.i = i;
}
public static void main(String[] args) throws UnsupportedEncodingException {
for (int i = 0; i < 1; i++) {
new Thread(new Client(i)).start();
}
}
@Override
public void run() {
startClient(i);
}
public void startClient(int i) {
SocketChannel socketChannel = null;
Selector selector = null;
ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
try {
socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("127.0.0.1", 1234));
selector = Selector.open();
socketChannel.register(selector, SelectionKey.OP_READ);
Thread thread = new Thread(new clientHandler(selector));
thread.start();
String info = "客户端" + i + ":" + Math.random() * 1000;
if (socketChannel.finishConnect()) {
System.out.println("-------------------------------------------------------------");
writeBuffer.clear();
writeBuffer.put(info.getBytes("utf-8"));
writeBuffer.flip();
while (writeBuffer.hasRemaining()) {
socketChannel.write(writeBuffer);
}
}
//
// while (true) {
// if (socketChannel.finishConnect()) {
// String info = "客户端" + i + ":" + Math.random() * 1000;
while (true) {
// System.out.println("-------------------------------------------------------------");
// writeBuffer.clear();
// writeBuffer.put(info.getBytes("utf-8"));
// writeBuffer.flip();
//
// while (writeBuffer.hasRemaining()) {
// socketChannel.write(writeBuffer);
// }
//
// int bytesRead = socketChannel.read(readBuffer);
// if (bytesRead > 0) {
// readBuffer.flip();
// byte[] bytes = new byte[bytesRead];
// readBuffer.get(bytes, 0, bytesRead);
// String str = new String(bytes);
// System.out.println(str);
// readBuffer.clear();
//
// }
Thread.sleep(2000);
Thread.sleep(Integer.MAX_VALUE);
}
// break;
// }
// }
} catch (IOException e) {
e.printStackTrace();
} finally {
// if (socketChannel != null) {
// socketChannel.close();
// System.out.println("关闭socketChannel");
// }
}
}
public void hook() {
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run() {
}
}));
}
}
测试发现,这个handler必须单独放,不能放client类里面。
package netty;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
/**
-
@author toryxu
-
@version 1.0
-
@date 2020/3/31 1:19 上午
*/
class clientHandler implements Runnable {private Selector selector;
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
public clientHandler(Selector selector) {
this.selector = selector;
}@Override
public void run() {
while (true) {
try {
if (selector.select() > 0) {
Iterator iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey key = (SelectionKey) iterator.next();
if (key.isReadable()) {
int bytesRead = 0;
try {
bytesRead = ((SocketChannel) key.channel()).read(readBuffer);
} catch (IOException e) {
e.printStackTrace();
}
if (bytesRead > 0) {
readBuffer.flip();
byte[] bytes = new byte[bytesRead];
readBuffer.get(bytes, 0, bytesRead);
String str = new String(bytes);
System.out.println(str);
readBuffer.clear();
}
}
iterator.remove();
}
}
} catch (IOException e) {
e.printStackTrace();
}}
}
}
服务端
package netty;
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.util.Iterator;
public class Server {
private static final int BUF_SIZE = 1024;
private static final int PORT = 1234;
private ServerSocketChannel servSocketChannel = null;
private Selector selector = null;
public static void main(String[] args) {
new Server().startServer();
}
public void startServer() {
try {
servSocketChannel = ServerSocketChannel.open();
// 设置为非阻塞
servSocketChannel.configureBlocking(false);
// 绑定端口
servSocketChannel.socket().bind(new InetSocketAddress(PORT));
selector = Selector.open();
// 注册监听事件
servSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
listen();
} catch (IOException e) {
e.printStackTrace();
}
}
private void listen() {
while (true) {
try {
//
selector.select();
Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
while (iter.hasNext()) {
SelectionKey key = iter.next();
if (key.isValid() && key.isAcceptable()) {
System.out.println("handleAccept");
handleAccept(key);
}
if (key.isValid() && key.isReadable()) {
System.out.println("handleRead");
handleRead(key);
}
if (key.isValid() && key.isWritable()) {
System.out.println("handleWrite");
handleWrite(key);
}
if (key.isValid() && key.isConnectable()) {
System.out.println("isConnectable = true");
}
//
iter.remove();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void handleAccept(SelectionKey key) throws IOException {
ServerSocketChannel ssChannel = (ServerSocketChannel) key.channel();
SocketChannel sc = ssChannel.accept();
sc.configureBlocking(false);
sc.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocateDirect(BUF_SIZE));
}
public static void handleRead(SelectionKey key) throws IOException {
SocketChannel sc = (SocketChannel) key.channel();
try {
ByteBuffer buf = (ByteBuffer) key.attachment();
ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
int bytesRead = sc.read(buf);
if (bytesRead > 0) {
buf.flip();
byte[] bytes = new byte[bytesRead];
buf.get(bytes, 0, bytesRead);
String str = new String(bytes);
System.out.println(str);
buf.clear();
writeBuffer.clear();
writeBuffer.put(bytes);
writeBuffer.flip();
sc.write(writeBuffer);
// writeBuffer.put(bytes);
// writeBuffer.flip();
// while (writeBuffer.hasRemaining()) {
// sc.write(writeBuffer);
// }
// writeBuffer.compact();
} else {
//关闭客户端,服务端会发生一个read事件,并且在read的时候抛出异常,来表示关闭
//并把socket channel关闭
System.out.println("关闭的连接");
key.cancel();
sc.close();
}
} catch (Exception e) {
e.printStackTrace();
key.cancel();
sc.close();
}
}
public static void handleWrite(SelectionKey key) throws IOException {
ByteBuffer buf = (ByteBuffer) key.attachment();
buf.put("客户端返回".getBytes());
buf.flip();
SocketChannel sc = (SocketChannel) key.channel();
while (buf.hasRemaining()) {
sc.write(buf);
}
buf.compact();
}
}
逻辑:
服务端将serversocketchannel注册到selector,通过循环select()方法监听就绪的操作,并执行相关操作。
客户端建立连接,并在连接成功时进行写操作(判断连接成功的标志为finishConnect()返回true)。同时客户端也可以监听读操作,获得服务端返回的信息。