http 深入浅出

前言

相信大家对http都不陌生,我们平时用的网络是在tcp/ip协议族的基础上运作的,http是属于内部的一个子集。tcp/ip协议分别有四层:应用层(http)、传输层(tcp)、网络层(ip)、数据链路层(网络等),简要说一下大概的流程:

  • 客户端发起一个http的请求(http请求报文)
  • 传输层收到http请求报文数据后进行分割,当然是为了传输方便,然后在上面打上序号和端口。
  • 网络层接收后添加上MAC地址然后转发给下一层链路层
  • 对应的接收服务器链路层收到数据后就会解析,然后就是网络层》传输层》应用层一步一步解析。
    下面一张图就非常直观了。


    tcp.png

    ok,了解大概流程后,我们继续,这节主要说的是应用层http,当然本人也是菜鸟,只是想分享交流一下,不足的请指出。

了解 HTTP

HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。
HTTP是一个基于TCP/IP通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)

  • 请求的一方叫客户端,响应的一方叫服务器端
  • 通过请求和响应达成通信
  • HTTP是一种不保存状态的协议

HTTP的历史(了解一下哈~):

HTTP/0.9 --1990年问世,那时的HTTP并没有作为正式的标准被建立,只支持GET方法,不支持MIM类型,很快被HTTP/1.0取代
HTTP/1.0 --HTTP正式作为标准被公布是在1996年的5月,版本被命名为HTTP/1.0,并记载于RFC1945。虽然说是初期标准,但该协议标准至今仍被使用在服务器端。
HTTP/1.1 --1997年公布的HTTP/1.1是目前主流的HTTP协议版本。之前的标准是RFC2068,之后又发布了修订版RFC2616。
HTTP/2 --2015年,HTTP/2 发布。它不叫 HTTP/2.0,是因为标准委员会不打算再发布子版本了,下一个新版本将是 HTTP/3。

HTTP之请求报文&相应报文

请求报文

请求报文的组成部分由请求行、请求头、请求体三部分组成。

http请求结构.png

请求行

请求行由请求方法、URL 、协议/版本组成。

  • 这里说一下请求方法,http请求中我们最常用的就是POST和 GET了,下面列出方法:

(1)GET:获取资源
(2)POST:传输实体
(3)PUT:传输文件。PUT方法自身不带验证机制,任何人都可以上传文件,存在安全性问题,因此一般的Web网站不使用该方法。若配合Web应用程序的验证机制或架构设计采用REST(表征状态转移)标准的同类Web网站,就可能会开放使用PUT方法。
(4)HEAD:获得报文首部
(5)DELETE:删除文件。特点同PUT方法。
(6)OPTIONS:询问支持的方法。服务器不一定支持所有HTTP协议的方法,所以可以通过OPTIONS请求查看服务器支持的HTTP方法有哪些。
(7)TRACE:追踪路径。用来确认连接过程中发生的一系列操作,但该方法易引起XST(跨站追踪)攻击,通常是不会用到的。
(8)CONNECT:要求用隧道协议连接代理。

请求头

  • 通用首部(General Header)
  • 请求首部(Request Header)
  • 响应首部(Response Header)
  • 实体首部(Entity Header Fields)

请求实体

也就是我们平时的请求数据,通常get请求数据为空。

响应报文

一般情况下,服务器接收并处理客户端发过来的请求后会返回一个HTTP的响应消息。

HTTP响应由三个部分组成,分别是:状态行、消息报头、响应正文
这里我们主要说一下状态码:

200 请求已正常处理
204 请求资源成功,但没有资源可返回
206 部分资源的请求

301 资源的URI已更新,你也更新下你的书签引用吧
302 资源的URI已临时定位到其他位置了,姑且算你已经知道这个情况了
303 资源的URI已更新,你是否能临时按新的URI访问
304 资源已找到,但未符合条件请求......304虽然被划分在3XX类别中,但是和重定向没有关系。
307 临时重定向,和302有着相同的含义,307会遵照标准,不会从POST变成GET。

400 服务器无法理解这个请求
401 需要认证的请求,且认证失败时,返回该状态码
403 服务器拒绝该请求,可能是客户端未获得文件系统的访问授权,也可能是访问权限出现了某些问题。
404 服务器上没有访问的资源

500 服务器端在执行请求时发生了错误。
503 服务器暂时处于超负载或正在进行停机维修,现在无法处理请求

