webSocket server 跟http包一起使用方法
最近在捣鼓一个小工具需要用到http+websocket,就去百度了下发现网上大部分教程都是用的github.com/gorilla/websocket
这个包作为实例。之前记得x/net
下面是有一个websocket
包的.想着官方的东西解耦可能更好点,就再网上搜了下但是关于这个包的实例很少。既然这样那就看下这个包下有没有example(go的每个官方包基本上下面都有test,但是test里面大都是实现自己的内部的单元测试)
提供给我们,看了下golang的确提供了俩个文件
- examplehandler_test.go(跟http包配合使用的实例)
- exampledial_test.go(客户端websocket的实例)
我这里把examplehandler_test.go
的代码改动贴出来
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"io"
"net/http"
"golang.org/x/net/websocket"
)
// Echo the data received on the WebSocket.
func EchoServer(ws *websocket.Conn) {
io.Copy(ws, ws)
}
// This example demonstrates a trivial echo server.
func main() {
http.Handle("/echo", websocket.Handler(EchoServer))
err := http.ListenAndServe(":12345", nil)
if err != nil {
panic("ListenAndServe: " + err.Error())
}
}
如果你环境中有golang.org/x/net
这个库的话应该是c+v过来就能跑通的。使用websocktClinet连接的时候url输入 ws://127.0.0.1:12345/echo
应该是能正常连接的。如果这个时候你向服务器发送任何数据都会得到你发送过去的数据。
这里推荐一个websocket的在线测试工具,如果websocket是需要发送的内容是text类型用这个调试不错。
服务器 13:7:6
连接已建立,正在等待数据...
你 13:7:7
1234
服务器 13:7:7
1234
你 13:7:10
ad
服务器 13:7:10
ad
造成这个现象的原因就是EchoServer
函数中的io.Copy(ws, ws)
,由于 websocket.Conn
实现了net.Conn
,net.Conn
又实现了Writer
跟Reader
,所以功能原理是:读取客户端的发过来的数据,并返回给客户端!并且io.Copy
这个函数会造成io堵塞从而保持住websocket的连接!这里我们稍微改造下让他给客户端发送的消息内容受我们的控制。
func EchoServer(ws *websocket.Conn) {
defer ws.Close()
websocket.Message.Send(ws, "你好欢迎访问本服务器!")
for {
var message string
err := websocket.Message.Receive(ws, &message)
if err != nil {
websocket.Message.Send(ws, "服务出现错误!")
}
websocket.Message.Send(ws, message+" -fromServer")
}
}
我们每次连接上websocket他都会给我发送一条信息 欢迎访问
,并且客户端给服务端发送的每条消息都会被加上后缀-fromServer
服务器 16:44:56
连接已建立,正在等待数据...
服务器 16:44:56
你好欢迎访问本服务器!
你 16:44:57
asd
服务器 16:44:57
asd -fromServer
这里的websocket.Message
的是对websocket.Codec
的封装,Codec
为了让开发者更好对数据进行转换类型做的开发,类似json库。Message暂时只支持对string和[]byte的类型转换
。当然直接调用ws.Read
、ws.Write
也能进行读写不过没有Codec
方便应为他是更高级别的封装。(go官方还提供了基于Codec的JSON的实现,具体可以自行测试!)
经过反复测试后得知如果客户端主动关闭了连接websocket.Message.Receive
这里的err如果为io.EOF代表客户端跟服务端断开连接了,最终修改为
func EchoServer(ws *websocket.Conn) {
defer ws.Close()
websocket.Message.Send(ws, "你好欢迎访问本服务器!")
for {
var message string
err := websocket.Message.Receive(ws, &message)
if err != nil {
if err == io.EOF {
log.Println("用户关闭了连接")
ws.Close()
}
}
websocket.Message.Send(ws, message+" -fromServer")
}
}
###握手(Handshake)
通过上面的代码我们知道可以使用websocket.Handler再http服务器上注册路由并处理我们自己的websocket的逻辑。那他是怎么实现handshake,进入源码里面发现websocket.Handler只是对websocket.Server的封装而已,而websocket.Server对外开放Handshake函数,websocket.Handler
只是内部实现了一个叫checkOrigin
的Handshake函数。下面是官方对这个函数的说明,具体的请自行翻译
// Handler is a simple interface to a WebSocket browser client.
// It checks if Origin header is valid URL by default.
接着看websocket.Handler
的ServeHTTP
函数,里面没几行代码就是初始化一个struct,并且调用了websocket.Server
的一个内部方法,当时感觉都要凉了,不过还是进去稍微看了下websocket.Serve
的源码,发现还好websocket.Serve
内部已经实现了Handler.ServeHTTP
方法(具体详情请查看源码!)。稍微写了个简单的Handshake函数
func EchoServer(ws *websocket.Conn) {
defer ws.Close()
websocket.Message.Send(ws, "你好欢迎访问本服务器!")
for {
var message string
err := websocket.Message.Receive(ws, &message)
if err != nil {
if err == io.EOF {
log.Println("用户关闭了连接")
ws.Close()
}
}
websocket.Message.Send(ws, message+" -fromServer")
}
}
func testHandshake(config *websocket.Config, request *http.Request) error {
if request.URL.Query().Get("name") != "帅哥" {
log.Println("有个丑比要连接我们的服务器")
return errors.New("你不是个帅哥拒绝连接!")
}
return nil;
}
func main() {
server := websocket.Server{Handshake: testHandshake, Handler: websocket.Handler(EchoServer)}
http.Handle("/echo", server)
err := http.ListenAndServe(":12345", nil)
if err != nil {
panic("ListenAndServe: " + err.Error())
}
}
这个时候如果连接的url中query.name不等于帅哥那么服务器就会断开连接!如果是的话则通过。真实环境中检查header应该会更多.握手通过地址
Handshake函数返回的err类型不限制,在底层调用的时候如果返回的错误不为空统统返回http 403