NIO 原理
参考:
https://zhuanlan.zhihu.com/p/345808940
https://blog.csdn.net/u013857458/article/details/82424104
1. Java 实现 Selector
实现代码
Server
public static void main(String[] args) throws IOException {
//创建一个 SocketServerChannel 类似于 listen
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//创建一个 Selector 对象
Selector selector = Selector.open();
//绑定端口 类似 listen
serverSocketChannel.socket().bind(new InetSocketAddress(6666));
//设置非阻塞的
serverSocketChannel.configureBlocking(false);
//把 listen 注册到 selector,关心的事件为 OP_ACCEPT 读写
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
//循环等待客户端连接
while (true) {
//使用 selector 等待一秒
//slelect() 将返回可以操作 socket 的个数 >= 0
if (selector.select(1000) == 0) {
System.out.print(".");
continue;
}
//如果有可以操作的 socket
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectionKeys.iterator();
//遍历可以操作的 socket
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if (key.isAcceptable() && !key.isReadable()) {
//怎么又生成一个 channel ? 对 之前的 没有生成,之前是 server .
SocketChannel channel = serverSocketChannel.accept();
channel.configureBlocking(false);
//关联一个 buffer
channel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
}
if (key.isReadable()) {
//拿到 channel.
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buffer = (ByteBuffer) key.attachment();
channel.read(buffer);
System.out.println("客户端发送数据:"+new String(buffer.array()));
}
//手动移除 selector 中的 key 防止重复操作.
keyIterator.remove();
}
}
}
}
Client
public class Client {
public static void main(String[] args) throws IOException, InterruptedException {
for (int i = 0; i < 10; i++) {
new Runnable() {
@Override
public void run() {
try {
SocketChannel channel = SocketChannel.open();
channel.configureBlocking(false);
InetSocketAddress address = new InetSocketAddress(6666);
//连接
if (!channel.connect(address)) {
System.out.println("没有连接成功,连接需要时间,可以做其他事情");
while (!channel.finishConnect()) {
Thread.sleep(1000);
System.out.print(".");
}
}
//连接成功的事情.
String str = "Hi There";
ByteBuffer buffer = ByteBuffer.wrap(str.getBytes());
channel.write(buffer);
} catch (Exception e) {
}
}
}.run();
}
while (true) {
Thread.sleep(1000);
System.out.print(".");
}
}
}
slectorepoll
2. C 实现
需要在 linux 上面运行
3. golang 实现
其实 golang 和 C 语言实现差不多,都是调用 linux 系统调用
需要在 linux 上面运行
问题:多路复用有哪几种方式,有什么区别?
linux中有3种常见的多路复用方式1.selector 2.poll 3.epoll
就绪链表
但是在 fd 比较少且比较活跃的时候,epoll 因为需要给每个连接注册回调函数,所以性能也许会差一些。
问题 Goland 需要 NIO 吗?为什么?
因为 goland 并不会阻塞线程,他的协程本来就是非阻塞的,具体参考 golang GMP 模型和原理
golang 的网络 IO 本来就是底层使用的 epoll
参考:
https://zhuanlan.zhihu.com/p/108509080