WebSocket 和 HTTP
WebSocket和HTTP协议一样,都是基于TCP协议实现的应用层协议。
HTTP协议通常是单边通信,主要用于传输静态文档、请求-响应通信,适用于Web浏览器加载网页、API调用等。然而WebSocket则是会主动给你发消息,实现实时双向通信,我们经常玩的游戏、使用的聊天软件什么采用的都是这个协议。
这里进行详细比较
共同点:
- 基于TCP: WebSocket和HTTP都是基于TCP协议的应用层协议,用于在客户端和服务器之间进行通信。
- 使用URI(统一资源标识符): 两者都使用统一资源标识符(URI)来标识资源或服务的位置。
- 都是面向应用层的协议: WebSocket和HTTP都属于应用层协议,用于在不同设备或应用程序之间进行通信。
不同点:
- 连接性和通信方式:
- HTTP: 是一种无状态的请求-响应协议。每个HTTP请求都是独立的,服务器在每个请求之后会断开连接,需要在每次请求时重新建立连接。
- WebSocket: 提供了全双工通信,允许客户端和服务器之间建立持久的连接,可以实现双向实时通信,而不需要为每个消息建立新的连接。
- 数据帧格式:
- HTTP: 通常使用明文文本或基于二进制的数据传输,但每个请求和响应都具有特定的结构。
- WebSocket: 使用帧(frame)的形式传输数据,可以是文本帧或二进制帧。
- 协议头和握手:
- HTTP: 通过请求头和响应头来传递元数据信息,如请求方法、状态码等。
- WebSocket: 在建立连接时,使用HTTP进行初始握手,然后切换到WebSocket协议。握手时使用特定的HTTP头部,如Upgrade和Connection。
- 目的和应用场景:
- HTTP: 主要用于传输静态文档、请求-响应通信,适用于Web浏览器加载网页、API调用等。
- WebSocket: 适用于需要实时、双向通信的应用场景,如实时聊天、在线游戏、股票市场数据更新等。
总的来说,WebSocket和HTTP都是网络通信中的重要协议,但它们在连接性、通信方式和应用场景等方面存在明显的区别。WebSocket更适合实时、双向通信,而HTTP主要用于请求-响应式的通信。
解析
接下来对此协议进行详细讲解,先看图:

首先客户端(client)发送连接请求,通过HTTP发送到服务端,但是请求头会进行一定改变,比如:
1 2 3 4 5 6 7 8
| GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13 Origin: http:
|
这其中包含了一些特殊的头部,比如 Upgrade
和 Connection
服务器接收到这个请求后,如果支持WebSocket,就会回复一个HTTP 101状态码(切换协议)以及一些额外的头部,表示同意建立WebSocket连接
1 2 3 4 5
| HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk= Sec-WebSocket-Protocol: chat
|
此过程则称为握手,服务端和客户端握手建立连接之后,连接将一直保持,直到其中一方终止
过程中的数据传输:客户端和服务器可以在连接上自由地发送文本或二进制数据帧。这些数据帧包含消息内容以及一些控制信息,如数据帧的类型和长度等
关闭连接:关闭连接时,终止方会发送一个特殊的关闭帧。
例子
以下是一个golang的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| package main
import ( "log" "net/http" "github.com/gorilla/websocket" )
var upgrader = websocket.Upgrader{ CheckOrigin: func(r *http.Request) bool { return true }, }
func handleConnections(w http.ResponseWriter, r *http.Request) { conn, err := upgrader.Upgrade(w, r, nil) if err != nil { log.Fatal(err) return } defer conn.Close()
for { messageType, p, err := conn.ReadMessage() if err != nil { log.Println(err) return }
if err := conn.WriteMessage(messageType, p); err != nil { log.Println(err) return } } }
func main() { http.HandleFunc("/ws", handleConnections) log.Println("WebSocket server is running on :8080") err := http.ListenAndServe(":8080", nil) if err != nil { log.Fatal("Error starting server: ", err) } }
|
客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| package main
import ( "fmt" "log" "github.com/gorilla/websocket" )
func main() { conn, _, err := websocket.DefaultDialer.Dial("ws://localhost:8080/ws", nil) if err != nil { log.Fatal(err) } defer conn.Close()
message := []byte("Hello, WebSocket!") err = conn.WriteMessage(websocket.TextMessage, message) if err != nil { log.Fatal(err) }
_, receivedMessage, err := conn.ReadMessage() if err != nil { log.Fatal(err) }
fmt.Println("Received message from server:", string(receivedMessage)) }
|
最后效果:
