说说Keep-Alive | Pipelining | Content-Length | Transfer Encoding

目录

Keep-Alive 模式

管道机制(Pipelining)

Content-Length

Transfer Encoding Chunked


Keep-Alive 模式

理解 Keep-Alive 模式,就要提到http1.0协议的缺点。

http1.0协议的主要缺点就是,每个TCP连接只能发一个请求, 每次数据发送完毕,连接就关闭。 如果还要请求其他资源,就必须新建连接。

因为三次握手、慢启动等,TCP连接新建的成本很高。 随着web的发展,需要请求的资源越来越多,这个问题就越来越突出。

在http1.0时代,为了解决这个问题, 有些浏览器在请求时会在请求头里加上一个自定义字段:

Connection: keep-alive

这个字段就是告诉服务器不要关闭TCP连接,以便其他请求复用,但是,这还不是标准字段。

1997年1月, http1.1版本发布,引入了持久连接,即默认情况下TCP连接不关闭,可以被复用,也就是默认 keep-alive 。同时,支持通过 Connection:close 来通知关闭TCP连接。

所以实际上,http1.0协议下客户端和服务端可以通过 Connection: keep-alive 告知对方发送完数据后不要关闭TCP连接,而http1.1协议下默认就是 Connection: keep-alive 持久连接。

* 示例:服务端

[shell]# vim server.go

package main

import (
	"io"
	"log"
	"net/http"
)

func indexServer(w http.ResponseWriter, req *http.Request) {
	io.WriteString(w, "hei !\n")
}
func main() {
	http.HandleFunc("/index", indexServer)
	log.Fatal(http.ListenAndServe(":8888", nil))
}

[shell]# go run server.go

http1.1协议默认情况下就加了Connection: keep-alive,会持久连接,如下面三次请求复用同一个TCP连接,而且在等待一段时间没有新请求的之后,关闭连接。

GET /index HTTP/1.1
Host	192.168.56.101:8888
Cache-Control	max-age=0
Upgrade-Insecure-Requests	1
User-Agent	Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36
Accept	text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding	gzip, deflate
Accept-Language	zh-CN,zh;q=0.9,en;q=0.8
Connection	keep-alive

图上本地端口始终是 51411:

ARq54CTVtijDMoO.png

当 Connection: close 时,数据发送完毕会断开TCP连接,每次请求都需要新建连接。

GET /index HTTP/1.1
Host	192.168.56.101:8888
Cache-Control	max-age=0
Upgrade-Insecure-Requests	1
User-Agent	Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36
Accept	text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding	gzip, deflate
Accept-Language	zh-CN,zh;q=0.9,en;q=0.8
Connection	close

管道机制(Pipelining)

http1.0还有一个问题是当客户端复用TCP连接发送请求时, 要先发送一个请求,等到服务器响应之后,再发出一个请求,这也会造成效率低。

http1.1为了解决这个问题引入了管道机制(Pipelining),管道机制允许客户端同时发出多个请求,然后服务端按照顺序响应。

Connection: keep-alive

在非持久连接下可以可以通过连接关闭得知数据已经发送完毕, 在持久连接下显然不行。http1.1开始, 一个TCP连接可以发出多个请求,并传送多个返回, 这时就需要区分数据包是哪一个请求返回的。有一个方法就是通过 Content-Length 字段, 请求双方可以通过

Content-Length: 6

告知对方响应数据字节数,比如Content-Length:6,6个字节后的数据就属于下一个请求。

3.png

Transfer Encoding Chunked

接着上面提到的持久连接下如何区分数据包边界, 在客户端请求的是一个静态页面或者一张图片时,服务端可以明确的计算出Content-Length返回给客户端。

但是当请求的内容是需要耗时计算的动态数据时,服务端不可能预先知道数据包大小,这时服务端就可以通过 Transfer Encoding:chunked 的方式来传输数据。

chunked 编码将数据分成一个个数据块发送, 每个数据块之前会有一个16进制的数值表示这个数据块的长度。最后是一个小为0的块,表示数据发送完毕。

* 服务端代码调整

package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"time"
)

func indexServer(w http.ResponseWriter, req *http.Request) {
	w.Header().Set("Connection", "Keep-Alive")
	w.Header().Set("Transfer-Encoding", "chunked")
	w.Header().Set("X-Content-Type-Options", "nosniff")

	ticker := time.NewTicker(time.Second)
	go func() {
		for t := range ticker.C {
			io.WriteString(w, "Chunk")
			fmt.Println("Tick at", t)
		}
	}()
	time.Sleep(time.Second * 5)
	ticker.Stop()
	fmt.Println("Finished: should return Content-Length: 0 here")
	w.Header().Set("Content-Length", "0")
}
func main() {

	http.HandleFunc("/index", indexServer)
	log.Fatal(http.ListenAndServe(":8888", nil))

}

* 客户端:

4.png

参考文献

 《HTTP权威指南》 

参考资料

详解TCP、HTTP中的保活机制_Keepalive和Keep-Alive |

HTTP灵魂之问,巩固你的 HTTP 知识体系HTTP协议中的Content-Length |

HTTP 协议中的 Transfer-Encoding详解TCP、HTTP中的保活机制_Keepalive和Keep-Alive

猜你喜欢

转载自blog.csdn.net/sunyctf/article/details/129114128