zoukankan      html  css  js  c++  java
  • 深入刨析tomcat 之---第2篇,解决第3章bug 页面不显示内容http://localhost:8080/servlet/ModernServlet?userName=zhangyantao&password=1234

    writedby 张艳涛7月2日,

    在学习第4张的过程中,发现了前一篇文章写的是关于1,2张的bug不用设置response响应头,需要在servlet的service()方法里面写是错误想

    servlet是tomcat的容器内的居住者,主要功能在于交互式地浏览和生成数据,生成动态Web内容,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。那么这个servlet实际上是基于http协议的响应体的实现,可以这么说,servlet本质上是对于一个http请求的响应,servlet程序段service()方法,根据http协议来回复客户端的http请求, 对于一个http 请求,回复的内容在哪里? 在response的body里面,如果你是一个网页那么,Content-Type="text/html", response.setContentType("text/html");其实如果你不设置content_type浏览器也能只能的显示为网页;突然发现浏览器很傻_B,如果你返回给浏览器为json格式那么你就给一个Content-Type:

    application/json;charset=UTF-8. 让后在写一个{} json串就行了,如果你是个图片
     out = new PrintWriter(socket.getOutputStream());
                    if (filePath.endsWith("jpg") || filePath.endsWith("ico")) {    
                        in = new FileInputStream(filePath);//读图片
                        ByteArrayOutputStream baos = new ByteArrayOutputStream();
                        int i=0;
                        while (((i = in.read()) != -1)) {
                            baos.write(i);
                        }
                        byte[] array= baos.toByteArray();
                        out.println("HTTP/1.1 200 OK");
                        out.println("Server: Molly");
                        out.println("Content-Type: image/jpeg");
                        out.println("Content-Length: "+array.length);
                        out.println("");
                        out.flush();
                        socket.getOutputStream().write(array,0,array.length);//写入图片
                        System.out.println(Thread.currentThread().getName());
    
            

    这样子就成功了,

    上一篇文章对于response头信息的设置实现

    对于xxx.servlet文件中

        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("<html>");

    如果out.println("内容"),这个out是socket.getoutputstream的对象,肯定不行,那么就直接发送了,如果保证在头对象之后是这个设计的精髓

    这个out对象是

    ResponseStream

    因为response.getWriter();内容为

    HttpResponse类中
    @Override
        public PrintWriter getWriter() throws IOException {
            ResponseStream newStream = new ResponseStream(this);
            newStream.setCommit(false);
            OutputStreamWriter osr =
                    new OutputStreamWriter(newStream, getCharacterEncoding());
            writer = new ex03.pyrmont.connector.ResponseWriter(osr);
            return writer;
        }

    那么久确定了out.println("内容"),的out是ResponseStream ,那么会通过printwrite调用write()

        public void write(byte b[], int off, int len) throws IOException {
            if (closed)
                throw new IOException("responseStream.write.closed");
    
            int actual = len;
            if ((length > 0) && ((count + len) >= length))
                actual = length - count;
            response.write(b, off, actual);
            count += actual;
            if (actual < len)
                throw new IOException("responseStream.write.count");
    
        }

    那么

    HttpResponse类中
        public void write(int b) throws IOException{
            if (bufferCount>=buffer.length) {
                flushBuffer();
            }
            buffer[bufferCount++] = ((byte) b);
            contentCount++;
        }
    
        public void write(byte b[],int off,int len) throws IOException{
            if (len==0) {
                return;
            }
            if (len<=buffer.length-bufferCount) {
                System.arraycopy(b,off,buffer,bufferCount,len);
                bufferCount += len;
                contentCount += len;
                return;
            }
            flushBuffer(); /** 将buffercount置为0;清空buffer*/
            int iterations= len/buffer.length; /**len太大 看一共有几倍 */
            int leftoverStart=iterations*buffer.length;
            int leftoverLen = len - leftoverStart;
            for (int i = 0; i < iterations; i++) {
                write(b, off + (i * buffer.length), buffer.length);
            }
            if(leftoverLen>0){
                write(b, off + leftoverStart, leftoverLen);
            }
        }

    看代码,层层分析之后能发现,最终调用的给buffer数组赋值,只有到flush之后才会发送网络数据,那么,要做的就是先调用sendHeadrs在out.flush();

    ServletProcessor类中
                servlet = ((Servlet) myClass.newInstance());
                HttpRequestFacade httpRequestFacade = new HttpRequestFacade(request);
                HttpResponseFacade httpResponseFacade = new HttpResponseFacade(response);
                servlet.service(httpRequestFacade,httpResponseFacade);
                ((HttpResponse) response).finishResponse();

    接着

        public void finishResponse() throws IOException {
            sendHeaders();
            if (writer != null) {//   writer = new ex03.pyrmont.connector.ResponseWriter(osr);,这里是
                writer.flush();
                writer.close();
            }
        }

    代码和上面的描述一致的

    那么,看sendheaders代码

        protected void sendHeaders() throws IOException{
            if (isCommitted()) {
                return;
            }
            OutputStreamWriter osr = null;
            try {
                osr = new OutputStreamWriter(getStream(), getCharacterEncoding());
            } catch (UnsupportedEncodingException e) {
                osr  = new OutputStreamWriter(getStream());
            }
            final PrintWriter outputWriter = new PrintWriter(osr);
    
            // Send the "Status:" header
            outputWriter.print((this.request.getProtocol()));
            outputWriter.print(" ");
            outputWriter.print(status);
            if(message!=null){
                outputWriter.print(" ");
                outputWriter.print(message);
            }
            outputWriter.print("
    ");
            if(getContentType()!=null){
                outputWriter.print(" ");
                outputWriter.print("Content-Type: " + getContentType() + "
    ");
            }
            if (getContentLength() >= 0) {
                outputWriter.print("Content-Length: " + getContentLength() + "
    ");
            }
            synchronized (headers) {
                Iterator names = headers.keySet().iterator();
                while (names.hasNext()) {
                    String name = (String) names.next();
                    ArrayList values = (ArrayList) headers.get(name);
                    Iterator items = values.iterator();
                    while (items.hasNext()) {
                        String value = (String) items.next();
                        outputWriter.print(name);
                        outputWriter.print(": ");
                        outputWriter.print(value);
                        outputWriter.println("
    ");
                    }
                }
            }
            synchronized (cookies) {
                Iterator items = cookies.iterator();
                while (items.hasNext()) {
                    Cookie cookie = (Cookie) items.next();
                    outputWriter.print(CookieTools.getCookieHeaderName(cookie));
                    outputWriter.print(": ");
                    outputWriter.print(CookieTools.getCookieHeaderValue(cookie));
                    outputWriter.print("
    ");
                }
    
            }
            outputWriter.print("
    ");
            outputWriter.flush();
            committed = true;
    
        }

    这个outwriter直接取值socket.getoutputstream方法

    以上代码全来深入刨析tocmat第3章

    那么第三章的bug是什么呢?,浏览器执行

    http://localhost:8080/servlet/ModernServlet?userName=zhangyantao&password=1234

    运行程序页面没反应!!!

    给程序打断点也发送sendHeaders,这颗怎么办呢?chrome f12 看不出来什么呢,只能看人才的了

    package com.zyt.tomcat.ex03.startup;
    
    import java.io.*;
    import java.net.Socket;
    
    public class Client {
        public static void main(String[] args) throws IOException {
            Socket socket = new Socket("localhost", 8080);
            System.out.println(socket);
            PrintWriter pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
            InputStream inputStream = socket.getInputStream();
    
            pw.println("GET /servlet/ModernServlet?userName=zhangyantao&password=1234l HTTP/1.1");
            pw.println("Host: localhost:8080");
            pw.println("Connection: keep-alive
    " +
                    "sec-ch-ua: " Not;A Brand";v="99", "Google Chrome";v="91", "Chromium";v="91"
    " +
                    "sec-ch-ua-mobile: ?0
    " +
                    "Upgrade-Insecure-Requests: 1
    " +
                    "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36
    " +
                    "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
    " +
                    "Sec-Fetch-Site: none
    " +
                    "Sec-Fetch-Mode: navigate
    " +
                    "Sec-Fetch-User: ?1
    " +
                    "Sec-Fetch-Dest: document
    " +
                    "Accept-Encoding: gzip, deflate, br
    " +
                    "Accept-Language: zh,zh-TW;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6
    " +
                    "Cookie: Webstorm-33fda740=2357c3d1-7421-48dd-b63d-9dfa59664905; Idea-d43876f9=54507cfb-5164-4924-84ad-1ddc6e24306e");
            pw.println("
    
    ");
            pw.flush();
            int b;
            while ((b=inputStream.read())!=-1){
                System.out.print(((char) b));
            }
    
        }
    }

    写了一个client,注意必须是 否则不行

    打印的内容为

    D:devJDK8injava.exe -javaagent:D:devideaIU-2020.3.1.winlibidea_rt.jar=58985:D:devideaIU-2020.3.1.winin -Dfile.encoding=UTF-8 -classpath D:devJDK8jrelibcharsets.jar;D:devJDK8jrelibdeploy.jar;D:devJDK8jrelibextaccess-bridge-64.jar;D:devJDK8jrelibextcldrdata.jar;D:devJDK8jrelibextdnsns.jar;D:devJDK8jrelibextjaccess.jar;D:devJDK8jrelibextjfxrt.jar;D:devJDK8jrelibextlocaledata.jar;D:devJDK8jrelibext
    ashorn.jar;D:devJDK8jrelibextsunec.jar;D:devJDK8jrelibextsunjce_provider.jar;D:devJDK8jrelibextsunmscapi.jar;D:devJDK8jrelibextsunpkcs11.jar;D:devJDK8jrelibextzipfs.jar;D:devJDK8jrelibjavaws.jar;D:devJDK8jrelibjce.jar;D:devJDK8jrelibjfr.jar;D:devJDK8jrelibjfxswt.jar;D:devJDK8jrelibjsse.jar;D:devJDK8jrelibmanagement-agent.jar;D:devJDK8jrelibplugin.jar;D:devJDK8jrelib
    esources.jar;D:devJDK8jrelib
    t.jar;D:wksp_studydesignbook	argetclasses;D:
    epojunitjunit4.11junit-4.11.jar;D:
    epoorghamcresthamcrest-core1.3hamcrest-core-1.3.jar;D:
    epoorgopenjdkjoljol-core0.10jol-core-0.10.jar;D:
    epojavaxservletservlet-api2.3servlet-api-2.3.jar;D:wksp_studydesignbooklibjaas.jar;D:wksp_studydesignbooklibjnet.jar;D:wksp_studydesignbooklibjsse.jar;D:wksp_studydesignbooklibmx4j.jar;D:wksp_studydesignbooklibjcert.jar;D:wksp_studydesignbooklibservlet.jar;D:wksp_studydesignbooklibxercesImpl.jar;D:wksp_studydesignbooklib	omcat-util.jar;D:wksp_studydesignbooklib
    aming-common.jar;D:wksp_studydesignbooklibxmlParserAPIs.jar;D:wksp_studydesignbooklibcommons-daemon.jar;D:wksp_studydesignbooklib
    aming-factory.jar;D:wksp_studydesignbooklibcommons-logging.jar;D:wksp_studydesignbooklibcommons-modeler.jar;D:wksp_studydesignbooklibcommons-digester.jar;D:wksp_studydesignbooklib
    aming-resources.jar;D:wksp_studydesignbooklibcommons-beanutils.jar;D:wksp_studydesignbooklibjakarta-regexp-1.2.jar;D:wksp_studydesignbooklibcommons-collections.jar com.zyt.tomcat.ex03.startup.Client
    Socket[addr=localhost/127.0.0.1,port=8080,localport=58988]
    <html>
    <head>
    <title>Modern Servlet</title>
    </head>
    <body>
    <h2>Headers</h2
    <br>sec-fetch-mode : navigate
    <br>sec-fetch-site : none
    <br>accept-language : zh,zh-TW;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6
    <br>cookie : Webstorm-33fda740=2357c3d1-7421-48dd-b63d-9dfa59664905; Idea-d43876f9=54507cfb-5164-4924-84ad-1ddc6e24306e
    <br>sec-fetch-user : ?1
    <br>accept : text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
    <br>sec-ch-ua : " Not;A Brand";v="99", "Google Chrome";v="91", "Chromium";v="91"
    <br>sec-ch-ua-mobile : ?0
    <br>host : localhost:8080
    <br>upgrade-insecure-requests : 1
    <br>connection : keep-alive
    <br>accept-encoding : gzip, deflate, br
    <br>user-agent : Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36
    <br>sec-fetch-dest : document
    <br><h2>Method</h2
    <br>GET
    <br><h2>Parameters</h2
    <br>password : 1234l
    <br>userName : zhangyantao
    HTTP/1.1 200 OK
    Server: Pyrmont Servlet Container
    
    
    <br><h2>Query String</h2
    <br>userName=zhangyantao&password=1234l
    <br><h2>Request URI</h2
    <br>/servlet/ModernServlet
    </body>
    </html>
    
    Process finished with exit code 0

    发现发送了响应头,但是位置不对,进一步发现是他程序写错了

        public void write(byte b[],int off,int len) throws IOException{
            if (len==0) {
                return;
            }
            if (len<=buffer.length-bufferCount) {
                System.arraycopy(b,off,buffer,bufferCount,len);
                bufferCount += len;
                contentCount += len;
                return;
            }
            flushBuffer(); /** 将buffercount置为0;清空buffer*/
            int iterations= len/buffer.length; /**len太大 看一共有几倍 */
            int leftoverStart=iterations*buffer.length;
            int leftoverLen = len - leftoverStart;
            for (int i = 0; i < iterations; i++) {
                write(b, off + (i * buffer.length), buffer.length);
            }
            if(leftoverLen>0){
                write(b, off + leftoverStart, leftoverLen);
            }
        }

    缓存区大小

    public class HttpResponse  implements HttpServletResponse {
        private static final int BUFFER_SIZE=1024;
    
        protected byte[] buffer = new byte[BUFFER_SIZE];
    }

    如果缓存区满了 ,内容还没写完超过了1024个字节,他就发送了,那么就比响应头的发送提前了,之后才会执行sendheaders和发送后一部分内容

    如果你设置  private static final int BUFFER_SIZE=4096;

    那么你就获取了一个惊喜

    D:devJDK8injava.exe -javaagent:D:devideaIU-2020.3.1.winlibidea_rt.jar=57838:D:devideaIU-2020.3.1.winin -Dfile.encoding=UTF-8 -classpath D:devJDK8jrelibcharsets.jar;D:devJDK8jrelibdeploy.jar;D:devJDK8jrelibextaccess-bridge-64.jar;D:devJDK8jrelibextcldrdata.jar;D:devJDK8jrelibextdnsns.jar;D:devJDK8jrelibextjaccess.jar;D:devJDK8jrelibextjfxrt.jar;D:devJDK8jrelibextlocaledata.jar;D:devJDK8jrelibext
    ashorn.jar;D:devJDK8jrelibextsunec.jar;D:devJDK8jrelibextsunjce_provider.jar;D:devJDK8jrelibextsunmscapi.jar;D:devJDK8jrelibextsunpkcs11.jar;D:devJDK8jrelibextzipfs.jar;D:devJDK8jrelibjavaws.jar;D:devJDK8jrelibjce.jar;D:devJDK8jrelibjfr.jar;D:devJDK8jrelibjfxswt.jar;D:devJDK8jrelibjsse.jar;D:devJDK8jrelibmanagement-agent.jar;D:devJDK8jrelibplugin.jar;D:devJDK8jrelib
    esources.jar;D:devJDK8jrelib
    t.jar;D:wksp_studydesignbook	argetclasses;D:
    epojunitjunit4.11junit-4.11.jar;D:
    epoorghamcresthamcrest-core1.3hamcrest-core-1.3.jar;D:
    epoorgopenjdkjoljol-core0.10jol-core-0.10.jar;D:
    epojavaxservletservlet-api2.3servlet-api-2.3.jar;D:wksp_studydesignbooklibjaas.jar;D:wksp_studydesignbooklibjnet.jar;D:wksp_studydesignbooklibjsse.jar;D:wksp_studydesignbooklibmx4j.jar;D:wksp_studydesignbooklibjcert.jar;D:wksp_studydesignbooklibservlet.jar;D:wksp_studydesignbooklibxercesImpl.jar;D:wksp_studydesignbooklib	omcat-util.jar;D:wksp_studydesignbooklib
    aming-common.jar;D:wksp_studydesignbooklibxmlParserAPIs.jar;D:wksp_studydesignbooklibcommons-daemon.jar;D:wksp_studydesignbooklib
    aming-factory.jar;D:wksp_studydesignbooklibcommons-logging.jar;D:wksp_studydesignbooklibcommons-modeler.jar;D:wksp_studydesignbooklibcommons-digester.jar;D:wksp_studydesignbooklib
    aming-resources.jar;D:wksp_studydesignbooklibcommons-beanutils.jar;D:wksp_studydesignbooklibjakarta-regexp-1.2.jar;D:wksp_studydesignbooklibcommons-collections.jar com.zyt.tomcat.ex03.startup.Client
    Socket[addr=localhost/127.0.0.1,port=8080,localport=57842]
    HTTP/1.1 200 OK
    Server: Pyrmont Servlet Container
    
    
    <html>
    <head>
    <title>Modern Servlet</title>
    </head>
    <body>
    <h2>Headers</h2
    <br>sec-fetch-mode : navigate
    <br>sec-fetch-site : none
    <br>accept-language : zh,zh-TW;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6
    <br>cookie : Webstorm-33fda740=2357c3d1-7421-48dd-b63d-9dfa59664905; Idea-d43876f9=54507cfb-5164-4924-84ad-1ddc6e24306e
    <br>sec-fetch-user : ?1
    <br>accept : text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
    <br>sec-ch-ua : " Not;A Brand";v="99", "Google Chrome";v="91", "Chromium";v="91"
    <br>sec-ch-ua-mobile : ?0
    <br>host : localhost:8080
    <br>upgrade-insecure-requests : 1
    <br>connection : keep-alive
    <br>accept-encoding : gzip, deflate, br
    <br>user-agent : Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36
    <br>sec-fetch-dest : document
    <br><h2>Method</h2
    <br>GET
    <br><h2>Parameters</h2
    <br>password : 1234l
    <br>userName : zhangyantao
    <br><h2>Query String</h2
    <br>userName=zhangyantao&password=1234l
    <br><h2>Request URI</h2
    <br>/servlet/ModernServlet
    </body>
    </html>
    
    Process finished with exit code 0

    浏览器正常显示页面

    这书,怎么好呢?解决一个问题太费劲了...一章一个问题,真是费劲呢...,

    接下来分析长连接的实现吧,和短链接的区别

  • 相关阅读:
    ASP.NET Core: What I learned!
    Entity Framework Core with GraphQL and SQL Server using HotChocolate
    Angular 9 Chart.js with NG2-Charts Demo
    POST调用WCF方法-项目实践
    项目实战-登录速度优化笔记
    MP4视频流base64数据转成Blob对象
    使用Vue+ElementUI实现前端分页
    JS端实现图片、视频时直接下载而不是打开预览
    Dynamic CRM工作流流程实战
    Dynamic CRM插件调试与单元测试
  • 原文地址:https://www.cnblogs.com/zytcomeon/p/14964199.html
Copyright © 2011-2022 走看看