poll模型:其实是在jvm的native中维护了fd列表。系统调用poll的时候,需要传递fds到kernel中,kernel遍历fds,返回有变化的fds。

epoll模型:Selector#open 通过epoll_create函数,创建一个name为eventpoll的fd,在kernel中创建一个eventpoll结构体,这个结构体包含红黑树和双向链表。Selector#register 向name为eventpoll的fd注册fd,但是不会立马调用,等调用select的时候调用,对应epoll_ctl函数。Selector#select 首先会调用注册事件epoll_ctl函数,然后调用epoll_wait返回有变化fd。

单线程模型下:如果连接过多,服务器可能处理不过来。因为在系统io的时候,cpu中断,程序会停止执行。如果创建新的线程来处理io,那么需要调用key#cancel,把fd从eventpoll中移除,否则下次调用epoll_wait仍会返回该对象。

读事件依赖于Recvq是否有数据(其实是网卡拷贝完数据发送的回调函数把fd加入到rdlist中),写事件依赖于SendQ是否有数据,如果注册了写事件,因为SendQ一直处于可写状态,所以需要在写的时候才注册写事件,否则会出现死循环,cpu空转。

用一个Selector处理accept事件,用其他的Selector处理io读写事件,每个Selector在一个线程中,实际上fd的io的处理还是单线程的
这样做的好处是充分利用cpu,因为在处理io的过程中,cpu会中断,这时候可以让cpu处理别的事情,每个Selector中都有一些fd列表,这样其实在线程中处理fd的时候是线性的。

单线程多路复用器(Select) epoll,客户端发送什么服务器端会写什么事件。

    //创建一个用于accept的socket对象fd=4, 
    //socket(AF_INET6, SOCK_STREAM, IPPROTO_IP) = 4
    ServerSocketChannel server = ServerSocketChannel.open();   
    //绑定9090端口,监听fd=4
    // bind(4, {sa_family=AF_INET6, sin6_port=htons(9090), inet_pton(AF_INET6, "::", &sin6_addr), sin6_flowinfo=htonl(0), sin6_scope_id=0}, 28) = 0
    // listen(4, 50)   
    server.bind(new InetSocketAddress(9090)); 
    // 设置非阻塞
    // fcntl(4, F_SETFL, O_RDWR|O_NONBLOCK)    = 0
    server.configureBlocking(false);
    // 调用epoll_create创建一个name为eventpoll的fd,eventpoll对象中包含红黑树和双向链表
    // epoll_create(256)                       = 7
    Selector selector = Selector.open();
    // 此方法不会立马调用 epoll_ctl,会等到调用select方法,为了提高效率。
    server.register(selector, SelectionKey.OP_ACCEPT);
    while (true) {
        // ①注册ServerSocketChannel的fd到 epollevent上,并调用epoll_wait方法
        // ①epoll_ctl(7, EPOLL_CTL_ADD, 4, {EPOLLIN, {u32=4, u64=4186726359134896132}}) = 0
        // ①epoll_wait(7, [{EPOLLIN, {u32=4, u64=4186726359134896132}}], 4096, -1) = 1

       // ②注册fd为8的SocketChannel到 eventpoll上。
       // ②epoll_ctl(7, EPOLL_CTL_ADD, 8, {EPOLLIN, {u32=8, u64=4294967304}}) = 0
       // ②epoll_wait(7, [{EPOLLIN, {u32=8, u64=4294967304}}], 4096, -1) = 1

        // ③修改fd为8的SocketChannel到eventpoll上。
        // ③epoll_ctl(7, EPOLL_CTL_MOD, 8, {EPOLLOUT, {u32=8, u64=4294967304}}) = 0
        // ③epoll_wait(7, [{EPOLLOUT, {u32=8, u64=4294967304}}], 4096, -1) = 1
        while (selector.select() > 0) {
            Set<SelectionKey> keys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = keys.iterator();
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                iterator.remove();
                if(key.isAcceptable()) {
 // 应用程序接收socket,fd=8
 // accept(4, {sa_family=AF_INET6, sin6_port=htons(57572), inet_pton(AF_INET6, "::ffff:10.211.55.5", &sin6_addr), sin6_flowinfo=htonl(0), sin6_scope_id=0}, [28]) = 8
                    SocketChannel client = ((ServerSocketChannel) key.channel()).accept();
                    //设置为非阻塞
                    //fcntl(7, F_SETFL, O_RDWR|O_NONBLOCK)    = 0
                    client.configureBlocking(false);
                    ByteBuffer buffer = ByteBuffer.allocate(8192);
                    //此方法不会立马调用epoll_ctl
                    client.register(key.selector(), SelectionKey.OP_READ, buffer);
                } else if(key.isReadable()) {
                    SocketChannel client = ((ServerSocketChannel) key.channel()).accept();
                    ByteBuffer buffer = (ByteBuffer) key.attachment();
                    buffer.clear();
                    while (true) {
                        int read = client.read(buffer);
                        if (read > 0) {
                            client.register(key.selector(), SelectionKey.OP_WRITE, buffer);
                        } else if (read == 0) {
                            break;
                        } else {
                            client.close();
                            break;
                        }
                    }
                } else if(key.isWritable()) {
                    SocketChannel client = (SocketChannel) key.channel();
                    ByteBuffer buffer = (ByteBuffer) key.attachment();
                    buffer.flip();
                    while (buffer.hasRemaining()) {
                        client.write(buffer);
                    }
                    buffer.clear();
                    //此方法不会立马调用epoll_ctl
                    client.register(key.selector(), SelectionKey.OP_READ, buffer);                    
                }
            }
        }
    }

一只鱼
49 声望1 粉丝

« 上一篇
TCP/IP
下一篇 »
网络模型