我们常用的状态有:

  • 200 OK //客户端请求成功
  • 400 Bad Request //客户端请求有语法错误,不能被服务器所理解
  • 403 Forbidden //服务器收到请求,但是拒绝提供服务
  • 404 Not Found //请求资源不存在,eg:输入了错误的URL
  • 500 Internal Server Error //服务器发生不可预期的错误
  • 503 Server Unavailable //服务器当前不能处理客户端的请求,一段时间后可能恢复正常

举栗子说明一下

我们可以敲命令验证一下:

 curl -v www.baidu.com
test.png

红色区域为请求报文

  • 红色区域第一行 GET 为请求行
  • 其他行 各种首部字段
  • 因为是get方法所以没有 请求主体

蓝色区域为相应报文

  • 蓝色区域第一行 状态行
  • 蓝色区域第一行到第10行为各种首部字段
  • 空白行 html部分为 响应正文

下面是请求报文和响应报文的结构:


结构图.png

node创建HTTP服务器

首先要说明一下,http服务器是继承自tcp服务器 http协议是应用层协议,是基于TCP的。

let http = require('http');
//req 流对象 是可读流
//res 是一个可写流 write

let server = http.createServer();
let url = require('url');
//当客户端连接上服务器之后执行回调
server.on('connection', function (socket) {
    console.log('客户端连接 ');
});

/**
> POST / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.53.0
> Accept: *
> Content-Length: 9
> Content-Type: application/x-www-form-urlencoded
>
} [9 bytes data]
 */

server.on('close', function (req, res) {
    console.log('服务器关闭 ');
});
server.on('error', function (err) {
    console.log('服务器错误 ');
});
server.listen(8080, function () {
    console.log('server started at http://localhost:8080');
});

当客户端连接上来之后先触发connection事件,然后可以多次发送请求,每次请求都会触发request事件。流程就是服务器监听客户端的请求,当有请求到来的时候执行回调

HTTP请求

//req代表客户端的连接,server服务器把客户端的请求信息进行解析,然后放在req上面
//res代表响应,如果希望向客户端回应消息,需要通过 res
server.on('request', function (req, res) {
    console.log(req.method);//获取请求方法名
    let { pathname, query } = url.parse(req.url, true);
    console.log(pathname);
    console.log(query);
    console.log(req.url);//获取请求路径 
    console.log(req.headers);//请求头对象
    let result = [];
    req.on('data', function (data) {
        result.push(data);
    });
    req.on('end', function () {
        let r = Buffer.concat(result);//请求体
        console.log(r.toString());
        //如果进行响应

        res.end(r);
    })
});

HTTP响应

let http = require('http');
//如何向客户端写入响应信息 */
/**
HTTP/1.1 200 OK  响应行
Date: Fri, 02 Feb 2018 13:57:46 GMT   响应头
Connection: keep-alive
Content-Length: 9

name=dafei 响应体
Transfer-Encoding: chunked 分块传输 
*/
let server = http.createServer(function (req, res) {
    console.log('request');
    //在同一个方法里设置状态码,原因短语,响应头
    res.setHeader('Content-Type', 'text/html');
    console.log('headersSent1', res.headersSent);//响应头是否已经发送过了
   
    res.writeHead(200, {
        "Content-Type": "text/html;charset=utf8"
    })
    
    console.log('headersSent2', res.headersSent);//响应头是否已经发送过了
    res.end('byebye');
    // res.statusCode = 404;//设置响应码 
    // res.sendDate = false;//Date响应头默认会设置,如果真的不想要,可以设置为false 
    // res.setHeader('Content-Type', 'text/html;charset=utf8');//设置响应头
    // console.log('getHeader1', res.getHeader('Content-Type'));//获取响应头
    // res.removeHeader('Content-Type');//删除响应头
    // console.log('getHeader2', res.getHeader('Content-Type'));//获取响应头
    // res.write('hello');
    // res.write('world');
    // res.end();
    //res.write('byebye');//write after end 在可写流结束之后再次写入
});
server.on('connection', function (socket) {
    console.log('connection');
    socket.on('end', function () {
        console.log('连接end');
    });
    socket.on('close', function () {
        console.log('连接close');
    });
});
server.listen(8080);

writeHead一旦调用会立刻向客户端发送,setHeader.
当调用writeHead 或者调用write方法的时候才会向客户端发响应头,200是HTTP状态码,表示成功处理了此次请求, 当然也可以设置不同的响应码,用statusCode, response.end()表示的就是响应事件传输数据结束。

下一节有时间我将浅谈TCP/IP,关于TCP数据包封装、TCP的三次握手、TCP的四次挥手、TCP和UDP区别等,感谢你的关注,喜欢记得点赞哈

你可能感兴趣的:(http 深入浅出)