开始学习简单而基本的网络命令了,前天通过TCP协议建立了一个socket,在本机上让两个thread之间传输信息,感觉非常好玩,有种第二次以严肃姿态进入网络时代的感觉?

今天练习的是访问某个URL,以Yahoo为例,代码是书上的:

class YahooURL {

    public static void main(String[] args) throws  Exception{
        int c;
        java.net.URL yahoo= new java.net.URL("http://www.yahoo.com");
        URLConnection con = yahoo.openConnection();
        java.io.InputStream in = yahoo.openStream();
        while ((c=in.read()) != -1)
            System.out.print((char) c);
        System.out.println("Content-type: "+con.getContentType());
        System.out.println("Content Encoding: "+con.getContentEncoding());
        System.out.println("Content-length: "+con.getContentLength());
        System.out.println("Last-modified: "+new Date(con.getLastModified()));
        System.out.println("Date: "+new Date(con.getDate()));
        System.out.println("Expires: "+con.getExpiration());
        System.out.println("Connection timeout: "+con.getConnectTimeout());
    }
}

分别获取并输出了内容类型、内容编码、日期等信息。

疑问/有趣之处:

  1. URL信息读取的开始,当in.read()不为-1时,输出该数字代表的character,输出结果是redirect。(见图)
    为什么这么做书上没有解释,我试图修改-1到其他数值,比如0,33,100,114,其中0、33等ASCII对应不再redirect中任何字母的数值导致程序输出大量方框而且不断持续,而116 (t)、114 (r)、105 (i)等数值则导致“redirect”缩短到该数值对应字母出现的第一个位置前,比如116代表t,则输出“redirec”,而114代表r是第一个字符则没有输出。

该黑箱探索获得的猜想是:URL连接建立之初存在大量被接收成 -1 值的数据,在这个数字噪音背景中有 “redirect”。
实际情况是什么呢?

  1. Last-modified 是1970年的1月1日(格林威治0时),经查询这是UNIX时间的开始。

  2. Content-coding、Content-Length等尚不清楚具体含义,这些是不是HTTP Header的内容?

  1. 大概因为你被重定向了所以才输出的 redirect ,你换成 https 试试.. 另外 -1 是 EOF ,不为 -1 说明输入缓冲区有内容,没有你想像的大量噪音值..
  2. 我倾向于是因为服务器没有返回这个响应头, Java 会给你一个默认值 0 ,并且自动输出了 CST(China Standard Time) +0800 的格式.. 语言实现太贴心有时候挺不好的..
  3. 是的,参考 rfc2616

改成https的效果:

第一部分缓冲区【html的框架?】

