how2j.cn

下载区
文件名 文件大小
请先登录 7m
增值内容 7m
7m

5分40秒
本视频采用html5方式播放,如无法正常播放,请将浏览器升级至最新版本,推荐火狐,chrome,360浏览器。 如果装有迅雷,播放视频呈现直接下载状态,请调整 迅雷系统设置-基本设置-启动-监视全部浏览器 (去掉这个选项)。 chrome 的 视频下载插件会影响播放,如 IDM 等,请关闭或者切换其他浏览器

步骤 1 : 拓扑图点亮   
步骤 2 : 迷你浏览器   
步骤 3 : MiniBrowser   
步骤 4 : 比较可运行项目,快速定位问题   

如图所示,点亮了新的节点,整个项目正在逐渐展开。 通过点亮的位置,就知道现在项目进展到哪里了,还剩下多少没做,这样学习思路越来越清晰
拓扑图点亮
增值内容,请先登录
自己写一个Tomcat, 几乎使用到了除开框架外的所有Java 技术,如多线程,Socket, J2EE, 反射,Log4j, JSoup, JUnit, Html 等一整套技术栈, 从无到有,循序渐进涵盖全部74个知识点,549个开发步骤, 为竞争高薪资职位加上一个有吸引力的砝码.
增值内容,点击购买
使用爬虫已经被系统记录,请勿使用爬虫,增大封号风险。 如果是误封 ,请联系站长,谢谢
增值内容,请先登录
自己写一个Tomcat, 几乎使用到了除开框架外的所有Java 技术,如多线程,Socket, J2EE, 反射,Log4j, JSoup, JUnit, Html 等一整套技术栈, 从无到有,循序渐进涵盖全部74个知识点,549个开发步骤, 为竞争高薪资职位加上一个有吸引力的砝码.
增值内容,点击购买
使用爬虫已经被系统记录,请勿使用爬虫,增大封号风险。 如果是误封 ,请联系站长,谢谢
MiniBrowser
package cn.how2j.diytomcat.util; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.net.InetSocketAddress; import java.net.Socket; import java.net.URL; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Set; public class MiniBrowser { public static void main(String[] args) throws Exception { String url = "http://static.how2j.cn/diytomcat.html"; String contentString= getContentString(url,false); System.out.println(contentString); String httpString= getHttpString(url,false); System.out.println(httpString); } public static byte[] getContentBytes(String url) { return getContentBytes(url, false); } public static String getContentString(String url) { return getContentString(url,false); } public static String getContentString(String url, boolean gzip) { byte[] result = getContentBytes(url, gzip); if(null==result) return null; try { return new String(result,"utf-8").trim(); } catch (UnsupportedEncodingException e) { return null; } } public static byte[] getContentBytes(String url, boolean gzip) { byte[] response = getHttpBytes(url,gzip); byte[] doubleReturn = "\r\n\r\n".getBytes(); int pos = -1; for (int i = 0; i < response.length-doubleReturn.length; i++) { byte[] temp = Arrays.copyOfRange(response, i, i + doubleReturn.length); if(Arrays.equals(temp, doubleReturn)) { pos = i; break; } } if(-1==pos) return null; pos += doubleReturn.length; byte[] result = Arrays.copyOfRange(response, pos, response.length); return result; } public static String getHttpString(String url,boolean gzip) { byte[] bytes=getHttpBytes(url,gzip); return new String(bytes).trim(); } public static String getHttpString(String url) { return getHttpString(url,false); } public static byte[] getHttpBytes(String url,boolean gzip) { byte[] result = null; try { URL u = new URL(url); Socket client = new Socket(); int port = u.getPort(); if(-1==port) port = 80; InetSocketAddress inetSocketAddress = new InetSocketAddress(u.getHost(), port); client.connect(inetSocketAddress, 1000); Map<String,String> requestHeaders = new HashMap<>(); requestHeaders.put("Host", u.getHost()+":"+port); requestHeaders.put("Accept", "text/html"); requestHeaders.put("Connection", "close"); requestHeaders.put("User-Agent", "how2j mini brower / java1.8"); if(gzip) requestHeaders.put("Accept-Encoding", "gzip"); String path = u.getPath(); if(path.length()==0) path = "/"; String firstLine = "GET " + path + " HTTP/1.1\r\n"; StringBuffer httpRequestString = new StringBuffer(); httpRequestString.append(firstLine); Set<String> headers = requestHeaders.keySet(); for (String header : headers) { String headerLine = header + ":" + requestHeaders.get(header)+"\r\n"; httpRequestString.append(headerLine); } PrintWriter pWriter = new PrintWriter(client.getOutputStream(), true); pWriter.println(httpRequestString); InputStream is = client.getInputStream(); int buffer_size = 1024; ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte buffer[] = new byte[buffer_size]; while(true) { int length = is.read(buffer); if(-1==length) break; baos.write(buffer, 0, length); if(length!=buffer_size) break; } result = baos.toByteArray(); client.close(); } catch (Exception e) { e.printStackTrace(); try { result = e.toString().getBytes("utf-8"); } catch (UnsupportedEncodingException e1) { e1.printStackTrace(); } } return result; } }
步骤 4 :

