🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] ## http是tcp的超集 当浏览器请求一个url资源的时候,先和目标服务器建立tcp长连接,然后再发送http请求。(这意味着tcp链接建立完成之前,http请求并没有发送) ![](https://box.kancloud.cn/c2f7f107183b6f8e4cca0b7095e3d3de_495x276.png) 可以看到,这里connection先于request触发 ### 若http服务器没有res.end(),即不会断开连接 ![](https://box.kancloud.cn/219f60c61e1513dec698d972f245629e_782x442.png) #### end先于close ![](https://box.kancloud.cn/5e77b58b46501a86059570980d077922_588x389.png) ### Q #### http只是基于tcp的包装?还是不同于tcp的另一个链接请求? #### request是第一次发数据的时候才触发吗? 答:只要客户端向服务器发送消息,每次都会触发request事件 #### 发消息就等于客户端链接吗? 不等于,先建立链接才会发送http请求 ## 利用curl发送http请求 下图中“相应信息”应为“响应信息” **注意**:-X的x为大写,post也必须为大写 ![](https://box.kancloud.cn/3888143d82091bbf2db15082a3fa3e31_706x74.png) ### 请求报文 请求行 method url protocol 请求头 空白行 请求体 ![](https://box.kancloud.cn/ab5d87d0aee5345926c6407d54212543_627x261.png) ![](https://box.kancloud.cn/71facfb45a1c0ce4b8e180317532adc2_965x362.png) ### 响应报文 响应行 protocol statusCode statusDesc 响应头 空白行 响应体 ![](https://box.kancloud.cn/137951a2ffa83e1a2cee06fff93a714d_375x197.png) ![](https://box.kancloud.cn/9b5e4f4f674ad899e9a795cbcf4b44ca_797x295.png) ![](https://box.kancloud.cn/e5defb142590a505e2ca7eb970a39108_482x312.png) ## 创建一个http服务器 ``` let http = require('http'); let server = http.createServer(function(req,res){ }).listen(8080,function(){ console.log('server is running on the localhost:8080'); }); ``` ### req req代表客户端的一个请求。 server服务器把客户端的请求进行解析,然后放在req对象上。 #### 通过req.x的方式获取请求行和请求头 ![](https://box.kancloud.cn/d6fb95e9d2b0ee136b11f9de5bf31156_527x408.png) #### req对象中拿不到hash ![](https://box.kancloud.cn/3d6564e034d438b3496f356a6892d88e_694x233.png) #### url.parse(req.url) 获取 url对象 ``` import url = require('url'); //url为nodeJS中内置的模块,但需要引用 ``` ![](https://box.kancloud.cn/d80535b6036934433b688d92b00176a8_444x138.png) ##### req.url和url对象的比较 其中`path`为路径,`pathname`为path去掉`query`的部分 path即是 url从域名后面‘/’及其以后的部分 ![](https://box.kancloud.cn/52b51ccb7121822cdabf057646b5578b_266x414.png) ##### url对象中的query,以及其他 ![](https://box.kancloud.cn/7e343b51e81a991e93f1bb3ce37b7932_502x288.png) #### 从req中获取请求体 我们不能直接通过req.data的形式拿到请求数据,但! 因为req是一个可读流,可以通过监听"data"事件来获取请求体数据 **注意**:默认拿到的数据为buffer(一段16进制的类数组数据),需要toString ![](https://box.kancloud.cn/c76c6e12160cb0f935c562dc23bea212_506x293.png) ### res res代表响应,如果希望向客户端回应消息,需要通过res #### 设置响应行和响应头 statusCode:设置了Code会自动生成对应的描述 sendDate:设置是否在响应中显示响应开始发送的时间 setHeader:设置响应头 removeHeader:移除响应头 getHeader:获取响应头 ![](https://box.kancloud.cn/42b3d44f974c66ab4e3e012e7b3c7c71_631x565.png) #### writeHead,可以将状态码和响应头一起写了 headersSent:响应头是否已经发送 ![](https://box.kancloud.cn/cc82dbda7ac5bed422a72d256a6517ea_588x180.png) 这个api存在的意义? 当调用write时响应头就已经发送出去了 为什么调用write就发送了呢? 客户端这时就开始接收数据了,需要知道接收的数据长撒样 这里需要注意的是 而这时可能服务器还准备传点什么给客户端,还没有res.end链接还没有挂断, ![](https://box.kancloud.cn/0173bf14fb920bb7a75ffab0d8d38d8b_1065x356.png) **注意**: 1. 如果是调用write而不是直接调用end,这里会分块传输 见上图【Transfer-Encoding】 2. 如果响应时没有end,也不会在响应头中出现【Content-Length】这个属性 3. 这里分块传输的时候字节数比应该的多,是因为: 分块编码相当简单,在头部加入 Transfer-Encoding: chunked 之后,就代表这个报文采用了分块编码。这时,报文中的实体需要改为用一系列分块来传输。每个分块包含十六进制的长度值和数据,长度值独占一行,长度不包括它结尾的 CRLF(\r\n),也不包括分块数据结尾的 CRLF。最后一个分块长度值必须为 0,对应的分块数据没有内容,表示实体结束。 相关文章: https://www.cnblogs.com/simonbaker/p/5593177.html ### 创建http服务器的第二种形式 ``` let server = http.createServer(); server.on('connection',function(socket){ }); server.on('request',function(req,res){ }); server.listen(8080,function(){ console.log('server started at http://localhost:8080') }); ```