go实现的代码:
https://segmentfault.com/q/1010000015929180?sort=created
php实现的代码
https://segmentfault.com/a/1190000011059980
运行这个提问的第一个回答的代码后发现,虽然golang已经是实时把内容输出给浏览器了,但是浏览器并不是实时显示从服务器获取到的内容
请问如何才能实现浏览器也实时显示的功能呢?
go实现的代码:
https://segmentfault.com/q/1010000015929180?sort=created
php实现的代码
https://segmentfault.com/a/1190000011059980
运行这个提问的第一个回答的代码后发现,虽然golang已经是实时把内容输出给浏览器了,但是浏览器并不是实时显示从服务器获取到的内容
请问如何才能实现浏览器也实时显示的功能呢?
没办法实现。
如果楼主熟悉 HTTP 协议的话应该知道,协议中消息头(header
)在前,消息体(body
)在后,消息头与消息体之间有一个空行,并且服务端响应的消息头中包含了 Content-Length
一项,用来告诉客户端:“你接收到空行之后再接收这么多个字节就可以停止接收了,这么多个字节就是我发给你的完整的 HTTP body 了,保证你不会因为缺少或存在多余字节而造成解析出错。”
那么,这种消息头和消息体的前后顺序关系也就意味着服务端必须要先知道总体 body
有多大,才能确定 header
中的 content-type
的值。所以服务端没办法先将请求头发送,再不断地发送请求体,因为请求头中已经昭示了未来的行为。另外,从客户端的角度来讲,它必须知道 HTTP 消息的长度,并在接收到这么多长度的完整消息之后,才可以进行解析操作。
那为什么在 Go 代码中可以接连不断地进行 Write
呢,其实 http.ResponseWriter.Write()
也仅仅是将 bytes 写进了 underlying buffer,等待 handler
函数 return
之后再进行 body
长度的计算,写进 header
,然后将整个 HTTP 消息发送给客户端。
其实这个问题很好验证,在 handler 中连续两次 Write,中间设置时间间隔:
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte("hello "))
time.Sleep(3 * time.Second)
_, _ = w.Write([]byte("world"))
})
_ = http.ListenAndServe(":8888", mux)
然后,在命令行中进行 curl:
$ curl localhost:8888
你会发现,3
秒之后 hello
和 world
一同被响应。
5 回答5.6k 阅读✓ 已解决
6 回答2.7k 阅读
2 回答3.2k 阅读✓ 已解决
1 回答5.3k 阅读✓ 已解决
2 回答3.1k 阅读✓ 已解决
1 回答5.1k 阅读✓ 已解决
2 回答2.6k 阅读✓ 已解决
为了方便,我直接贴代码吧。
说实话,在这点上,go写的确实很模糊,谁会想到要把http.ResponseWriter转为http.Flusher呢?我也是看源码才知道