Home WebSocket
Post
Cancel

WebSocket

开始

原理:

  • 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))
	}
This post is licensed under CC BY 4.0 by the author.

MySQL实战45讲

OSI, TCP/IP Model