比较可运行项目,快速定位问题

edit
增值内容,请先登录
自己写一个Tomcat, 几乎使用到了除开框架外的所有Java 技术,如多线程,Socket, J2EE, 反射,Log4j, JSoup, JUnit, Html 等一整套技术栈, 从无到有,循序渐进涵盖全部74个知识点,549个开发步骤, 为竞争高薪资职位加上一个有吸引力的砝码.
增值内容,点击购买
使用爬虫已经被系统记录,请勿使用爬虫,增大封号风险。 如果是误封 ,请联系站长,谢谢


HOW2J公众号,关注后实时获知最新的教程和优惠活动,谢谢。


问答区域    
2021-06-09 响应报文
yr6




贴一个响应报文格式,会更好理解
加载中

							

							


1 个答案

how2j
答案时间:2021-06-20
Good!



回答已经提交成功,正在审核。 请于 我的回答 处查看回答记录,谢谢
答案 或者 代码至少填写一项, 如果是自己有问题,请重新提问,否则站长有可能看不到





2021-01-19 给各位写了个注释版本的,有问题的可以指出来,大家共同进步
LCJjjj




有错误可以指出来,各位共同进步
package cn.how2j.diytomcat;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @author za-lincanjian
 * @注意:核心是方法:getHttpBytes,看懂这个方法就看懂全部了
 */
public class MiniBrowser {

    public static void main(String[] args) {
        //初始化请求地址,这个请求地址就是待会会去链接的地址
        String url = "http://static.how2j.cn/diytomcat.html";
        //调用该方法,获取 http请求返回值的返回内容(简而言之就是去除掉了返回头的那些字符串)(请进到这个调用方法继续看)
        String contentString = getContentString(url,false);
        System.out.println(contentString);
        //这个方法就是获取全部的Http返回内容的字符串的方法了,各位看了刚才的一些解析这个方法就没啥好说的了
        String httpString = getHttpString(url,false);
        System.out.println(httpString);
    }

    //这些重载方法其实就是备用的,以后可以直接调url,默认不gzip
    public static byte[] getContentBytes(String url) {
        return getContentBytes(url,false);
    }

    //这些重载方法其实就是备用的,以后可以直接调url,默认不gzip
    public static String getContentString(String url) {
        return getContentString(url,false);
    }


    public static String getContentString(String url, boolean gzip) {
        //这里获取返回体具体内容的字节数组,请跟进去看
        byte[] result = getContentBytes(url,gzip);
        //getContentString 表示获取内容的字符串,我们获取到具体内容的字节数组后还需要进行编码
        if (result == null) {
            return null;
        }
        //这里就是一个编码过程了,我这里跟源代码不同,用StandarCahrset这个类可以避免抛异常,这里引入一个知识,因为这个是个常量,jvm可以知道你会选的是utf-8,所以不要求你抛异常
        return new String(result, StandardCharsets.UTF_8).trim();

    }

