理解HTTP如何升级为WebSocket需要了解一些协议层面的细节。WebSocket是基于HTTP协议的一个扩展,它允许客户端和服务器之间进行全双工通信。以下是详细的升级过程原理:

升级过程原理

  1. HTTP请求握手
    WebSocket连接始于一个HTTP请求,它包含特殊的头部字段,这些字段请求服务器升级连接到WebSocket协议。

  2. 客户端发送升级请求
    客户端发送一个HTTP GET请求到服务器,并在请求头中包含以下字段:

    • Upgrade: websocket:表示请求升级到WebSocket协议。
    • Connection: Upgrade:表示客户端希望升级连接。
    • Sec-WebSocket-Key:一个Base64编码的随机字符串,用于服务器生成响应。
    • Sec-WebSocket-Version:WebSocket协议版本,一般为13

    示例请求:

    GET /chat HTTP/1.1
    Host: example.com
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
    Sec-WebSocket-Version: 13
    
  3. 服务器处理升级请求
    服务器收到升级请求后,检查请求头以确保它们包含正确的值。如果请求有效,服务器生成一个响应以完成升级。

    服务器生成Sec-WebSocket-Accept值:

    • Sec-WebSocket-Key的值。
    • 连接一个固定的字符串258EAFA5-E914-47DA-95CA-C5AB0DC85B11
    • 对连接后的字符串进行SHA-1哈希计算。
    • 对哈希结果进行Base64编码。
  4. 服务器响应升级请求
    服务器发送一个HTTP 101响应,表示协议切换,并在响应头中包含以下字段:

    • Upgrade: websocket:确认协议升级。
    • Connection: Upgrade:确认协议升级。
    • Sec-WebSocket-Accept:包含服务器生成的接受密钥。

    示例响应:

    HTTP/1.1 101 Switching Protocols
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
    
  5. 建立WebSocket连接
    一旦服务器发送HTTP 101响应,连接正式升级为WebSocket协议,客户端和服务器可以开始通过WebSocket进行全双工通信。

代码实现示例

以下是一个简单的Go示例,展示如何实现HTTP到WebSocket的升级:

package main

import (
	"log"
	"net/http"
	"sync"

	"github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{
	CheckOrigin: func(r *http.Request) bool {
		return true // 允许所有来源
	},
}

var connections = make([]*websocket.Conn, 0)
var mu sync.Mutex

func handleConnections(w http.ResponseWriter, r *http.Request) {
	// 升级HTTP连接到WebSocket连接
	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Fatal(err)
	}
	defer ws.Close()

	mu.Lock()
	connections = append(connections, ws)
	mu.Unlock()

	for {
		// 读取消息
		messageType, message, err := ws.ReadMessage()
		if err != nil {
			log.Println(err)
			break
		}
		// 广播消息给所有连接
		mu.Lock()
		for _, conn := range connections {
			if conn != ws {
				if err := conn.WriteMessage(messageType, message); err != nil {
					log.Println(err)
				}
			}
		}
		mu.Unlock()
	}
}

func main() {
	http.HandleFunc("/ws", handleConnections)

	log.Println("服务器启动在 :8080")
	err := http.ListenAndServe(":8080", nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}

结论

WebSocket连接的升级过程通过HTTP请求握手完成。客户端发起一个带有特定头部的HTTP GET请求,服务器验证请求并响应协议切换,最终建立WebSocket连接。这一过程允许在客户端和服务器之间建立低延迟、全双工的通信通道。