什么是websocket
- WebSocket是HTML5下一种新的协议(websocket协议本质上是一个基于tcp的协议)
- 它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯的目的
- Websocket是一个持久化的协议
为什么需要websocket
HTTP
这与HTTP有关
HTTP协议是无状态协议,HTTP协议自身不对请求和响应之间的通信状态进行保存
也就是说在HTTP协议对于发送过的请求或响应都不做持久化处理
并且,HTTP协议是无连接的
无连接的含义是限制每次连接只处理一个请求
服务器处理完客户的请求,并收到客户的应答后,即断开连接
然而,HTTP协议存在以下问题:
- 无状态:因此每次会话完成后,服务端都不知道下一次客户端身份
- 无连接:HTPP协议采用一次请求,一次响应,每次请求和响应就携带有大量的header,因此,效率也更低下
- 服务端不能主动发送向客户端推送数据,只能在收到消息的时候给客户端回复
比如实现QQ消息及时推送该怎么办?
于是websocket该上场了
websocket
一旦WebSocket连接建立后,后续数据都以帧序列的形式传输。
在客户端断开WebSocket连接或Server端中断连接前,不需要客户端和服务端重新发起连接请求。
在海量并发及客户端与服务器交互负载流量大的情况下,极大的节省了网络带宽资源的消耗,有明显的性能优势,且客户端发送和接受消息是在同一个持久连接上发起,实现了“真·长链接”,实时性优势明显。
go操作websocket
推荐使用 gorilla/websocket 库 在终端输入:
go get github.com/gorilla/websocket
这里用gin框架来实现其example
升级协议
使用websocket,我们需要使用http协议来交换信息,让客户端与服务端达成一致,然后升级协议
需要用到这个websocket.Upgrader
type Upgrader struct {
// HandshakeTimeout 指定握手完成的持续时间。
HandshakeTimeout time.Duration
//ReadBufferSize和WriteBufferSize以字节为单位指定I/O缓冲区大小。
ReadBufferSize, WriteBufferSize int
//WriteBufferPool是用于写入操作的缓冲区池
WriteBufferPool BufferPool
//Subprotocols 包含需要用到的子协议
Subprotocols []string
// Error 指定用于生成HTTP错误响应的函数。如果出现错误为nil,则使用http.Error生成http响应。
Error func(w http.ResponseWriter, r *http.Request, status int, reason error)
//CheckOrigin 防止跨站点请求伪造
CheckOrigin func(r *http.Request) bool
// EnableCompression 指定服务器是否应尝试根据消息压缩(RFC 7692)
EnableCompression bool
}
一般无需特别设置
var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
},
}
然后使用这个方法,即可完成协议升级
func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error)
代码如下
package main
import (
"log"
"net/http"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
)
func main() {
r := gin.Default()
r.GET("/websocket", websocketFc)
r.Run(":8080")
}
func websocketFc(c *gin.Context) {
//设置Upgrader
var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
},
}
//升级协议,返回ws连接
conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
if err != nil {
log.Println(err)
c.JSON(200, gin.H{
"status": 500,
"info": "failed",
})
return
}
//......
}
收发数据
//接受数据
go func() {
for ; ; {
_, p, err := conn.ReadMessage()
if err != nil {
log.Println(err)
} else {
log.Println(string(p))
}
}
}()
//发送数据
go func() {
for i := 0; ; i++ {
conn.WriteJSON(gin.H{
"time": time.Now().Format(time.RFC3339),
"No": i,
})
}
}()
总结
在服务端需要主动向客户端推送消息时 websockt有很大的作用。