本文只是一个简单的案例,如有疑问,欢迎探讨!
首先来看,为什么需要WebSocket?
再来看一类需求:一个天气预报软件,我们需要根据天气变化实时获取最新数据,如果使用平常用的http需要客户端主动的从服务端拉取,并且我们不知道服务端什么时候会产生新的数据,在这个时候我们使用http就无法很好的完成这个功能了。
因为Http协议只能由客户端发起请求,而无法由服务端主动向客户端推送数据。所以介于类似的问题,WebSocket就油然而生了。
WebSocket 协议在2008年诞生,2011年成为国际标准,现在大部分的浏览器都已经支持该协议。
Golang中使用的WebSocket有好几个,本文使用的是gorilla里面的。
- 获取WebSocket
go get github.com/gorilla/websocket
- 普通的Http协议到WebSocket协议要进行升级处理
upgrader := websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
}
}
upgrader.Upgrade(w,r,nil)
每升级一次链接就表示有一个客户连接产生!
服务端代码是做为一个中转站,将客户端发过来的代码转发出去:
func main() {
http.HandleFunc("/serveWs", serveWs)
go handleMsg()
err := http.ListenAndServe(":8080", nil)
if err != nil {
fmt.Println("ListenAndServe出错:", err.Error())
}
}
func serveWs(w http.ResponseWriter, r *http.Request) {
count++
fmt.Println("新增客户端", count)
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
fmt.Println("升级ws出错:\n", err.Error())
return
}
defer conn.Close()
clients[conn] = true
for {
_, msg, err := conn.ReadMessage()
if err != nil {
fmt.Println("serveWs读取消息出错:\n", err.Error())
break
}
msg_chan <- msg
//fmt.Printf("serveWs读取到消息:%s,mt=%d\n", msg, mt)
// err = conn.WriteMessage(mt, msg)
// if err != nil {
// fmt.Println("serveWs写入消息出错:\n", err.Error())
// break
// }
}
}
func handleMsg() {
for {
data := <-msg_chan
for c, _ := range clients {
err := c.WriteMessage(websocket.TextMessage, &data)
if err != nil {
fmt.Println("服务端转发失败", err.Error())
return
}
}
}
}
客户端是简单的连接到服务,然后进行数据的发送。
var input string
func main() {
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt)
u := url.URL{Scheme: "ws", Host: "localhost:8080", Path: "/serveWs"}
fmt.Println("connecting to %s\n", u.String())
c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
if err != nil {
fmt.Println("连接失败:", err.Error())
return
}
defer c.Close()
go func() {
for {
_, msg, err := c.ReadMessage()
if err != nil {
fmt.Println("Client接收消息失败:\n", err.Error())
return
}
fmt.Printf("Client接收消息:%s\n", msg)
}
}()
go func() {
for {
n, err := fmt.Scanln(&input)
if err != nil {
fmt.Println(n, "--", err.Error())
} else {
err = c.WriteMessage(websocket.TextMessage, []byte(input))
if err != nil {
fmt.Println("client", err.Error())
return
}
}
}
}()
//ticker := time.NewTicker(2 * time.Second)
//defer ticker.Stop()
for {
select {
// case <-ticker.C:
// err := c.WriteMessage(websocket.TextMessage, []byte("Client写入.."))
// if err != nil {
// fmt.Println("Client写入数据失败:", err.Error())
// return
// }
case <-interrupt:
fmt.Println("客户端interrupt")
err := c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
if err != nil {
fmt.Println("客户端写入关闭数据失败:", err.Error())
return
}
}
}
}
本小案例还有一大堆的需要加的东西,争取下次把ping pong之类的配置加上去,共勉!
有疑问加站长微信联系(非本文作者)