首先需要下载安装websocket
- cmd中:go get -u -v github.com/gorilla/websocket
首先在连接中传输的数据抽象出对象,这些对象在接下来都会用到
type Data struct{
Ip string `json:"ip"`
Type string `json:"type"`
// 代表哪个用户说的
From string `json:"from"`
// 传输内容
Content string `json:"content"`
// 用户名
User string `json:"user"`
// 用户列表
UserList []string `json:"user_list"`
}
创建一个ws连接器,这里需要创建一个websocket的链接,便于从后台导出数据
type connection struct {
// ws连接器
ws *websocket.Conn
//管道
send chan []byte
//数据
data *Data
}
接着创建一个hub,hub主要处理ws中的各种逻辑
type hub struct {
// connnections注册了连接器
connections map[*connection]bool
//从连接器发送的信息
broadcast chan []byte
// 从连接器注册请求
register chan *connection
//销毁请求
unregister chan *connection
}
创建了对象之后,就需要将连接器对象进行初始化
var h = hub{
//connections 注册了连接器
connections: make(map[*connection]bool),
//从连接器发送的信息
broadcast: make(chan []byte),
//从连接器注册请求
register: make(chan *connection),
//销毁请求
unregister: make(chan *connection),
}
创建一个字符串切片,存用户名
// 用户列表
var user_list = []string{}
ws的主要作用就是实现读和写,接下来就需要先实现ws的读和写
//ws中写数据
//这里必须要知道是谁写的,所以将connection绑定在这里
func (c *connection)writer(){
//从管道遍历数据
for message := range c.send{
// 数据写出
c.ws.WriteMessage(websocket.TextMessage,message)
}
c.ws.Close()
}
//ws连接中读数据
func (c *connection) reader(){
//创建一个死循环 ,不断地读数据
for{
_,message,err := c.ws.ReadMessage()
if err != nil{
// 读不进数据,将用户移除
h.unregister <- c
break
}
// 读取数据
json.Unmarshal(message,&c.data)
//根据data 的type判断该做什么,根据不同的数据类型,进行不同的处理
switch c.data.Type{
case "login":
// 弹出窗口
c.data.User = c.data.Content
c.data.From = c.data.User
//登录后
user_list = append(user_list,c.data.User)
//每个用户都加在所有登录的列表
c.data.UserList = user_list
// 数据序列化
data_b, _ := json.Marshal(c.data)
h.broadcast <- data_b
// 普通状态
case "user":
c.data.Type = "user"
data_b ,_ := json.Marshal(c.data)
h.broadcast <- data_b
case "logout":
//当用户退出时,就需要删除用户数据
default:
fmt.Println("其他")
}
}
}
当用户退出时,我们需要删除用户切片中的数据,所以就需要创建一个函数来删除数据
//删除用户切片中数据
func remove(slice []string,user string) []string{
// 严谨判断
count := len(slice)
if count == 0{
return slice
}
if count == 1 && slice[0] == user{
return []string{}
}
//定义新的返回切片
var my_slice = []string{}
//删除传入切片中的指定用户,其他用户放到新的切片
for i := range slice{
// 利用索引删用户
if slice[i] == user && i == count{
return slice[:count]
}else if slice[i] == user {
my_slice = append(slice[:i],slice[i+1:]...)
break
}
}
return my_slice
}
logout部分代码:
c.data.Type = "logout"
// 用户列表删除
user_list = remove(user_list,c.data.User)
c.data.UserList = user_list
c.data.Content = c.data.User
// 数据序列化,让所有人看到xxx下线了
data_b,_ := json.Marshal(c.data)
h.broadcast <- data_b
h.unregister <- c
先定义一个升级器,将http请求升级成websocket请求
var upgrader = &websocket.Upgrader{ReadBufferSize:1024,WriteBufferSize:1024,
CheckOrigin: func(r *http.Request) bool{
return true
}}
最终写主函数的时候,需要一个回调函数,主要写ws应该做什么事,这里的参数主要是进行请求和响应的
func wsHandler(w http.ResponseWriter, r *http.Request){
// 获取ws对象
ws,err := upgrader.Upgrader(w,r,nil)
if err != nil{
return
}
//创建连接诶额对象去做事情
// 初始化链接对象
c := &connection{send:make(chan[]byte,128),ws:ws,data:&Data{}}
// 在ws 中注册一下
h.register <- c
//ws将数据读写跑起来
go c.writer()
c.reader()
defer func(){
c.data.Type = "logout"
// 用户列表删除
user_list = remove(user_list,c.data.User)
c.data.UserList = user_list
c.data.Content = c.data.User
// 数据序列化,让所有人看到xxx下线了
data_b,_ := json.Marshal(c.data)
h.broadcast <- data_b
h.unregister <- c
}()
}
接着处理一下ws的逻辑实现
func (h *hub) run(){
//监听数据管道,在后端不断处理管道数据
for{
// 根据不同的数据管道,处理不同的逻辑
select {
// 注册
case c := <- h.register:
//标志注册了
h.connections[c] = true
// 组装data数据
c.data.Ip = c.ws.RemoteAddr().String()
// 更新类型
c.data.Type = "handshake"
// 用户列表
c.data.UserList = user_list
data_b, _ := json.Marshal(c.data)
//将数据放入数据管道
c.send <- data_b
case c := <- h.unregister:
//判断map里存在要删除的数据
if _,ok := h.connections[c]; ok{
delete(h.connections,c)
close(c.send)
}
case data := <- h.broadcast:
//处理数据流转,将数据同步到所有用户
// c是具体的每一个连接
for c := range h.connections{
// 将数据同步
select {
case c.send <- data:
default:
//防止死循环
delete(h.connections,c)
close(c.send)
}
}
}
}
接着就是创建一个主函数
func main() {
//创建路由
router := mux.NewRouter()
// ws控制器不断地去处理管道数据,进行同步数据
go h.run()
//指定ws回调函数
router.HandleFunc("/ws",wsHandler)
// 开启服务器监听
if err := http.ListenAndServe("127.0.0.1:8080",router);err != nil{
fmt.Println("err:", err)
}
}