golang聊天室的搭建(实现room,群聊,指定room推送)加锁版本

package main

import (
   "log"
   "net/http"
   "github.com/gorilla/websocket"
   "sync"
   "fmt"
   "strconv"
   "encoding/json"
)

//定义我们的消息对象
type Message struct {
   Room_id int    `json:"room_id"`
   Uid     int    `json:"uid"`
   Name    string `json:"name"`
   Type    string
   Msg     string
   Price   string //用户出价的价格
   Bdlist  string
}

//var lock sync.Mutex
var lock sync.RWMutex
var room = make(map[int]map[*websocket.Conn]int) //房间的详情
var price = make(map[int]int)                    // 房间的价格
var count = make(map[int]int)                    // 房间实时人数
var broadcast = make(chan Message)               // 创建广播信道

//websocket设置
var upgrader = websocket.Upgrader{
   CheckOrigin: func(r *http.Request) bool {
      return true
   },
}

//创建web_socket连接
func handleConnections(w http.ResponseWriter, r *http.Request) {
   //http升级到web_socket
   ws, err := upgrader.Upgrade(w, r, nil)
   if err != nil {
      log.Fatal(err)
   }
   defer ws.Close()
   //监听消息
   //room =>
   //    room_id=>
   //          ws=>uid
   for {
      var msg Message
      //读取json消息,并解析为结构体
      err := ws.ReadJSON(&msg)
      msg.Type = "join"
      //上锁,防止数据的冲突
      lock.Lock()
      //存储信息到对应房间
      if _, ok := room[msg.Room_id]; !ok { //判断该房间是否存在
         //不存在
         mm := make(map[*websocket.Conn]int)
         mm[ws] = msg.Uid
         room[msg.Room_id] = mm
         count[msg.Room_id] = 1
      } else {
         //存在
         room[msg.Room_id][ws] = msg.Uid
         //更新人数
         count[msg.Room_id] += 1
      }
      delete(room, 0)
      lock.Unlock()
      //注册到新客户缓存中
      //读取失败,删除用户
      if err != nil {
         delete(room[msg.Room_id], ws)
         break
      }
      //设置关闭程序(检测断开后的在线人数变化)
      ws.SetCloseHandler(func(code int, text string) error {
         count[msg.Room_id] -= 1
         return nil
      })
      //将新接收的消息发送到广播信道
      broadcast <- msg
   }
}

//全局推送消息
func push() {
   for {
      //从广播频道抓取下一条消息
      msg := <-broadcast
      //获取推送信息
      msginfo := msginfo(&msg)
      //将其发送到当前连接的每个客户端
      //上锁,保证数据
      lock.RLock()
      //对应房间推送
      for k, _ := range room[msg.Room_id] {
         //在本房间的进行推送
         err := k.WriteJSON(msginfo)
         //推送失败,删除数组中的该用户状态
         if err != nil {
            k.Close()
            //fmt.Println("错误的:",room[msg.Room_id][k])
            delete(room[msg.Room_id], k)
            //fmt.Println("删除后:",room)
         }
      }
      //活动结束后,关闭房间
      if msginfo["type"] == "end" {
         delete(room, msg.Room_id)
         delete(count, msg.Room_id)
         delete(price, msg.Room_id)
      }
      lock.RUnlock()
   }
}

//client发送数据组装
func msginfo(m *Message) (msg map[string]interface{}) {
   msg = make(map[string]interface{})
   switch m.Type {
   case "join":
      msg["type"] = "join"                //加入房间
      msg["new_price"] = price[m.Room_id] //最新价格
      msg["number"] = count[m.Room_id]    //最新人数
      msg["name"] = m.Name
      return
   case "start":
      msg["type"] = "start"
      msg["number"] = count[m.Room_id]
      msg["room_id"] = m.Room_id
      price[m.Room_id], _ = strconv.Atoi(m.Price)
      return
   case "change":
      msg["type"] = "change"
      msg["name"] = m.Name
      msg["room_id"] = m.Room_id
      msg["price"] = m.Price
      msg["bdlist"] = m.Bdlist
      price[m.Room_id], _ = strconv.Atoi(m.Price)
   case "end":
      msg["type"] = "end"
      msg["name"] = m.Name
      msg["room_id"] = m.Room_id
      msg["price"] = m.Price
      break
   }
   return
}

//结果
func errors(code int, message string) string {
   data := make(map[string]interface{})
   data["code"] = code
   data["data"] = nil
   data["message"] = message
   datas, _ := json.Marshal(data)
   return string(datas)
}

//开始拍卖
func start(w http.ResponseWriter, r *http.Request) {
   w.Header().Set("Content-Type", "application/json;charset=UTF-8")
   new_price := r.PostFormValue("new_price")
   room_id := r.PostFormValue("room_id")
   if len(room_id) > 0 {
      var msg Message
      msg.Type = "start"
      msg.Room_id, _ = strconv.Atoi(room_id)
      msg.Price = new_price
      //将新接收的消息发送到广播信道
      broadcast <- msg
      fmt.Fprintln(w, errors(200, "推送成功"))
   } else {
      fmt.Fprintln(w, errors(403, "缺少参数"))
   }
}

//出价
func change(w http.ResponseWriter, r *http.Request) {
   w.Header().Set("Content-Type", "application/json;charset=UTF-8")
   room_id := r.PostFormValue("room_id")
   name := r.PostFormValue("name")
   price := r.PostFormValue("price")
   bdlist := r.PostFormValue("bdlist")
   if len(room_id) > 0 && len(name) > 0 && len(price) > 0 && len(bdlist) > 0 {
      var msg Message
      msg.Type = "change"
      msg.Room_id, _ = strconv.Atoi(room_id)
      msg.Name = name
      msg.Price = price
      msg.Bdlist = bdlist
      //将新接收的消息发送到广播信道
      broadcast <- msg
      fmt.Fprintln(w, errors(200, "推送成功"))
   } else {
      fmt.Fprintln(w, errors(403, "缺少参数"))
   }
}

//结束拍卖
func end(w http.ResponseWriter, r *http.Request) {
   w.Header().Set("Content-Type", "application/json;charset=UTF-8")
   room_id := r.PostFormValue("room_id")
   name := r.PostFormValue("name")
   price := r.PostFormValue("price")
   if len(room_id) > 0 && len(name) > 0 && len(price) > 0 {
      var msg Message
      msg.Type = "end"
      msg.Room_id, _ = strconv.Atoi(room_id)
      msg.Name = name
      msg.Price = price
      //将新接收的消息发送到广播信道
      broadcast <- msg
      fmt.Fprintln(w, errors(200, "推送成功"))
   } else {
      fmt.Fprintln(w, errors(403, "缺少参数"))
   }
}

func main() {
   // 配置Web的路由
   http.HandleFunc("/ws", handleConnections)
   http.HandleFunc("/start", start)
   http.HandleFunc("/change", change)
   http.HandleFunc("/end", end)
   //开始监听传入聊天消息
   go push()
   //在本地主机端口8000启动服务器并记录错误
   log.Println("http server started on :88")
   err := http.ListenAndServe(":88", nil)
   if err != nil {
      log.Fatal("ListenAndServe: ", err)
   }
}

猜你喜欢

转载自blog.csdn.net/feiwutudou/article/details/80948370
今日推荐