Go WebSocket 的使用

您所在的位置:网站首页 gowebsocket项目 Go WebSocket 的使用

Go WebSocket 的使用

2023-12-02 12:22| 来源: 网络整理| 查看: 265

WebSocket在 HTML5 游戏和网页消息推送都使用比较多。WebSocket 是 HTML5 的重要特性,它实现了基于浏览器的远程socket,它使浏览器和服务器可以进行全双工通信。 WebSocket 具体的特性和 http 的区别这里就不多说,可以去自己查一下。

Go 官方没有提供对 WebSocket 的支持,必须选择第三方提供的包。《Go Web 编程》一书中的例子使用了 golang.org/x/net 下的 websocket 包。 另外一个使用比较多的是 gorilla/websocket ,我接触的项目是使用的这个。下面我就以 gorilla/websocket 来写一个简单的通信示例。

gorilla/websocket 的资料参考: GitHub:https://github.com/gorilla/websocket Doc:https://godoc.org/github.com/gorilla/websocket

gorilla/websocket 简述 Upgrader

Upgrader 用于升级 http 请求,把 http 请求升级为长连接的 WebSocket。结构如下:

type Upgrader struct { // 指定升级 websocket 握手完成的超时时间 HandshakeTimeout time.Duration // 指定 io 操作的缓存大小,如果不指定就会自动分配。 ReadBufferSize, WriteBufferSize int // 写数据操作的缓存池,如果没有设置值,write buffers 将会分配到链接生命周期里。 WriteBufferPool BufferPool //按顺序指定服务支持的协议,如值存在,则服务会从第一个开始匹配客户端的协议。 Subprotocols []string // 指定 http 的错误响应函数,如果没有设置 Error 则,会生成 http.Error 的错误响应。 Error func(w http.ResponseWriter, r *http.Request, status int, reason error) // 请求检查函数,用于统一的链接检查,以防止跨站点请求伪造。如果不检查,就设置一个返回值为true的函数。 // 如果请求Origin标头可以接受,CheckOrigin将返回true。 如果CheckOrigin为nil,则使用安全默认值:如果Origin请求头存在且原始主机不等于请求主机头,则返回false CheckOrigin func(r *http.Request) bool // EnableCompression 指定服务器是否应尝试协商每个邮件压缩(RFC 7692)。 // 将此值设置为true并不能保证将支持压缩。 // 目前仅支持“无上下文接管”模式 EnableCompression bool } func (*Upgrader) Upgrade

Upgrade 函数将 http 升级到 WebSocket 协议。定义如下:

// responseHeader包含在对客户端升级请求的响应中。 // 使用responseHeader指定cookie(Set-Cookie)和应用程序协商的子协议(Sec-WebSocket-Protocol)。 // 如果升级失败,则升级将使用HTTP错误响应回复客户端 // 返回一个 Conn 指针,拿到他后,可使用 Conn 读写数据与客户端通信。 func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) 使用实例 type WsServer struct { ...... // 定义一个 upgrade 类型用于升级 http 为 websocket upgrade *websocket.Upgrader } func NewWsServer() *WsServer { ws.upgrade = &websocket.Upgrader{ ReadBufferSize: 4096,//指定读缓存区大小 WriteBufferSize: 1024,// 指定写缓存区大小 // 检测请求来源 CheckOrigin: func(r *http.Request) bool { if r.Method != "GET" { fmt.Println("method is not GET") return false } if r.URL.Path != "/ws" { fmt.Println("path error") return false } return true }, } return ws } func (self *WsServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { ...... // 收到 http 请求后 升级 协议 conn, err := self.upgrade.Upgrade(w, r, nil) if err != nil { fmt.Println("websocket error:", err) return } fmt.Println("client connect :", conn.RemoteAddr()) go self.connHandle(conn) } http 服务

启动一个 http 服务有多种方法

第一种 func (self *WsServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/v1" { httpCode := http.StatusInternalServerError reasePhrase := http.StatusText(httpCode) fmt.Println("path error ", reasePhrase) http.Error(w, reasePhrase, httpCode) return } } func (w *WsServer) Start() (err error) { // 使用 net.Listen 监听端口服务 w.listener, err = net.Listen("tcp", w.addr) if err != nil { fmt.Println("net listen error:", err) return } // 启动 http 服务, w 参数类型需要 实现 Handler 接口,也就是 ServeHTTP 函数 err = http.Serve(w.listener, w) if err != nil { fmt.Println("http serve error:", err) return } return nil } 第二种 import ( "fmt" "io" "net/http" ) func serveHandle(w http.ResponseWriter, r *http.Request) { buf := make([]byte, 1024) r.Body.Read(buf) fmt.Println("request body", string(buf)) io.WriteString(w, "hello http server 1") } func main() { http.HandleFunc("/v1", serveHandle) // 直接使用 http 包的 ListenAndServe 函数监听服务 http.ListenAndServe(s.addr, nil) } WebSocket 完整代码

http 服务 + Upgrade 实现 WebSocket

package main import ( "fmt" "net" "net/http" "time" "github.com/gorilla/websocket" ) type WsServer struct { listener net.Listener addr string upgrade *websocket.Upgrader } func NewWsServer() *WsServer { ws := new(WsServer) ws.addr = "0.0.0.0:10215" ws.upgrade = &websocket.Upgrader{ ReadBufferSize: 4096, WriteBufferSize: 1024, CheckOrigin: func(r *http.Request) bool { if r.Method != "GET" { fmt.Println("method is not GET") return false } if r.URL.Path != "/ws" { fmt.Println("path error") return false } return true }, } return ws } func (self *WsServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/ws" { httpCode := http.StatusInternalServerError reasePhrase := http.StatusText(httpCode) fmt.Println("path error ", reasePhrase) http.Error(w, reasePhrase, httpCode) return } conn, err := self.upgrade.Upgrade(w, r, nil) if err != nil { fmt.Println("websocket error:", err) return } fmt.Println("client connect :", conn.RemoteAddr()) go self.connHandle(conn) } func (self *WsServer) connHandle(conn *websocket.Conn) { defer func() { conn.Close() }() stopCh := make(chan int) go self.send(conn, stopCh) for { conn.SetReadDeadline(time.Now().Add(time.Millisecond * time.Duration(5000))) _, msg, err := conn.ReadMessage() if err != nil { close(stopCh) // 判断是不是超时 if netErr, ok := err.(net.Error); ok { if netErr.Timeout() { fmt.Printf("ReadMessage timeout remote: %v\n", conn.RemoteAddr()) return } } // 其他错误,如果是 1001 和 1000 就不打印日志 if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseNormalClosure) { fmt.Printf("ReadMessage other remote:%v error: %v \n", conn.RemoteAddr(), err) } return } fmt.Println("收到消息:", string(msg)) } } //测试一次性发送 10万条数据给 client, 如果不使用 time.Sleep browser 过了超时时间会断开 func (self *WsServer) send10(conn *websocket.Conn) { for i := 0; i < 100000; i++ { data := fmt.Sprintf("hello websocket test from server %v", time.Now().UnixNano()) err := conn.WriteMessage(1, []byte(data)) if err != nil { fmt.Println("send msg faild ", err) return } // time.Sleep(time.Millisecond * 1) } } func (self *WsServer) send(conn *websocket.Conn, stopCh chan int) { self.send10(conn) for { select { case = 2000){ counter = 0; console.log("handler receive message ", data) } } }; let socket = new VIL.EngineSocket("ws://127.0.0.1:10215/ws", handler);

WebSocket 服务端 和 客户端在 GitHub 上有源码,需要的可以访问 https://github.com/vilsongwei/practiceDemo/tree/master/websockTest



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3