[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')
});
```