    public static byte[] getContentBytes(String url, boolean gzip) {
        //这里是真正的逻辑,就是与请求地址建立连接的逻辑,是整个类的核心,其他方法都只是处理这个方法返回值的一些逻辑而已
        byte[] response = getHttpBytes(url,gzip);
        //这个doubleReturnq其实是这样来的:我们获取的返回值正常其实是这样的
        /**(响应头部分)
         *  xxxxx
         *  xxxxx
         *  xxxxx
         *
         *  (具体内容部分,在这个代码中是hello diytomcat)
         *  xxx
         */
        //也就是说响应头部分和具体内容部分其实隔了一行, \r表示回到行首\n表示换到下一行,那么\r\n就相当于说先到了空格一行的那一行的行首,接着又到了具体内容的那部分的行首
        byte[] doubleReturn = "\r\n\r\n".getBytes();

        //接着这里初始化一个记录值,做记录用,往下看
        int pos = -1;
        //开始遍历返回内容
        for (int i = 0;i < response.length - doubleReturn.length;i++) {
            //这里的意思就是不断去初始化一个数组(从原数组进行拷贝),目的其实是为了获取到\r\n这一行的起始位置
            byte[] temp = Arrays.copyOfRange(response,i,i + doubleReturn.length);

            //来到这里,就是比较内容,当走到这里,说明temp这个字节数组的内容就是\r\n\r\n的内容了,说明我们找到了他的其实位置
            if (Arrays.equals(temp,doubleReturn)) {
                //将pos等于i,记录位置
                pos = i;
                break;
            }
        }
        //如果没记录到,那就说明压根没具体内容,那其实就是null
        if (-1 == pos) {
            return null;
        }

        //接着pos就是\r\n\n的第一个\的这个位置,加上\r\n\r\n的长度,相当于来到了具体内容的其实位置
        pos += doubleReturn.length;

        //最后,确定了具体内容是在哪个字节开始,就拷贝这部分内容返回
        byte[] result = Arrays.copyOfRange(response,pos,response.length);
        return result;
    }

    public static String getHttpString(String url,boolean gzip) {
        //这里也没啥了,就是少了截取内容的那部分操作,直接就返回整个返回值的字节数组出来
        byte[] bytes = getHttpBytes(url,gzip);
        return new String(bytes).trim();
    }

    //这些重载方法其实就是备用的,以后可以直接调url,默认不gzip
    public static String getHttpString(String url) {
        return getHttpString(url,false);
    }

