xsocket分析二,接收连接、收发数据

接着上一篇分析,Acceptor阻塞在accept函数中

SocketChannel channel = serverChannel.accept();//等待新的连接

// create IoSocketHandler
IoSocketDispatcher dispatcher = dispatcherPool.nextDispatcher();//获取Dispatcher
IoChainableHandler ioHandler = ConnectionUtils.getIoProvider().createIoHandler(false, dispatcher, channel,sslContext, sslOn);

// notify call back
callback.onConnectionAccepted(ioHandler);
acceptedConnections++;  

有新的连接时进入到下面三行函数,首先从DispatcherPool中获取一个Dispatcher,采用轮询的方式,然后构造一个IoChainableHandler(可以成链)对象,返回的是IoSocketHandler类的一个实例(非常重要),IoSocketHandler类继承自IoChainableHandler,封装了channel和Dispatcher以及一些当前连接的属性。然后回调callback的onConnectionAccepted,这个callback就是前面的那个LifeCycleHandler,该回调函数中有两行非常重要的代码

NonBlockingConnection connection = new NonBlockingConnection(connectionManager, handlerAdapter.getConnectionInstance()));
init(connection, ioHandler);

这里出现了一个connectionManager,对连接进行管理,先不管,然后是一个handlerAdapter,它封装了一个handlerInfo,就是对我们传入的EchoHandler进行解析适配,根据这两个参数构造出一个NonBlockingConnection。这个类是对连接的抽象(非常重要),后面会看到它就是传给我们的Handler的参数。NonBlockingConnection有几个很重要的成员变量如IoHandlerCallback,SerializedTaskQueue等后面会讲。

然后是init函数,参数ioHandler就是前面构造的IoChainableHandler,在init函数中将NonBlockingConnection的IoHandlerCallback(后面讲)赋值给了handlerAdapter的IIoHandlerCallback,然后调用了Dispatcher的register方法,将channel注册到Dispatcher的Selector中。

public boolean register(IoSocketHandler socketHandler, int ops) throws IOException {
    socketHandler.setMemoryManager(memoryManager);

    if (isDispatcherInstanceThread()) {
        registerHandlerNow(socketHandler, ops);//判断是否在dispatcher线程中,在则直接注册
    } else {
    }
    registerQueue.add(new RegisterTask(socketHandler, ops));//不在加入到队列中,同时将IoSocketHandler传给了RegisterTask
    wakeUp();
    }
    return true;
}

 注意这里并不是直接注册,而是将注册任务加入到了Dispatcher的registerQueue中,因为线程安全的问题。同时将IoSocketHandler对象传给了RegisterTask的构造函数,RegisterTask实现了Runnable,在run方法中调用了

socketHandler.getChannel().register(selector, ops, socketHandler);                
socketHandler.onRegisteredEvent();

 可以看到socketHandler作为了key attachment,ops为读操作。

之后在Dispatcher的run方法中从registerQueue中取出registerTask执行run方法完成注册,可想而知这里的registerQueue是线程安全的ConcurrentLinkedQueue。

这样就完成了新连接的注册以及读事件的监听,这里已经可以看出IoSocketHandler和NonBlockingConnection的重要性。

 

从客户端发出一条字符串信息,Dispatcher run方法里的selector.select就会检测到读事件,调用handleReadWriteKeys()

    private void handleReadWriteKeys() {
        Set<SelectionKey> selectedEventKeys = selector.selectedKeys();
        Iterator<SelectionKey> it = selectedEventKeys.iterator();
        // handle read & write
        while (it.hasNext()) {
            try {
                SelectionKey eventKey = it.next();
                it.remove();//需要移除这个key
                IoSocketHandler socketHandler = (IoSocketHandler) eventKey.attachment();//取出IoSocketHandler
                try {
                    // read data
                    if (eventKey.isValid() && eventKey.isReadable()) {
                        onReadableEvent(socketHandler);//读
                    }
                    // write data
                    if (eventKey.isValid() && eventKey.isWritable()) {
                        onWriteableEvent(socketHandler);//写
                    }
                } catch (Exception e) {
                    socketHandler.close(e);
                }
            } catch (Exception e) {
                // eat and log exception
                if (LOG.isLoggable(Level.FINE)) {
                    LOG.fine("error occured by handling selection keys + " + e.toString());
                }
            }
        }
    }

 Dispatcher的onReadableEvent()调用了IoSocketHandler的onReadableEvent

    long onReadableEvent() throws IOException {long read = 0;
        // read data from socket
        ByteBuffer[] received  = readSocket();//读取数据,里面涉及到ByteBuffer的内存管理后面再讲
        // handle the data
        if (received != null) {
            int size = 0;
            for (ByteBuffer byteBuffer : received) {
                size += byteBuffer.remaining();
            }
            read += size;//作为统计
            getPreviousCallback().onData(received, size);//调用回调函数
            getPreviousCallback().onPostData();//回调
        }
        // increase preallocated read memory if not sufficient
        checkPreallocatedReadMemory();
        return read;
    }

 这里的getPreviousCallback()即返回了之前NonBlockingConnection传的IoHandlerCallback,它是NonBlockingConnection的一个内部类,它的方法直接调用了NonBlockingConnection相应的方法,这个内部类可以看做只是提供了闭包的作用,甚至可以将NonBlockingConnection直接传给IoSocketHandler的。

