HttpServletResponse Writer 是怎么把数据写到网络上的?

本文深入解析了HTTP响应如何从应用层写入到底层Socket的过程。从CoyoteWriter到OutputBuffer,再到Http11OutputBuffer,直至最终的数据在网络层面的传输,详细介绍了每个环节的实现机制和关键步骤。

问题:

如下代码底层是如何把响应写到socket的???

一般常用写法为:

// 清空所有的头信息、状态码、数据缓存buffer
response.reset();
response.setContentType("charset=utf-8");
response.setStatus(200);

// 写到缓存区域,如果写的内容大于缓冲区域buffer,会自动flush到网络上,翻过来说,如果内容不多,则先存在缓存中,等内容多了一并写到网络上,这样提高效率
response.getWriter().write(responseString);

// 如果缓存中还剩下没写到网络的内容,则写到网络上
response.getWriter().flush();
response.getWriter().close();

write的流程

org.apache.catalina.connector.CoyoteWriter 类

(1)CoyoteWriter#write() [1]

(2)使用CoyoteWriter持有的OutputBuffer#write()执行

org.apache.catalina.connector.OutputBuffer 类

[1] public void write(String s, int off, int len) throws IOException {
                。。。
                while(sOff < sEnd) {
                    // translate的作用是把String类型的s写到缓存CharBuffer cb 中(里面有个char[]数组)
                    int n = this.transfer(s, sOff, sEnd - sOff, this.cb);

                    sOff += n;
                    // 如果数据缓存长度已满,则直接flush,下面详细说明flush
                    if (this.isFull(this.cb)) {
                        this.flushCharBuffer();[2]
                    }

                }
                。。。
}

[2] private void flushCharBuffer() throws IOException {
        // 正真写数据,从char[]中读出
        this.realWriteChars(this.cb.slice());[3]
        。。。。。。
    }

[3] public void realWriteChars(CharBuffer from) throws IOException {
        while(true) {
            if (from.remaining() > 0) {
                // 将char[]缓存成byte[]缓存,实际写到网络上时,都是从byte[]缓存中读取出来的
                this.conv.convert(from, this.bb);

                if (this.bb.remaining() != 0) {
                    if (from.remaining() > 0) {
                        // 写到网络上
                        this.flushByteBuffer();[4]
                        continue;
                    }

                    if (this.conv.isUndeflow() && this.bb.limit() > this.bb.capacity() - 4) {
                       // byte[]的缓存写到网络上
                        this.flushByteBuffer();
                    }
                    continue;
                }
            }

            return;
        }
    }

通过名字就看的出来,是从byte[]中读出再写到网络上

   [4] private void flushByteBuffer() throws IOException {
        this.realWriteBytes(this.bb.slice());
        。。。。。。
    }

public void realWriteBytes(ByteBuffer buf) throws IOException {
       。。。。。。
       // 使用response写buffer
       this.coyoteResponse.doWrite(buf);   [5]         
    }

org.apache.coyote.Response 类

[5] public void doWrite(ByteBuffer chunk) throws IOException {
        。。。
        this.outputBuffer.doWrite(chunk);[6]
        。。。
   } 

outputBuffer的运行类型是一个org.apache.coyote.http11.Http11OutputBuffer类

org.apache.coyote.http11.Http11OutputBuffer 类

[6] public int doWrite(ByteBuffer chunk) throws IOException {
        if (!this.response.isCommitted()) {

            // ActionCode.COMMIT就是表示数据要提交,发送到网络中
            this.response.action(ActionCode.COMMIT, (Object)null); [7]

        }

       。。。。。。
    }


org.apache.coyote.AbstractProcessor 类

 [7] public final void action(ActionCode actionCode, Object param) {
        AtomicBoolean result;
        switch(actionCode) {
        case COMMIT:
            if (!this.response.isCommitted()) {
                    this.prepareResponse(); [8]
            }

       case CLOSE:
            this.action(ActionCode.COMMIT, (Object)null);
            this.finishResponse(); 
            break;

        case ACK:
            this.ack();
            break;

根据Writer的操作,底层会映射不同的写Http网络的操作,使用这些case来匹配实现

prepareResponse、finishResponse都是​​​​​​​使用实现类 org.apache.coyote.http11.Http11Processor 类

[8] protected final void prepareResponse() throws IOException {
  。。。
   // 实际的发送到网络端
  this.outputBuffer.commit(); [9]
}

org.apache.coyote.http11.Http11OutputBuffer 类

[9] protected void commit() throws IOException {

               // 使用 SocketWrapperBase 和  ByteBuffer 把数据写到网络中

                socketWrapper.write(this.isBlocking(), this.headerBuffer);
          
        }

    }

org.apache.tomcat.util.net.NioEndpoint(内部类:NioSocketWrapper)类

最终使用内部类NioSocketWrapper实现写到网络中

protected void doWrite(boolean block, ByteBuffer from)  {

   this.pool.write(from, socket, selector, writeTimeout);
   if (block) {
            while(!socket.flush(true, selector, writeTimeout)) {
   }
}

再探究下Writer的三个方法:

response.getWriter().write(responseString);
response.getWriter().flush();
response.getWriter().close();

我们的以下三个操作,都会调用到flushCharBuffer;而close方法,还在调用了flushCharBuffer后,调用了coyoteResponse.action(ActionCode.CLOSE, (Object)null);来结束网络

 



 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值