开始
原理:
- WebSocket 同 HTTP 一样也是应用层的协议,但是它是一种双向通信协议,是建立在 TCP 之上的。
WebSocket和Http的关系和异同点
- 每个WebSocket连接都始于一个HTTP请求。 具体来说,WebSocket协议在第一次握手连接时,通过HTTP协议在传送WebSocket支持的版本号,协议的字版本号,原始地址,主机地址等等一些列字段给服务器端
- Upgrade首部,用来把当前的HTTP请求升级到WebSocket协议,这是HTTP协议本身的内容,是为了扩展支持其他的通讯协议。 如果服务器支持新的协议,则必须返回101
- 一个WebSocket连接是在客户端与服务器之间HTTP协议的初始握手阶段将其升级到Web Socket协议来建立的,其底层仍是TCP/IP连接 ***
- 相同点:
- (1)都是建立在TCP之上,通过TCP协议来传输数据。
- (2)都是可靠性传输协议。
- (3)都是应用层协议。
- 不同点:
- (1)WebSocket支持持久连接,HTTP不支持持久连接。
- (2)WebSocket是双向通信协议,HTTP是单向协议,只能由客户端发起,做不到服务器主动向客户端推送信息。
WebSocket和Socket
- Socket 其实并不是一个协议,而是为了方便使用 TCP 或 UDP 而抽象出来的一层,是位于应用层和传输控制层之间的一组接口。 Socket本身并不是一个协议,它工作在OSI模型会话层,是一个套接字,TCP/IP网络的API,是为了方便大家直接使用。
- 更底层协议而存在的一个抽象层。Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。 而WebSocket则是一个典型的应用层协议。
WebSocket HTTP和TCP/IP
- WebSocket和HTTP一样,都是建立在TCP之上,通过TCP来传输数据。
Socket和TCP/IP
- Socket是对TCP/IP协议的封装,像创建Socket连接时,可以指定使用的传输层协议,Socket可以支持不同的传输层协议(TCP或UDP),当使用TCP协议进行连接时,该Socket连接就是一个TCP连接。
搭建websocket消息推送服务,要考虑的以下问题
- 心跳机制
- 具有良好兼容性的SDK
- 断网自动重连和消息补发机制
- 离线消息
- 上下线提醒,客户端在线列表
- 历史消息查询
- 消息的压缩
- 不论是为了保证消息通讯的速度和实时性,还是为了节约流量和带宽费用,或者是出于提高网卡的使用效率和增加系统的吞吐量,在通讯过程中对消息进行必要的压缩都是必不可少的。
- 缓存和持久化
- 异步调用
- 独立于业务和标准化
- 幂等性和重复消息的过滤
- 支持QoS 服务质量分级
- QoS 0(At most once):“最多发一次”,意味着发送就可以了,不需要确认机制,发送了即可,适用于要求不高的场景,可以接受一定的不到达率,成本最低。
- QoS 1(At least once):“至少发一次”,意味着发送方必须明确收到接收方的确认信号,否则就会反复发,每条消息至少需要两次通信来确认到达,可以接受一些消息被重发,但成本不高 。
- QoS 2(Exactly once):“确保只发一次”,意味着每条消息只能到达一次,且不允许重复到达,为了达到这个目标就需要双方至少通讯三次,成本最高。
客户端
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
// github.com/gorilla/websocket
u := url.URL{Scheme: "ws", Host: "localhost:8080", Path: "/echo"}
fmt.Println("connecting to", u.String())
// set up websocket connection
conn, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
if err != nil {
fmt.Println("dial error:", err)
}
defer conn.Close()
// write message to websocket connection
message := []byte("hello, world!")
err = conn.WriteMessage(websocket.TextMessage, message)
if err != nil {
fmt.Println("write message error:", err)
}
// read message from websocket connection
_, p, err := conn.ReadMessage()
if err != nil {
fmt.Println("read message error:", err)
}
fmt.Printf("received message: %s\n", p)
// wait for some time and then close the connection
time.Sleep(2 * time.Second)
fmt.Println("closing the connection...")
服务端
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
// github.com/gorilla/websocket
upgrader := websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool {
return true
},
}
http.HandleFunc("/echo", func(w http.ResponseWriter, r *http.Request) {
fmt.Println("connection established")
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
fmt.Println("upgrade error:", err)
return
}
defer conn.Close()
// read message from websocket connection
messageType, message, err := conn.ReadMessage()
if err != nil {
fmt.Println("read message error:", err)
return
}
fmt.Printf("received message: %s\n", message)
// send the message back to the client
if err := conn.WriteMessage(messageType, message); err != nil {
fmt.Println("write message error:", err)
return
}
fmt.Println("message sent back to client")
// wait for some time and then close the connection
time.Sleep(2 * time.Second)
fmt.Println("closing the connection...")
})
fmt.Println("listening on :8080")
http.ListenAndServe(":8080", nil)
测试代码
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
48
49
50
51
52
53
54
55
56
57
58
// github.com/gorilla/websocket
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
upgrader := websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool {
return true
},
}
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
t.Fatalf("upgrade error: %v", err)
}
defer conn.Close()
// read message from websocket connection
messageType, message, err := conn.ReadMessage()
if err != nil {
t.Fatalf("read message error: %v", err)
}
if string(message) != "hello, world!" {
t.Fatalf("received unexpected message: %s", string(message))
}
// send the message back to the client
if err := conn.WriteMessage(messageType, message); err != nil {
t.Fatalf("write message error: %v", err)
}
}))
defer server.Close()
u := "ws" + strings.TrimPrefix(server.URL, "http") + "/echo"
conn, _, err := websocket.DefaultDialer.Dial(u, nil)
if err != nil {
t.Fatalf("dial error: %v", err)
}
defer conn.Close()
// write message to websocket connection
message := []byte("hello, world!")
if err := conn.WriteMessage(websocket.TextMessage, message); err != nil {
t.Fatalf("write message error: %v", err)
}
// read message from websocket connection
_, p, err := conn.ReadMessage()
if err != nil {
t.Fatalf("read message error: %v", err)
}
if string(p) != "hello, world!" {
t.Fatalf("received unexpected message: %s", string(p))
}