Fetch请求与WebSocket速度对比实测:WebSocket为何比HTTP更快?
问题描述
我使用Fetch和WebSocket传输相同的数据时,出现了以下情况:
- HTTP传输约
200 MB数据耗时29秒 - WebSocket传输约
200 MB数据仅耗时6秒
疑问
为什么WebSocket的传输速度比HTTP更快?是与编码、底层技术原理还是其他因素有关?
测试代码
Fetch 实现代码
async function postData(url = '', data) { const response = await fetch(url, { method: 'POST', mode: 'no-cors', cache: 'no-cache', credentials: 'same-origin', headers: { 'Content-Type': 'application/octet-stream' }, redirect: 'follow', referrerPolicy: 'no-referrer', body: data }); return response; //.arrayBuffer(); } postData('http://localhost:3800/buffer', arrU8) .then(data => { console.log(data); });
其中arrU8是长度为185578200的Uint8Array类型数据。
WebSocket 实现代码
function connect() { return new Promise(function (resolve, reject) { var ws = new WebSocket('ws://127.0.0.1:8081/echo'); ws.onopen = function () { resolve(ws); }; ws.onerror = function (err) { reject(err); }; ws.onclose = function (evt) { console.log("CLOSE SOCKET", new Date().toLocaleString()); }; ws.onmessage = function (evt) { console.log("RESPONSE SOCKET: " + "RECEIVED" /* evt.data */, new Date().toLocaleString()); }; }); } connect().then(function (ws) { // onopen ws.binaryType = 'arraybuffer'; ws.send(arrU8); ws.close(); }).catch(function (err) { // onerror console.log("ERROR: " + evt.data, new Date().toLocaleString()); });
服务端Go实现代码
HTTP 服务端代码
func process(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/buffer" { http.Error(w, "404 not found.", http.StatusNotFound) return } switch r.Method { case "GET": fmt.Fprintf(w, "GET request is received: %s \n", r.URL) case "POST": body, err := ioutil.ReadAll(r.Body) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } log.Println("Data length received = ", len(body)) fmt.Fprintf(w, "Data length = %v \n", len(body)) default: fmt.Fprintf(w, "Sorry, only GET and POST methods are supported.") } }
WebSocket 服务端代码
package main import ( "flag" "log" "net/http" "text/template" "github.com/gorilla/websocket" ) var addr = flag.String("addr", "localhost:8081", "http service address") var upgrader = websocket.Upgrader{} // use default options func main() { log.Println("Launching WebSocket server...", *addr) flag.Parse() log.SetFlags(log.LstdFlags | log.Lmicroseconds) http.HandleFunc("/echo", echo) http.HandleFunc("/", home) log.Fatal(http.ListenAndServe(*addr, nil)) } func echo(w http.ResponseWriter, r *http.Request) { // To fix 403 error temporarily. It's unsafe: upgrader.CheckOrigin = func(r *http.Request) bool { return true } c, err := upgrader.Upgrade(w, r, nil) if err != nil { log.Print("upgrade:", err) return } defer c.Close() for { mt, message, err := c.ReadMessage() if err != nil { log.Println("read:", err) break } log.Println("recv: message type:", mt) log.Printf("recv: bytes count: %v", len(message)) // // // Disable echo and log // log.Printf("recv: %s", message) // err = c.WriteMessage(mt, message) // if err != nil { // log.Println("write:", err) // break // } } } func home(w http.ResponseWriter, r *http.Request) { homeTemplate.Execute(w, "ws://"+r.Host+"/echo") }
速度差异原因
连接与协议开销差异
WebSocket仅在建立连接时进行一次HTTP握手,后续复用同一个TCP连接传输数据,仅需2~10字节的轻量帧头,没有额外的HTTP头开销。而HTTP/1.x每个请求都需要携带完整的请求头(通常几百字节以上),就算开启Keep-Alive复用TCP连接,每次请求的头开销也会累积,大体积数据传输时有效载荷占比远低于WebSocket。传输模式差异
HTTP是半双工的请求-响应模式,客户端发送请求后必须等待服务端返回响应才能进行后续操作,链路存在闲置空窗期,利用率低。WebSocket是全双工传输,连接建立后两端可以同时收发数据,不需要等待响应,链路利用率更高。测试代码的额外影响
你在Fetch配置中使用了mode: 'no-cors',该模式会触发浏览器的跨域限制逻辑,如果服务端没有正确配置CORS,还会产生额外的OPTIONS预检请求开销,进一步拖慢传输速度。
内容的提问来源于stack exchange,提问作者Babak irannezhad
相关产品推荐
相关产品推荐

