WebSocket
也可前往本人的个人网站进行阅读
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发送到服务端,但是请求头会进行一定改变,比如:
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://example.com
这其中包含了一些特殊的头部,比如 Upgrade
和 Connection
服务器接收到这个请求后,如果支持WebSocket,就会回复一个HTTP 101状态码(切换协议)以及一些额外的头部,表示同意建立WebSocket连接
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
此过程则称为握手,服务端和客户端握手建立连接之后,连接将一直保持,直到其中一方终止
过程中的数据传输:客户端和服务器可以在连接上自由地发送文本或二进制数据帧。这些数据帧包含消息内容以及一些控制信息,如数据帧的类型和长度等
关闭连接:关闭连接时,终止方会发送一个特殊的关闭帧。
例子
以下是一个golang的例子
package mainimport ("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) {// 将HTTP连接升级为WebSocket连接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)}
}
客户端
package mainimport ("fmt""log""github.com/gorilla/websocket"
)func main() {// 连接WebSocket服务器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))
}
最后效果: