笔记 - HTTP入门

245 阅读6分钟

问:hosts 文件在哪?
答:

  • 在 Windows 系统中,hosts 位于 C:\Windows\System32\drivers\etc\hosts 。
  • 在 macOS / Linux系统中,hosts 位于 /etc/hosts

URL - Uniform Resource Locator 统一资源定位符

统一资源定位符的标准格式如下:

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

[协议类型]://[服务器地址]:[端口号]/[资源层级UNIX文件路径][文件名]?[查询]#[片段ID]

协议类型:http, https, ftp ...
服务器地址:域名或IP
端口:提供服务的端口号
路径:UNIX文件路径
查询:query string,也叫查询参数,可被服务器接收和识别
片段/锚点:fragment 可用于定位到页面上的指定位置

查看本机IP设置

windows: ipconfig
mac: ifconfig

loopback: 127.0.0.1 localhost

截屏2021-02-27 13.21.11.png

Port 端口

计算机之间依照互联网传输层TCP/IP协议的协议通信,不同的协议都对应不同的端口

  • 0-1023:官方端口,应用与端口组合记录在IANA的端口分配列表中
  • 1024-49151:非官方 应用与端口组合不在IANA的端口分配列表中
  • 49152-65535:根据定义,该段端口属于“动态端口”范围,没有端口可以被正式地注册占用。

常用端口: HTTP:80,替代端口为8080 HTTPS:443 FTP:21(控制端口,常用),20(默认数据端口) SSH:22 DNS:53

端口被占用是个非常常见的情况,如果发现被占用换一个就行。

ping 工具

ping利用ICMP返回包来计算网络的丢包率和RTT 用法

  - Ping the specified host:
    ping host

  - Ping a host a specific number of times:
    ping -c count host

DNS - Domain Name System

域名对应IP,方便用户记忆。
负载均衡:域名对应多个IP,防止压力过大。
共享主机:IP对应多个域名。

nslookup 工具 查询域名

向DNS发出请求,获得域名和对应IP。

理论上的网页请求顺序: html -> css -> js 网页内容按顺序请求,因而应合理排布网页内容。

域名层次:

域名由多个部分组成,这些部分通常连接在一起,并由点分隔。

  • 顶级域名: 顶级域(英语:Top-level domains,缩写:TLD)是域名中最高的一级,每个域名都以顶级域结尾。
    .com .cn .org. .gov均属于顶级域名

  • 子域名: 二级域名

位于顶级域名的左侧。 对于 wikipedia.org来说: wikipedia是一个‘二级域名’。 而 wikipedia.org可被称为‘一级域名’

三级域名

位于二级域名的左侧。

对于 zh.wikipedia.org来说: zh是一个‘三级域名’。 而zh.wikipedia.org可被称为‘二级域名’


也即 `www.wikipedia.org` 和 `wikipeida.org` 并不是一个域名,前者为二级域名,后者为一级域名。

所有带www的网站会被默认指向它的一级域名。

Query String 查询参数

GET模式的窗体参数:
以“?”字符为起点,每个参数以“&”隔开,再以“=”分开参数名称与资料,通常以UTF8的URL编码,避开字符冲突的问题

Fragment 锚点/片段

片段。以“#”字符为起点,可迅速定位到页面的指定位置(锚点)
锚点不支持中文。
锚点会被浏览器接收,但不会发送给服务器

URL中的中文

实际上URL本身是不支持中文的,URL的中文会根据UTF-8转码,每个字节前加上%,形成URL编码。
在浏览器地址栏里,浏览器认为%是个转义字符,浏览器会把%与%之间的编码,两位两位取出后进行解码,然后再传递给后端,然后由后端进行再次解码。
encodeURI()方法可对字符串进行转义,形成URL编码。
decodeURI()方法可还原转义后的URL片段,为encodeURI的逆运算。

HTTP

HTTP规格文档:RFC2612

HTTP 请求与响应

可参考的内容: www.liaoxuefeng.com/wiki/101695…

User-Agent -> Server

GET请求和POST请求的区别:
GET只向服务器发送header data,POST请求除了header data以外附带一个body,里面包含用户数据。
此外GET是明文请求,而POST请求并不对外显示。

HTTP Request (RFC 2612 Chapter5)

request格式:

  • Request header
请求动词 路径和查询参数 协议名/版本 
Host: 域名或IP
Accept: 可接受的内容,e.g. text/html<br>
Content-Type: request body所含内容的格式

-- 请求动词:GET / POST / PUT / PATCH / DELETE

  • Request body GET请求一般不含body,post会带有上传的内容。

HTTP Response (RFC 2612 Chapter6)

