握手阶段采用 HTTP 协议,能通过各种 HTTP 代理服务器
数据格式比较轻量,性能开销小,通信高效
可以发送文本和二进制数据
没有浏览器的同源限制
</ol>websocket 的典型场景:
<a name="_lab2_0_1">
2. WebSocket 推送和浏览器轮询
在 B/S 开发领域,若需要浏览器 B 即时得到服务器的状态更新,常使用两个方案:
浏览器轮询:浏览器端,当需要获取最新数据状态时,利用脚本程序循环向服务端发送请求。
服务器推送,服务器端,当状态改变时,将数据发送到浏览器端。
HTTP/2 版本也支持服务器端推送,但实现上以推送静态资源为主,不能基于业务逻辑推送特定的消息,因此当前的普及使用率 websocket 还是主流。
3. WebSocket 和 http
相同点
- 应用层协议
- B/S 架构中使用
- 基于 TCP 协议
- 端口默认都是:80 和 443
不同点
4. WebSocket 握手过程
通过 HTTP 请求响应,中的头信息,完成 websocket 握手,如图:
基于以上请求头,服务器端,就知道需要将协议升级为 websocket 协议,并提供一些验证信息。
- ws://host:port/path/querywss://host:port/path/query
复制代码 基于以上响应头,浏览器端就知道服务器端升级成功,并通过了验证。
至此,B/S 端可以基于该连接,完成 websocket 双向通信了。
websocket 只能发送 GET 请求
5. WebSocket 状态码和消息类型
5.1 状态码
WebSocket协议状态码解析
5.2 消息类型
TextMessage 和 BinaryMessage 分别表示发送文本消息和二级制消息
CloseMessage 关闭帧,接收方收到这个消息就关闭连接
PingMessage 和 PongMessage : 是保持心跳的帧
- 发送方 -> 接收方是 PingMessage
- 接收方 -> 发送方是 PongMessage
- # 升级为 websocket
- Upgrade: websocket
- Connection: Upgrade
- # 一个 Base64 encode 的值,有于验证服务器端是否支持websocket
- Sec-WebSocket-Key: x4JJHMbDL22zLk1GBhXDw==
- # 用户协议,可以视为不同业务逻辑的频道
- Sec-WebSocket-Protocol: chat
- # 协议版本,13是当前通用版本,几乎不需要更改
- Sec-WebSocket-Version: 13
复制代码 6. WebSocket 服务器实现
使用 github.com/gorilla/websocket 这个库函数- HTTP/1.1 101 Switching Protocols
- # 协议升级
- Upgrade: websocket
- # 连接状态
- Connection: Upgrade
- # WebSocket服务端根据Sec-WebSocket-Key生成
- Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
- # WebSocket协议用户协议
- Sec-WebSocket-Protocol: chat
复制代码 使用 Apifox 测试 websocket 是否能连接并且发送消息
消息发送成功,同时也接收到来服务端的消息
消息接收成功
7. WebSocket 代理实现
- 由服务器发 ping 给浏览器,浏览器返回 pong 消息
复制代码
8. WebSocket 服务端主动推送功能的实现
- func WebSocketServer() {
- addr := "localhost:8002"
- http.HandleFunc("/wshandler", WebSocketUpgrade)
- log.Println("Starting websocket server at " + addr)
- go func() {
- err := http.ListenAndServe(addr, nil)
- if err != nil {
- log.Fatal(err)
- }
- }()
- log.Println("WebSocket 服务器正在运行。按Ctrl+C退出")
- select {}
- }
- func WebSocketUpgrade(resp http.ResponseWriter, req *http.Request) {
- // 初始化 Upgrader
- upgrader := websocket.Upgrader{} // 使用默认的选项
- // 第三个参数是响应头,默认会初始化
- conn, err := upgrader.Upgrade(resp, req, nil)
- if err != nil {
- log.Println(err)
- return
- }
- defer conn.Close()
- // 读取客户端的发送额消息,并返回
- go ReadMessage(conn)
- select {}
- }
- // 读取客户端发送的消息,并返回
- func ReadMessage(conn *websocket.Conn) {
- for {
- // 消息类型:文本消息和二进制消息
- messageType, msg, err := conn.ReadMessage()
- if err != nil {
- log.Println(err)
- return
- }
- fmt.Println("receive msg:", string(msg))
- err = conn.WriteMessage(messageType, msg)
- if err != nil {
- log.Println("write error:", err)
- return
- }
- }
- }
复制代码 每隔三秒可以看到服务推送过来的消息
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
来源:互联网
免责声明:如果侵犯了您的权益,请联系站长(1277306191@qq.com),我们会及时删除侵权内容,谢谢合作! |