啊,我觉得这样的探索很有意思!我不熟悉 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/
,信息点有:
- ftp:// 这个资源需要用FTP协议获取到
- ftp.freebsd.org 服务器的域名是 ftp.freebsd.org
- /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 乃至其它类型的协议的各个部分代表的意义出发,思考怎么去构造出合适的请求,怎么去解析响应的问题,在这方面有一些基本了解之后,再去研究封装好的库应该就有方向了!