几十行代码简单实现 NIO/多路复用 epoll/selector Golang/Java/C++

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(".");
        }
    }
}

Client 和 Server 都是非阻塞的
其实 Java 基于 slector 封装了太多.
下面我们将基于 C epoll 和 Golang 实现高性能网络通信

2. C 实现

需要在 linux 上面运行

代码地址:https://github.com/dengjiawen8955/epll_c

3. golang 实现

其实 golang 和 C 语言实现差不多,都是调用 linux 系统调用

需要在 linux 上面运行

代码地址:https://github.com/dengjiawen8955/epoll_go

问题:多路复用有哪几种方式,有什么区别?

linux中有3种常见的多路复用方式1.selector 2.poll 3.epoll

  • selector 和 poll 都是轮询所有连接 fd,存在内核态和用户态的大量 fd 拷贝。很相似

  • epoll 是给每个连接注册一个回调函数,
    有链接需要处理时候,回调函数将 fd 放入就绪链表 中,
    并且利用 mmap() 减少内核态和用户态的内存拷贝。


    因为 epoll 的 回调函数mmap 机制,epoll 性能优于 poll 和 selector

但是在 fd 比较少且比较活跃的时候,epoll 因为需要给每个连接注册回调函数,所以性能也许会差一些。

详细参考:https://zhuanlan.zhihu.com/p/272891398

问题 Goland 需要 NIO 吗?为什么?

因为 goland 并不会阻塞线程,他的协程本来就是非阻塞的,具体参考 golang GMP 模型和原理

golang 的网络 IO 本来就是底层使用的 epoll
参考:
https://zhuanlan.zhihu.com/p/108509080

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JarvanStack

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值