<!DOCTYPE html>
<html dir="ltr" class="ltr eu yahoo-page height100">
    <head>
        <title>Dabar Yahoo yra „Oath“ dalis</title>
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" >
        <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, viewport-fit=cover">
        <link rel="stylesheet" href="https://s.yimg.com/oa/build/css/site-ltr-b90b2cb1.css">
        <link rel="icon" type="image/png" href="https://s.yimg.com/oa/build/images/favicons/yahoo.png">
    </head>
    
    <body class="no-js no-touch blur-preview-tpl eu-localized home lt-LT">
        <script nonce="2SmroJ0FYmqyhlcVNGQa/D81frr0LsnC">
            if ('classList' in document.body) {
                document.body.classList.add('js');
                document.body.classList.remove('no-js');
            }
        </script>

        <div class="consent-semi-transparent">
        <div class="consent-round-corner">
        <div class="consent-container">
            <h1 class="consent-title">
                Prieš atlikdami tolesnius veiksmus...
            </h1>
            <div class="consent-text">
                „Yahoo“ yra „Oath“ dalis. „Oath“ ir mūsų partneriams reikia jūsų sutikimo, kad galėtume pasiekti jūsų įrenginį ir naudoti jūsų duomenis (įskaitant jūsų buvimo vietą) siekdami suprasti jūsų interesus ir teikti individualiems poreikiams pritaikytų paslaugų reklamas. „Oath“ taip pat teiks individualiems poreikiams pritaikytas partnerių produktų reklamas.

                <a class="expand-learn-more">Sužinokite daugiau.</a>
                <section class="learn-more-content hidden">
                    <h2>Kaip „Oath“ ir mūsų partneriai teikia jums geresnę reklamos patirtį</h2>
                    <p>
                        Siekdami suteikti jums geresnę bendrą patirtį, norime teikti aktualias reklamas, kurios yra jums naudingesnės. Pavyzdžiui, kai ieškote filmo, naudojame jūsų paieškos informaciją ir vietos duomenis, kad galėtume parodyti jums aktualiausius kino teatrus, esančius netoli jūsų. Šią informaciją taip pat naudojame, kad galėtume rodyti reklamas apie panašius filmus, kuriuos galbūt norėsite pamatyti vėliau. Kaip ir „Oath“, mūsų partneriai gali rodyti jums reklamas, kurios, kaip jie mano, atitinka jūsų interesus.
                    </p>
                    <p>
                        Sužinokite daugiau apie tai, kaip <a href="/redirect?to=https%3A%2F%2Fmydata.oath.com%2F%23sharingdata" target="_blank">„Oath“ renka ir naudoja duomenis</a> ir kaip mūsų <a href="/collectConsent/partners?sessionId=3_cc-session_17c13252-410a-4dfd-a445-84e9174605ad&lang=lt-LT&step=EU_SINGLEPAGE">partneriai renka ir naudoja duomenis</a>.
                    </p>
                </section>

                Norėdami tęsti ir leisti „Oath“ bei mūsų partneriams naudoti jūsų duomenis pasirinkite „Gerai“ arba pasirinkite „Tvarkyti parinktis“ ir peržiūrėkite savo pasirinkimus.
            </div>

            <form method="post" class="consent-form" action="/consent">
                <input type="hidden" name="consentCollectionStep" value="EU_SINGLEPAGE">
                <input type="hidden" name="previousStep" value="">
                <input type="hidden" name="csrfToken" value="PbaUTJUUUFO6XqJxU8vfM5xapCYikHDN">
                <input type="hidden" name="jurisdiction" value="">
                <input type="hidden" name="locale" value="lt-LT">
                <input type="hidden" name="doneUrl" value="https://guce.yahoo.com/copyConsent?sessionId&#x3D;3_cc-session_17c13252-410a-4dfd-a445-84e9174605ad&amp;inline&#x3D;false&amp;lang&#x3D;lt-LT">
                <input type="hidden" name="tosId" value="eu">
                <input type="hidden" name="sessionId" value="3_cc-session_17c13252-410a-4dfd-a445-84e9174605ad">
                <input type="hidden" class="namespace" name="namespace" value="yahoo">
                <input type="hidden" name="originalDoneUrl" value="https://www.yahoo.com/?guccounter&#x3D;1">
                <input type="hidden" name="inline" value="false">
                <input type="hidden" name="startStep" value="EU_SINGLEPAGE">
                <input type="hidden" name="isSDK" value="false">
                <input type="hidden" name="brandBid" value="71icn8lees7s4">
                <input type="hidden" name="userType" value="NON_REG">
                <input type="hidden" name="country" value="LT">
                <input type="hidden" name="ybarNamespace" value="YAHOO">                <div>
                    <button type="submit" class="btn link" name="moreOptions" value="moreOptions">
                        Tvarkyti parinktis
                    </button>
                    <button type="submit" class="btn primary" name="agree" value="agree">

                            Gerai
                    </button>
                </div>
            </form>
            </div>
        </div>
        </div>

        <script src="https://s.yimg.com/oa/build/js/site-2c739c91.js" nonce="2SmroJ0FYmqyhlcVNGQa/D81frr0LsnC"></script>
    </body>
</html>

第二部分
Content-type: text/html;charset=UTF-8
Content Encoding: null
Content-length: 5205
Last-modified: Thu Jan 01 08:00:00 CST 1970
Date: Wed May 29 14:06:12 CST 2019
Expires: 0
Connection timeout: 0

