理论
WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。而且没有同源策略的限制,不存在跨域问题。协议的标识符就是ws
。像https一样如果加密的话就是wxs
。
参考资料
https://blog.csdn.net/yjp19871013/article/details/83444148
https://www.cnblogs.com/lanyangsh/p/9190296.html
https://www.cnblogs.com/bener/p/10717466.html
仅做个人笔记,浏览请看原博主原文
包
golang.org/x/net/websocket
https://godoc.org/golang.org/x/net/websocket
连接1
该Server能够处理两个请求
/请求,这是Web项目的根路径,index函数作为处理方法,返回首页index.html。
/upper请求,该请求的处理函数是一个WebSocket的处理函数,它包裹了我们自己定义的upper函数,upper函数有一个参数,就是服务端创建好的WebSocket连接,upper函数逻辑很简单,通过WebSocket连接读取服务器的请求内容,将内容转化为大写后,通过WebSocket将结果返回给客户端。
package main
import (
"fmt"
"html/template"
"net/http"
"os"
"strings"
"golang.org/x/net/websocket"
)
//处理ws请求
func upper(ws *websocket.Conn) {
var err error
for {
var reply string
if err = websocket.Message.Receive(ws, &reply); err != nil {
fmt.Println(err)
continue
}
if err = websocket.Message.Send(ws, strings.ToUpper(reply)); err != nil {
fmt.Println(err)
continue
}
}
}
//处理http请求
func index(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
return
}
t, _ := template.ParseFiles("index.html")
t.Execute(w, nil)
}
func main() {
//ws连接
http.Handle("/upper", websocket.Handler(upper))
//http连接
http.HandleFunc("/", index)
if err := http.ListenAndServe(":9999", nil); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
连接2
package main
import (
"golang.org/x/net/websocket"
"net/http"
)
func Echo(ws *websocket.Conn) {
var err error
for {
var reply string
//websocket接受信息
if err = websocket.Message.Receive(ws, &reply); err != nil {
fmt.Println("receive failed:", err)
break
}
fmt.Println("reveived from client: " + reply)
msg := "received:" + reply
fmt.Println("send to client:" + msg)
//这里是发送消息
if err = websocket.Message.Send(ws, msg); err != nil {
fmt.Println("send failed:", err)
break
}
}
}
func main() {
//接受websocket的路由地址
http.Handle("/websocket", websocket.Handler(Echo))
}
函数
// websocket connection manager
type ConnManager struct {
// websocket connection number
Online *int32
// websocket connection
connections *sync.Map
}
上面定义了一个连接管理结构体,Online为在线的人数,connections为客户端的连接管理(key为userId,value为websocket connection)。
下面为ConnManager添加一些方法来处理连接、断开连接、发送消息、广播等操作。
// add websocket connection
// online number + 1
func (m *ConnManager) Connected(k, v interface{}) {
m.connections.Store(k, v) //存储已连接用户的wsconn 以备后面对指定用户推送消息
atomic.AddInt32(m.Online, 1)
}
// remove websocket connection by key
// online number - 1
func (m *ConnManager) DisConnected(k interface{}) {
m.connections.Delete(k)
atomic.AddInt32(m.Online, -1)
}
// get websocket connection by key
func (m *ConnManager) Get(k interface{}) (v interface{}, ok bool) {
return m.connections.Load(k)
}
// iter websocket connections
func (m *ConnManager) Foreach(f func(k, v interface{})) {
m.connections.Range(func(k, v interface{}) bool {
f(k, v)
return true
})
}
// send message to one websocket connection
func (m *ConnManager) Send(k string, msg *Message) {
v, ok := m.Get(k) //对指定用户推送消息
// 注:
// 对指定用户推送消息,需要当前用户之前必须在登录服登录,我们才能记录他的wsconn,广播也是对这些在线用户发送消息
if ok {
if conn, ok := v.(*websocket.Conn); ok {
if err := websocket.JSON.Send(conn, msg); err != nil {
fmt.Println("Send msg error: ", err)
}
} else {
fmt.Println("invalid type, expect *websocket.Conn")
}
} else {
fmt.Println("connection not exist")
}
}
// send message to multi websocket connections
func (m *ConnManager) SendMulti(keys []string, msg interface{}) {
for _, k := range keys {
v, ok := m.Get(k)
if ok {
if conn, ok := v.(*websocket.Conn); ok {
if err := websocket.JSON.Send(conn, msg); err != nil {
fmt.Println("Send msg error: ", err)
}
} else {
fmt.Println("invalid type, expect *websocket.Conn")
}
} else {
fmt.Println("connection not exist")
}
}
}
// broadcast message to all websocket connections otherwise own connection
func (m *ConnManager) Broadcast(conn *websocket.Conn, msg *Message) {
m.Foreach(func(k, v interface{}) {
if c, ok := v.(*websocket.Conn); ok && c != conn {
if err := websocket.JSON.Send(c, msg); err != nil {
fmt.Println("Send msg error: ", err)
}
}
})
}