这是一种"化整为零"或"分治"的思路,在 HTTP 协议里的体现就是"chunked"分块传输编码。在http响应报文中用头字段“Transfer-Encoding: chunked”,表示响应中的body不是一次性发送完毕,而是分成了许多的块(chunk)逐个发送,直到发送完毕。
2)分块传输的编码规则1)每个分块包含两个部分,<长度头>和<数据块>;
2)<长度头>是以 CRLF(回车换行,即\r\n)结尾的一行明文,用 16 进制数字表示长度;
3)<数据块>紧跟在<长度头>后,最后也用 CRLF 结尾,但数据不包含 CRLF;
4)最后用一个长度为 0 的块表示数据传输结束,即“0\r\n\r\n”。
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"time"
)
func main() {
server := &APIServer{
engine: gin.Default(),
}
server.registryApi()
server.engine.Run(":38080")
}
type APIServer struct {
engine *gin.Engine
}
func (s *APIServer) registryApi() {
registryStream(s.engine)
}
func registryStream(engine *gin.Engine) {
engine.GET("/stream", func(ctx *gin.Context) {
w := ctx.Writer
header := w.Header()
//在响应头添加分块传输的头字段Transfer-Encoding: chunked
header.Set("Transfer-Encoding", "chunked")
header.Set("Content-Type", "text/html")
w.WriteHeader(http.StatusOK)
//Flush()方法,好比服务端在往一个文件中写了数据,浏览器会看见此文件的内容在不断地增加。
w.Write([]byte(`
<html>
<body>
`))
w.(http.Flusher).Flush()
for i:=0 ;i<10; i++{
w.Write([]byte(fmt.Sprintf(`
<h1>%d</h1>
`,i)))
w.(http.Flusher).Flush()
time.Sleep(time.Duration(1) * time.Second)
}
w.Write([]byte(`
</body>
</html>
`))
w.(http.Flusher).Flush()
})
}
4)浏览器测试
可以发现浏览器界面逐渐收到0,1,2,3…。
说明:从浏览器中看见的,不是完整的报文,因为是经过浏览器处理的,去掉了块的<长度头>信息。