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)
}
}
golang聊天室的搭建(实现room,群聊,指定room推送)加锁版本
最新推荐文章于 2024-05-22 10:07:31 发布