webSocket server 跟http包一起使用方法

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 又实现了WriterReader,所以功能原理是:读取客户端的发过来的数据,并返回给客户端!并且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.Readws.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.HandlerServeHTTP函数,里面没几行代码就是初始化一个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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值