WebSocket简介

RenXin Lv3

WebSocket 和 HTTP

WebSocket和HTTP协议一样,都是基于TCP协议实现的应用层协议。

HTTP协议通常是单边通信,主要用于传输静态文档、请求-响应通信,适用于Web浏览器加载网页、API调用等。然而WebSocket则是会主动给你发消息,实现实时双向通信,我们经常玩的游戏、使用的聊天软件什么采用的都是这个协议。

这里进行详细比较

共同点:

  1. 基于TCP: WebSocket和HTTP都是基于TCP协议的应用层协议,用于在客户端和服务器之间进行通信。
  2. 使用URI(统一资源标识符): 两者都使用统一资源标识符(URI)来标识资源或服务的位置。
  3. 都是面向应用层的协议: WebSocket和HTTP都属于应用层协议,用于在不同设备或应用程序之间进行通信。

不同点:

  1. 连接性和通信方式:
    • HTTP: 是一种无状态的请求-响应协议。每个HTTP请求都是独立的,服务器在每个请求之后会断开连接,需要在每次请求时重新建立连接。
    • WebSocket: 提供了全双工通信,允许客户端和服务器之间建立持久的连接,可以实现双向实时通信,而不需要为每个消息建立新的连接。
  2. 数据帧格式:
    • HTTP: 通常使用明文文本或基于二进制的数据传输,但每个请求和响应都具有特定的结构。
    • WebSocket: 使用帧(frame)的形式传输数据,可以是文本帧或二进制帧。
  3. 协议头和握手:
    • HTTP: 通过请求头和响应头来传递元数据信息,如请求方法、状态码等。
    • WebSocket: 在建立连接时,使用HTTP进行初始握手,然后切换到WebSocket协议。握手时使用特定的HTTP头部,如Upgrade和Connection。
  4. 目的和应用场景:
    • 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://example.com

这其中包含了一些特殊的头部,比如 UpgradeConnection

服务器接收到这个请求后,如果支持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) {
// 将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)
}
}

客户端

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() {
// 连接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))
}

最后效果:

  • Title: WebSocket简介
  • Author: RenXin
  • Created at : 2024-01-15 00:00:00
  • Updated at : 2024-01-15 22:54:18
  • Link: https://blog.renxin.space/tools/WebSocket/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments