http 深入浅出

作者: duffy990 | 来源:发表于2018-02-28 00:01 被阅读0次

    前言

    相信大家对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 深入浅出

        本文链接:https://www.haomeiwen.com/subject/gnggzxtx.html