Websoct入门 | 青训营

186 阅读3分钟

什么是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有很大的作用。