一般的开发者write和flush数据,都是基于ChannelHandlerContext ctx,然后调用其相应的write和flush方法。下面分别对这两个方法进行代码分析。(这里顺便提示一下,这两个方法都是从应用往底层发数据,属于OutboundHandler类型。如果没有特别的需求,我们不需要定义自己的handler,可以使用默认的Handler,这个在后面的分析中会体现。)
1、write方法
下面是ChannelHandlerContext的具体类DefaultChannelHandlerContext中write的相关代码
- @Override
- public ChannelFuture write(Object msg) {
- return write(msg, newPromise()); //构造了一个newPromise对象,调用带promise的write方法,
- }
- @Override
- public ChannelFuture write(final Object msg, final ChannelPromise promise) {
- if (msg == null) {
- throw new NullPointerException("msg");
- }
- validatePromise(promise, true);//验证promise
- write(msg, false, promise);//再一次调用write方法,flush参数设置为false,这个参数比较重要,真正的数据发送过程是flush,后面会提到
- return promise;
- }
- private void write(Object msg, boolean flush, ChannelPromise promise) {
- DefaultChannelHandlerContext next = findContextOutbound();
- EventExecutor executor = next.executor();
- if (executor.inEventLoop()) {
- next.invokeWrite(msg, promise);//调用invokeWrite函数
- if (flush) {
- next.invokeFlush();
- }
- } else {
- int size = channel.estimatorHandle().size(msg);
- if (size > 0) {
- ChannelOutboundBuffer buffer = channel.unsafe().outboundBuffer();
- // Check for null as it may be set to null if the channel is closed already
- if (buffer != null) {
- buffer.incrementPendingOutboundBytes(size, false);
- }
- }
- executor.execute(WriteTask.newInstance(next, msg, size, flush, promise));
- }
- }
- pre name="code" class="java"> private void invokeWrite(Object msg, ChannelPromise promise) {
- try {
- ((ChannelOutboundHandler) handler).write(this, msg, promise);//调用handler的write方法,这里我们使用HeadHandler默认的Handler方法。
- } catch (Throwable t) {
- notifyOutboundHandlerException(t, promise);
- }
- }
- @Override
- public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
- unsafe.write(msg, promise);
- }
- @Override
- public void write(Object msg, ChannelPromise promise) {
- if (!isActive()) {
- // Mark the write request as failure if the channel is inactive.
- if (isOpen()) {
- promise.tryFailure(NOT_YET_CONNECTED_EXCEPTION);
- } else {
- promise.tryFailure(CLOSED_CHANNEL_EXCEPTION);
- }
- // release message now to prevent resource-leak
- ReferenceCountUtil.release(msg);
- } else {
- outboundBuffer.addMessage(msg, promise);
- }
- }
2、flush方法
上面讲到flush是真正的发送数据的过程,所以这个方法还是比较关键的。先来看看DefaultChannelHandlerContext中的相关代码:
- @Override
- public ChannelHandlerContext flush() {
- final DefaultChannelHandlerContext next = findContextOutbound();
- EventExecutor executor = next.executor();
- if (executor.inEventLoop()) {
- next.invokeFlush();
- } else {
- Runnable task = next.invokeFlushTask;
- if (task == null) {
- next.invokeFlushTask = task = new Runnable() {
- @Override
- public void run() {
- next.invokeFlush();
- }
- };
- }
- executor.execute(task);
- }
- return this;
- }
- private void invokeFlush() {
- try {
- ((ChannelOutboundHandler) handler).flush(this);
- } catch (Throwable t) {
- notifyHandlerException(t);
- }
- }
- @Override
- public void flush(ChannelHandlerContext ctx) throws Exception {
- unsafe.flush();
- }
- @Override
- public void flush() {
- ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
- if (outboundBuffer == null) {
- return;
- }
- outboundBuffer.addFlush();
- flush0();
- }
- protected void flush0() {
- if (inFlush0) {
- // Avoid re-entrance
- return;
- }
- final ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
- if (outboundBuffer == null || outboundBuffer.isEmpty()) {
- return;
- }
- inFlush0 = true;
- // Mark all pending write requests as failure if the channel is inactive.
- if (!isActive()) {
- try {
- if (isOpen()) {
- outboundBuffer.failFlushed(NOT_YET_CONNECTED_EXCEPTION);
- } else {
- outboundBuffer.failFlushed(CLOSED_CHANNEL_EXCEPTION);
- }
- } finally {
- inFlush0 = false;
- }
- return;
- }
- try {
- doWrite(outboundBuffer);
- } catch (Throwable t) {
- outboundBuffer.failFlushed(t);
- if (t instanceof IOException) {
- close(voidPromise());
- }
- } finally {
- inFlush0 = false;
- }
- }
- @Override
- protected void doWrite(ChannelOutboundBuffer in) throws Exception {
- int writeSpinCount = -1;
- for (;;) {
- Object msg = in.current();
- if (msg == null) {
- // Wrote all messages.
- clearOpWrite();
- break;
- }
- if (msg instanceof ByteBuf) {
- ByteBuf buf = (ByteBuf) msg;
- int readableBytes = buf.readableBytes();
- if (readableBytes == 0) {
- in.remove();
- continue;
- }
- if (!buf.isDirect()) {
- ByteBufAllocator alloc = alloc();
- if (alloc.isDirectBufferPooled()) {
- // Non-direct buffers are copied into JDK's own internal direct buffer on every I/O.
- // We can do a better job by using our pooled allocator. If the current allocator does not
- // pool a direct buffer, we rely on JDK's direct buffer pool.
- buf = alloc.directBuffer(readableBytes).writeBytes(buf);
- in.current(buf);
- }
- }
- boolean done = false;
- long flushedAmount = 0;
- if (writeSpinCount == -1) {
- writeSpinCount = config().getWriteSpinCount();
- }
- for (int i = writeSpinCount - 1; i >= 0; i --) {
- int localFlushedAmount = doWriteBytes(buf);
- if (localFlushedAmount == 0) {
- break;
- }
- flushedAmount += localFlushedAmount;
- if (!buf.isReadable()) {
- done = true;
- break;
- }
- }
- in.progress(flushedAmount);
- if (done) {
- in.remove();
- } else {
- // Did not write completely.
- setOpWrite();
- break;
- }
- } else if (msg instanceof FileRegion) {
- FileRegion region = (FileRegion) msg;
- boolean done = false;
- long flushedAmount = 0;
- if (writeSpinCount == -1) {
- writeSpinCount = config().getWriteSpinCount();
- }
- for (int i = writeSpinCount - 1; i >= 0; i --) {
- long localFlushedAmount = doWriteFileRegion(region);
- if (localFlushedAmount == 0) {
- break;
- }
- flushedAmount += localFlushedAmount;
- if (region.transfered() >= region.count()) {
- done = true;
- break;
- }
- }
- in.progress(flushedAmount);
- if (done) {
- in.remove();
- } else {
- // Did not write completely.
- setOpWrite();
- break;
- }
- } else {
- throw new UnsupportedOperationException("unsupported message type: " + StringUtil.simpleClassName(msg));
- }
- }
- }
- @Override
- protected int doWriteBytes(ByteBuf buf) throws Exception {
- final int expectedWrittenBytes = buf.readableBytes();
- final int writtenBytes = buf.readBytes(javaChannel(), expectedWrittenBytes);
- return writtenBytes;
- }
- @Override
- public int readBytes(GatheringByteChannel out, int length)
- throws IOException {
- checkReadableBytes(length);
- int readBytes = getBytes(readerIndex, out, length);
- readerIndex += readBytes;
- return readBytes;
- }
- @Override
- public int getBytes(int index, GatheringByteChannel out, int length) throws IOException {
- ensureAccessible();
- if (length == 0) {
- return 0;
- }
- ByteBuffer tmpBuf = internalNioBuffer();
- tmpBuf.clear().position(index).limit(index + length);
- return out.write(tmpBuf);
- }
3、总结
本文简单的分析了write和flush的过程,从上到下的理了一遍数据write和flush的完整过程。另外在最后发送数据的时候,我们提到了ByteBuf,这个在后面我会写一篇文章来详细介绍。