首先是onData方法,将received 对象加入到了NonBlockingConnection的readQueue中(重要,后面会从此取数据)。然后是onPostData,它调用了

private void onPostData() {
        /**
         * This method will be called (by IoSocketHandler) after the onData method 
         * is called
         */
        //这里获取的就是之前的handlerAdapter,同时将NonBlockingConnection本身以及 NonBlockingConnection的taskQueue,workerpool传给了handlerAdapter
        handlerAdapterRef.get().onData(NonBlockingConnection.this, taskQueue, workerpool, false, false);
}

 HandlerAdapter的onData函数调用了NonblockingConnection的taskQueue的如下方法

taskQueue.performMultiThreaded(new PerformOnDataTask(connection, taskQueue, ignoreException, (IDataHandler) handler), workerpool);

这里的taskQueue就是之前提到的SerializedTaskQueue,它封装了一个Runnable 链表保存任务,一个执行线程执行链表中的任务。上面的方法将新创建的PerFormOnDataTask加入到了链表,并使用workpool启动了执行线程,这样就执行了PerformOnData的run方法。之前的操作都是在Dispatcher线程中,到此Dispatcher线程重新回到循环中等待新的事件。也就是每次我们的逻辑代码都是用的独立的线程执行的,PerformOnDataTask的run方法调用了performOnData函数

    private static void performOnData(INonBlockingConnection connection, SerializedTaskQueue taskQueue, boolean ignoreException, IDataHandler handler) 
        try { 
            // loop until readQueue is empty or nor processing has been done (readQueue has not been modified)
            while ((connection.available() != 0) && !connection.isReceivingSuspended()) {    
                if (connection.getHandler() != handler) {
                    if (LOG.isLoggable(Level.FINE)) {
                        LOG.fine("[" + connection.getId() + "] handler " + " replaced by " + connection.getHandler() + ". stop handling data for old handler");
                    }
                    return;
                }
                handler.onData(connection);
            }...省略了部分代码

 

 调用了handler的onData方法,这个handler就是我们的上一篇文章中的EchoHandler,在onData函数中我们调用了

String data = nbc.readStringByDelimiter("\r\n"); //这里的nbc就是上面的connection

 

 这里的readStringByDelimiter调用了readByteBufferByDelimiter

ByteBuffer[] buffers = readQueue.readByteBufferByDelimiter(delimiter.getBytes(encoding), maxLength);

 

 这里可以看到从Connection的readQueue中取数据,到此接收读取数据就完成了。(这里有tcp分包的处理,后续再讲)

 

再看写数据,在EchoHandler里读完数据后我们调用了下面的代码写数据

nbc.write(data + "\r\n");

 

其调用了如下方法

public int write(String message, String encoding) throws IOException, BufferOverflowException, ClosedChannelException {
        ensureStreamIsOpenAndWritable();
        
        ByteBuffer buffer = DataConverter.toByteBuffer(message, encoding);//将字符串转换为ByteBuffer,encoding为默认的utf-8
        int written = buffer.remaining();//数据字节数

        writeQueue.append(buffer);//加入到写队列中
        onWriteDataInserted();//调用了synchronWriter.syncWrite(recoveryBuffer)进行同步写,当数据写完才会返回
return written;
}

 

onWriteDataInserted()函数将数据从wirteQueue取出加入到IoSocketHandler的sendQueue中,同时注册写事件,写方法被阻塞等待写完成,通过synchronWrite.this.wait(remainingTime)实现,当然也可能配置为非阻塞写。

dispatcher.addKeyUpdateTask(setWriteSelectionKeyTask);//这里也是将task加入到了Dispatcher的并发队列keyUpdateQueue中

 

之后Dispatcher更新键集,触发写事件

onWriteableEvent(socketHandler);

 

 最终调用

    private void writeSocket() throws IOException {
        if (isOpen()) {
            IWriteTask writeTask = null;
            // does former task exists?
            if (pendingWriteTask != null) {
                writeTask = pendingWriteTask;
                pendingWriteTask = null;// no, create a new one
            } else {
                try {
                    writeTask = TaskFactory.newTask(sendQueue, soSendBufferSize);//创建写任务
                } catch (Throwable t) {
                    throw ConnectionUtils.toIOException(t);
                }
            }
            // perform write task
            IWriteResult result = writeTask.write(this); //从sendQueue中取出数据写到channel里,这里并没有新建线程,客户端socket这步就接收到了消息        
            // is write task not complete?
            if (result.isAllWritten()) {
                sendQueue.removeLeased();
                writeTask.release();
                result.notifyWriteCallback();//唤醒前面等待写完的线程,也即工作线程返回
            } else {
                pendingWriteTask = writeTask;
            } 
        } 

 

 这样就完成了写,之后取消掉写操作

boolean isKeyUpdated = dispatcher.unsetWriteSelectionKeyNow(this);//this即当前IoSocketHandler

 

 这样就完成了一次读写操作。

 

posted @ 2015-01-13 15:49  小码路  阅读(917)  评论(0编辑  收藏  举报