Response格式:

  • Response header
协议名/版本 状态码 状态字符串 
Content-Type: response body所含内容的格式 
  • Response body 响应内容,也就是下载内容

Status Code 状态码

cURL

curl命令是一个利用URL规则在命令行下工作的文件传输工具。它支持文件的上传和下载,所以是综合传输工具,但按传统,习惯称curl为下载工具。作为一款强力工具,curl支持包括HTTP、HTTPS、ftp等众多协议,还支持POST、cookies、认证、从指定偏移处下载部分文件、用户代理字符串、限速、文件大小、进度条等特征。做网页处理流程和数据检索自动化,curl可以助一臂之力。

curl发送HTTP请求

可参考以下文档学习curl: man.linuxde.net/curl

curl -v: verbose 详细模式

 -v, --verbose
              Makes curl verbose during the operation.  Useful  for  debugging
              and  seeing  what's  going  on "under the hood". A line starting
              with '>' means "header data" sent by  curl,  '<'  means  "header
              data"  received  by  curl  that is hidden in normal cases, and a
              line starting with '*' means additional info provided by curl.

              If you only want HTTP headers in the output, -i, --include might
              be the option you're looking for.

              If  you think this option still doesn't give you enough details,
              consider using --trace or --trace-ascii instead.

              Use -s, --silent to make curl really quiet.

简而言之verbose模式会提供详细的请求内容。
>代表发出的header data
<代表收到服务器返回的header data

从curl的运行的结果中可以看出:

  1. DNS请求得到域名对应的IP,通过80端口访问。
  2. 随即建立TCP连接
  3. 发送请求,包含访问方法(GET),指定host,user-agent(curl),以及期待返回的内容(*/*代表所有内容均接受)
  4. 获得反馈,status code 200 OK
  5. 反馈里包含header data和网页的实际内容
  6. 关闭TCP连接
  7. 结束程序

由于这次访问没有包含www, baidu 只返回了meta。
用同样的方法访问 bing.com, 得到302 Found, 访问 www.bing.com 则得到301 Moved Permanently,都不会返回完整内容,需要重新定向。

-X 请求动词
e.g. curl -X POST http://localhost:8888/xxx?test=hi

-H 'Name:Value' 或者 --header 'Name:Value':设置请求头
e.g. curl -H 'Accept:text/html' http://localhost:8888/xxx?test=hi

Node.js实现一个简单的请求和响应

可参考文档: nodejs.org/zh-cn/docs/…

var http = require('http');
var fas = require('fs');
var url = require('url');
var port = process.argv[2];

if (!port) {
    console.log('Please set a Port number: ');
    process.exit(1);
}

var server = http.createServer(
    function (request, response) {
        /* 基本参数 */
        var parsedURL = url.parse(request.url, true);
        var pathWithQuery = request.url;
        var queryString = '';
        if (pathWithQuery.indexOf('?') >= 0) {
            queryString = pathWithQuery.substring(pathWithQuery.indexOf('?'));
        }
        var path = parsedURL.pathname;
        var query = parsedURL.query;
        var method = request.method;
        /* 收到request并打印其路径和参数 */
        console.log('New request - Path and Query: ' + pathWithQuery);
        /* 根据request path返回不同的response header */
        if (path === '/') {
            response.statusCode = 200;
            response.setHeader('Content-Type', 'text/html;charset=utf-8');
            response.write(`root`);
            response.end();
        }
        else if (path === '/x') {
            response.statusCode = 200;
            response.setHeader('Content-Type', 'text/css;charset=utf-8');
            response.write(`body{color: red;}`);
            response.end();
        }
        else {
            response.statusCode = 404;
            response.setHeader('Content-Type', 'text/html;charset=utf-8');
            response.write(`The path does not have any content`);
            response.end();
        }
    }
)

server.listen(port);
console.log('Listen ' + port + ' successfully. You can open the page through http://localhost:' + port);

结果:
通过Chrome,Safari和curl各发了一次请求

Node.js 读取请求

  • 读取请求动词:request.method
  • 读取url(带查询参数):request.url
  • 读取纯路径path:`request.path
  • 读取查询参数:request.query
  • 读取请求头:request.headers[]

Node.js 设置响应

  • 设置状态码:response.statusCode = <codeNumber>;
  • 设置响应头:response.setHeader('Name', 'Value');
  • 设置响应体:response.write('Content');

解决Mac上的端口占用问题

启动服务失败有可能是该端口已被占用。

  1. 可使用lsof -i tcp:<port> 查询指定端口已启用的服务。
  2. 使用kill <PID>干掉该进程,解除端口占用。