编码在Content-type部分显示了,Encoding还是null。

    你说的第一部分就是你请求的网页内容(雅虎首页),没有任何问题。如果是 http 协议,雅虎做了个跳转,强制用户访问 https 协议,所以你之前没有跟随跳转的话,只能拿到一个 redirect 的提示输出。

    Content-Encoding 为 null 的话有可能并不是通过 URLConnection 取得呢?不熟悉 Java ,就瞎掰一下。看有没有学 Java 的大佬解释一下

    啊,我觉得这样的探索很有意思!我不熟悉 Java,不过我觉得可以从通用的概念的角度去看这个话题。Java 这种语言自带的类库对于协议本身的封装有点厉害,通过它来入门某个协议的话,不太容易抓住重点。

    对于 URL 的信息获取,我觉得可能需要明确 URL 的概念还有结构。(不知不觉就写了好多字

    关于URL

    刚刚找了一下 https://en.wikipedia.org/wiki/URL#Syntax
    URL 是 URI 的子集,主要用来指定某个特定的资源,还有资源所在的位置,获取的方法等等。

    然后它的格式是这样的(摘录自刚刚的维基链接

    URI = scheme:[//authority]path[?query][#fragment]

    其中 authority 由这几部分组成:

    authority = [userinfo@]host[:port]

    大概是这样的结构

    大部分元素都是可以省略(就是方括号的部分)
    http://www.yahoo.com 提供了这两个信息点:

    scheme: http
    host: www.yahoo.com

    也就是说,这个 URL 告诉我们,我们可以遵循 http 协议这种方法(scheme),从 www.yahoo.com 域名所指向的服务器地址来获取资源。

    HTTP 协议的默认端口是 80,所以这里最终是让系统和 www.yahoo.com 所指向的服务器的 80 端口建立 socket,然后再遵循 HTTP 的方法来与服务器进行交互。

    类似的协议还有 ftp 什么的,浏览器常用的还有 file,上古时期有个 gopher 协议。

    举两个例子:

    1. FTP协议

    这是 FTP 协议的 URL 的例子 ftp://ftp.freebsd.org/pub/FreeBSD/,信息点有:

    1. ftp:// 这个资源需要用FTP协议获取到
    2. ftp.freebsd.org 服务器的域名是 ftp.freebsd.org
    3. /pub/FreeBSD 要访问的资源路径在 /pub/FreeBSD

    FTP 协议默认端口是 21,所以程序解析以后,会让系统和域名 ftp.freebsd.org 指向的服务器的 21 端口建立 socket,然后按照 FTP 协议的约定,实现交互。

    2. file 协议

    直接访问文件系统的 File 协议 file:///home/username/RomeoAndJuliet.pdf。可以分为两部分

    file:///home/username/RomeoAndJuliet.pdf

    file:// 代表用 file 协议,然后 /home/username/RomeoAndJuliet.pdf 就是一个完整的文件路径。

    意思就是,这个资源是通过读取这个路径的文件而获得。

    ps: Windows下的文件路径会多一个盘符:

    HTTP

    大概把 URL 的概念理清楚之后,你可能会发现,(不扯上细节的问题的话)这里干的事情就是,先和服务器建立一个通信的通道,Socket 或者是 TLS。然后再通过 HTTP 协议和服务器交互。

    我觉得最好先从 HTTP 的协议本身去看这个问题,从封装好的类库的角度去看感觉容易被细节给带偏,弯路绕的比较大。

    总的来说,HTTP 是一种无状态的协议,客户端对服务器发出请求,服务器接到请求、针对请求做出响应。

    请求分为请求行,请求头和请求数据三部分,响应则分为响应的状态行,响应头和响应数据。

    具体可以查找相关的资料,我觉得MDN的这个链接可以带来一些印象:
    典型的 HTTP 会话

    然后我们可以通过一些工具来感受这个过程,纯手动的话,可以用 telnet 来手动和服务器进行 HTTP 交互,这是访问百度的例子:

    这里百度返回了一个简短的HTML,让浏览器跳转到 http://www.baidu.com

    手动很难构造出合适的请求,对于复杂的请求,推荐用命令行的 curl 工具来进行。通过 -v 参数,我们可以很直观地看到请求一个 URL 的通信过程发生了什么。

    命令:curl -v http://www.yahoo.com
    然后整个通信过程是这样的:

    这里的 redirect 和楼主用 Java 代码读取出来的是一致的(图中的 % 是命令结束以后为了换行强行加的标记,可以不管)。猜测 java.net.URL 实例的 openStream() 方法就是打开了一个针对响应数据的读取流吧,然后 openConnection 的返回值应该有对响应 Header 的封装,具体应该可以带着这个方向去查找文档。

    假设我们 curl 一个 HTTPS 的网站,通过 -v 参数,整个建立可靠连接和交互的流程都展示得清清楚楚。

    如果还不满足,可以用 WireShark 来监控整个流程,用它提供的"追踪 TCP 流"的功能,也可以看到电脑在完整的交互,还是蛮有意思的(不过在这个层面就不能直接抓到 HTTPS 的数据了。

    然后一切的一切,都是围绕着 HTTP 乃至其它类型的协议的各个部分代表的意义出发,思考怎么去构造出合适的请求,怎么去解析响应的问题,在这方面有一些基本了解之后,再去研究封装好的库应该就有方向了!

    NTL01 编码在Content-type部分显示了,Encoding还是null。

    结合楼上的帖子,这么一看的话,我感觉可能是因为响应头里面没有给 Content-Encoding,所以取到的是 null

    12 天 后

    © 2018-2025 0xFFFF