    public static byte[] getHttpBytes(String url, boolean gzip) {
        //首先初始化一个返回值,这个返回值是一个字节数组,utf-8编码的
        byte[] result = null;

        try {
            //通过url来new一个URL对象,这样你就不用自己去截取他的端口啊或者请求路径啥的,可以直接调他的方法获取
            URL u = new URL(url);
            //开启一个socket链接,client指的就是你现在的这台计算机
            Socket client = new Socket();
            //获取到端口号,要是端口号是-1,那就默认取80端口(这个端口也是web常用端口)
            int port = u.getPort();
            if (port == -1) {
                port = 80;
            }
            //这个是socket编程的内容,简单来说就是通过一个host+端口,和这个url建立连接
            InetSocketAddress inetSocketAddress = new InetSocketAddress(u.getHost(),port);
            //开始连接了,1000是超时时间,等于说超过1秒就算你超时了
            client.connect(inetSocketAddress,1000);
            //初始化请求头
            Map<String,String> requestHeaders = new HashMap<>();

            //这几个参数都是http请求时会带上的请求头
            requestHeaders.put("Host", u.getHost() + ":" + port);
            requestHeaders.put("Accept", "text/html");
            requestHeaders.put("Connection", "close");
            requestHeaders.put("User-Agent", "how2j mini browser / java1.8");

            //gzip是确定客户端或服务器端是否支持压缩
            if (gzip) {
                requestHeaders.put("Accept_Encoding", "gzip");
            }

            //获取到path,其实就是/diytomcat.html,如果没有的话就默认是/
            String path = u.getPath();
            if (path.length() == 0) {
                path = "/";
            }

            //接着开始拼接请求的字符串,其实所谓的请求头和请求内容就是这么一串字符串拼接出来
            String firstLine = "GET " + path + " HTTP/1.1\r\n";

            StringBuffer httpRequestString = new StringBuffer();
            //拼接firstLine的内容
            httpRequestString.append(firstLine);
            Set<String> headers = requestHeaders.keySet();

            //遍历header的那个map进行拼接
            for (String header : headers) {
                String headerLine = header + ":" + requestHeaders.get(header) + "\r\n";
                httpRequestString.append(headerLine);
            }

            /**走到这的时候,httpRequestString已经拼接好了,内容是:
             GET /diytomcat.html HTTP/1.1
             Accept:text/html
             Connection:close
             User-Agent:how2j mini browser / java1.8
             Host:static.how2j.cn:80
             */
            //通过输出流,将这么一串字符串输出给连接的地址,后面的true是autoFlush,表示开始自动刷新
            PrintWriter printWriter = new PrintWriter(client.getOutputStream(),true);
            printWriter.println(httpRequestString);
            //这时候你已经将需要的请求所需的字符串发给上面那个url了,其实所谓的http协议就是这样,你发给他这么一串符合规范的字符串,他就给你响应,接着他那边就给你返回数据
            //所以这时候我们开启一个输出流
            InputStream inputStream = client.getInputStream();

            //一次接受1m的数据就行了,1024byte = 1m
            int bufferSize = 1024;
            //这里初始化一个输出流,待会存取url返回给我们的数据用
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            //开始new一个1m大小的字节数组
            byte[] buffer = new byte[bufferSize];
            //循环继续读取
            while (true) {
                //从输入流获取数据,调read方法存到buffer数组中
                int length = inputStream.read(buffer);
                //读到的长度如果是-1,说明没读到数据了,直接退出
                if (length == -1) {
                    break;
                }
                //接着先将读到的1m数据输出到我们初始化的那个输出流中
                byteArrayOutputStream.write(buffer,0,length);
                //这里是一个结束的操作,length != bufferSize,说明已经是最后一次读取了,为什么这么说?
                //举个例子,如果你的数据是1025字节,当你第二次循环的时候就是只有一个字节了,这时候就说明处理完这一个字节的数组就可以结束了,因为已经没数据了
                if (length != bufferSize) {
                    break;
                }
            }
            //通过方法,将这个输出流返回成字节数组result,为什么要用这个输出流来存返回的字节数组呢?因为如果你用数组的话其实你不能确定
            //整个返回数据有多大,用集合理论上是可以实现的,各位有兴趣可以试试
            result = byteArrayOutputStream.toByteArray();
            //这是个好习惯,不过最好是放在finally进行关闭比较好,这里就是关闭连接了
            client.close();
        } catch (Exception e) {
            //接着这里是异常打印
            e.printStackTrace();
            //这里是将返回的异常信息进行字节数组编码,其实就是兼容这个方法
            try {
                result = e.toString().getBytes("utf-8");

            } catch (UnsupportedEncodingException e1) {
                e1.printStackTrace();
            }
        }

        //返回结果
        return result;
    }
}

							


3 个答案

刺客五六七
答案时间:2021-02-01
厉害,膜拜大哥

gjian707
答案时间:2021-01-24
很强

how2j
答案时间:2021-01-22
good and nice



回答已经提交成功,正在审核。 请于 我的回答 处查看回答记录,谢谢
答案 或者 代码至少填写一项, 如果是自己有问题,请重新提问,否则站长有可能看不到





2020-11-13 请问Minibroswer类是否就是socket网络编程
2020-10-05 这段代码的作用是什么呢
2020-05-26 为什么要 response.length-doubleReturn.length},运行了下for (int i = 0; i < response.length; i++) 也行




提问之前请登陆
提问已经提交成功,正在审核。 请于 我的提问 处查看提问记录,谢谢
关于 实践项目-DiyTomcat-迷你浏览器 的提问

尽量提供截图代码异常信息,有助于分析和解决问题。 也可进本站QQ群交流: 578362961
提问尽量提供完整的代码,环境描述,越是有利于问题的重现,您的问题越能更快得到解答。
对教程中代码有疑问,请提供是哪个步骤,哪一行有疑问,这样便于快速定位问题,提高问题得到解答的速度
在已经存在的几千个提问里,有相当大的比例,是因为使用了和站长不同版本的开发环境导致的,比如 jdk, eclpise, idea, mysql,tomcat 等等软件的版本不一致。
请使用和站长一样的版本,可以节约自己大量的学习时间。 站长把教学中用的软件版本整理了,都统一放在了这里, 方便大家下载: https://how2j.cn/k/helloworld/helloworld-version/1718.html

上传截图