├── .gitignore ├── README.md ├── banned_ip.go ├── banned_user.go ├── banned_words.go ├── broadcasting.go ├── chans.go ├── counter.go ├── daemon_change_room.go ├── daemon_clean.go ├── daemon_close.go ├── daemon_main_reciver.go ├── daemon_send_message.go ├── deamon_in_hall.go ├── error.go ├── log.go ├── node_message.go ├── public_run.go ├── public_settings.go ├── readme.go ├── recive_node.go ├── register.go ├── room.go ├── row_list.go └── signals.go /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #项目已经重构并发布在:https://github.com/bigBarrage/roomManager 2 | 3 | #欢迎参与并提出宝贵意见 4 | -------------------------------------------------------------------------------- /banned_ip.go: -------------------------------------------------------------------------------- 1 | package roomManager 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | var ipList []string 8 | var upListLock sync.RWMutex 9 | 10 | func SetIPList(list []string) { 11 | if len(list) == 0 { 12 | return 13 | } 14 | upListLock.Lock() 15 | defer upListLock.Unlock() 16 | ipList = list 17 | } 18 | 19 | func CheckIP(ip string) bool { 20 | if len(ipList) == 0 { 21 | return false 22 | } 23 | upListLock.RLock() 24 | defer upListLock.RUnlock() 25 | for _, v := range ipList { 26 | if v == ip { 27 | return true 28 | } 29 | } 30 | return false 31 | } 32 | -------------------------------------------------------------------------------- /banned_user.go: -------------------------------------------------------------------------------- 1 | package roomManager 2 | 3 | import ( 4 | "strings" 5 | "sync" 6 | ) 7 | 8 | var userList []string 9 | var userListLock sync.RWMutex 10 | 11 | func SetUserList(list []string) { 12 | if len(list) == 0 { 13 | return 14 | } 15 | userListLock.Lock() 16 | defer userListLock.Unlock() 17 | userList = make([]string, 0, 1024) 18 | for _, v := range list { 19 | userList = append(userList, strings.ToUpper(v)) 20 | } 21 | } 22 | 23 | func CheckUserID(userId string) bool { 24 | if len(userList) == 0 { 25 | return false 26 | } 27 | if userId == "" { 28 | return false 29 | } 30 | userListLock.RLock() 31 | defer userListLock.RUnlock() 32 | for _, v := range userList { 33 | if v == userId { 34 | return true 35 | } 36 | } 37 | return false 38 | } 39 | -------------------------------------------------------------------------------- /banned_words.go: -------------------------------------------------------------------------------- 1 | package roomManager 2 | 3 | import ( 4 | "errors" 5 | "strings" 6 | ) 7 | 8 | var wordList []string 9 | var WordsBannedError = errors.New("words in message is banned") 10 | 11 | func init() { 12 | wordList = make([]string, 0, 1024) 13 | } 14 | 15 | func SetWordList(list []string) { 16 | wordList = list 17 | } 18 | 19 | func CheckMessage(msg string) bool { 20 | if len(wordList) == 0 { 21 | return false 22 | } 23 | for _, v := range wordList { 24 | if strings.Index(msg, v) >= 0 { 25 | return true 26 | } 27 | } 28 | return false 29 | } 30 | -------------------------------------------------------------------------------- /broadcasting.go: -------------------------------------------------------------------------------- 1 | package roomManager 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "net/http" 7 | "net/url" 8 | "os" 9 | "time" 10 | 11 | "github.com/gorilla/websocket" 12 | ) 13 | 14 | var ( 15 | broadcastingHost = "localhost" 16 | broadcastingPort = 6666 17 | broadcastingURI = "broadcasting" 18 | useBroadcasting = false 19 | 20 | broadcastingConnection *websocket.Conn 21 | err error 22 | ) 23 | 24 | func ConnBroadcasting() { 25 | tryToConnBroadcastingStation() 26 | //处理读到消息之后 27 | go func() { 28 | for { 29 | mType, reader, err := broadcastingConnection.NextReader() 30 | if mType == websocket.CloseMessage || mType == -1 { 31 | tryToConnBroadcastingStation() 32 | } 33 | if err != nil { 34 | continue 35 | } 36 | msg := make([]byte, 1024) 37 | l, err := reader.Read(msg) 38 | if err != nil { 39 | continue 40 | } 41 | fmt.Println(string(msg)) 42 | proMessageFromBroadcast(msg[:l]) 43 | } 44 | }() 45 | } 46 | 47 | func tryToConnBroadcastingStation() { 48 | for connCount := 0; connCount < 10; connCount++ { 49 | fmt.Print(time.Now().Format("2006-01-02 15:04:05"), "[尝试创建广播站连接] ===========>") 50 | conn, err := net.Dial("tcp", broadcastingHost+":"+fmt.Sprint(broadcastingPort)) 51 | if err != nil { 52 | fmt.Println("广播站连接失败:", broadcastingHost+":"+fmt.Sprint(broadcastingPort), err) 53 | continue 54 | } 55 | u := url.URL{} 56 | u.Host = broadcastingHost + ":" + fmt.Sprint(broadcastingPort) 57 | u.Path = broadcastingURI 58 | u.Scheme = "ws" 59 | 60 | h := http.Header{} 61 | broadcastingConnection, _, err = websocket.NewClient(conn, &u, h, 1024, 1024) 62 | if err != nil { 63 | fmt.Println("websocket连接失败:", err) 64 | conn.Close() 65 | continue 66 | } 67 | fmt.Println("创建成功!") 68 | return 69 | } 70 | os.Exit(0) 71 | } 72 | -------------------------------------------------------------------------------- /chans.go: -------------------------------------------------------------------------------- 1 | package roomManager 2 | 3 | import ( 4 | "encoding/json" 5 | "sync" 6 | 7 | "github.com/gorilla/websocket" 8 | ) 9 | 10 | var messageChannel map[string]chan nodeMessage 11 | var messageChannelLock sync.RWMutex 12 | var messageRoomCreated map[string]bool 13 | 14 | func init() { 15 | if useBroadcasting { 16 | messageRoomCreated = make(map[string]bool) 17 | } else { 18 | messageChannel = make(map[string]chan nodeMessage) 19 | } 20 | } 21 | 22 | //给channel发送消息 23 | func sendMessageToChannel(roomId string, nm nodeMessage) error { 24 | if nm.messageType == NODE_MESSAGE_TYPE_SEND_MESSAGE { 25 | n, err := json.Marshal(nm.body) 26 | if err != nil { 27 | return nil 28 | } 29 | if CheckMessage(string(n)) { 30 | return nil 31 | } 32 | } 33 | messageChannelLock.RLock() 34 | //如果房间不存在,创建一个房间 35 | if c, ok := messageChannel[roomId]; ok { 36 | c <- nm 37 | messageChannelLock.RUnlock() 38 | } else { 39 | messageChannelLock.RUnlock() 40 | messageChannelLock.Lock() 41 | //创建房间通道 42 | messageChannel[roomId] = make(chan nodeMessage, 1024) 43 | messageChannel[roomId] <- nm 44 | messageChannelLock.Unlock() 45 | //创建房间实例 46 | roomObj := &RoomInfo{} 47 | roomObj.RoomID = roomId 48 | roomObj.Rows = make([]*RowList, 0, 4) 49 | roomObj.Lock = &sync.Mutex{} 50 | 51 | //创建新的协程来监控房间 52 | go daemonReciver(messageChannel[roomId], roomObj) 53 | go timerForClean(messageChannel[roomId]) 54 | //如果是大厅的话,启动大厅清理协程 55 | if roomId == "" { 56 | go CleanHall(roomObj) 57 | } 58 | } 59 | return nil 60 | } 61 | 62 | //给广播站发送消息 63 | func sendMessageToBroadcast(roomId string, nm nodeMessage) error { 64 | n, err := json.Marshal(nm.body) 65 | if err != nil { 66 | return nil 67 | } 68 | if nm.messageType == NODE_MESSAGE_TYPE_SEND_MESSAGE { 69 | if CheckMessage(string(n)) { 70 | return WordsBannedError 71 | } 72 | } 73 | w, err := broadcastingConnection.NextWriter(websocket.TextMessage) 74 | if err != nil { 75 | return err 76 | } 77 | w.Write(n) 78 | w.Close() 79 | /* 80 | messageChannelLock.RLock() 81 | //如果房间不存在,创建一个房间 82 | if _, ok := messageRoomCreated[roomId]; ok { 83 | w, err := broadcastingConnection.NextWriter(websocket.TextMessage) 84 | if err != nil { 85 | messageChannelLock.RUnlock() 86 | return err 87 | } 88 | w.Write(n) 89 | w.Close() 90 | messageChannelLock.RUnlock() 91 | } else { 92 | messageChannelLock.RUnlock() 93 | messageChannelLock.Lock() 94 | //创建房间通道 95 | messageChannel[roomId] = make(chan nodeMessage, 1024) 96 | w, err := broadcastingConnection.NextWriter(websocket.TextMessage) 97 | if err != nil { 98 | messageChannelLock.Unlock() 99 | return err 100 | } 101 | w.Write(n) 102 | w.Close() 103 | messageChannelLock.Unlock() 104 | //创建房间实例 105 | roomObj := &RoomInfo{} 106 | roomObj.RoomID = roomId 107 | roomObj.Rows = make([]*RowList, 0, 4) 108 | roomObj.Lock = &sync.Mutex{} 109 | 110 | //创建新的协程来监控房间 111 | go daemonReciver(messageChannel[roomId], roomObj) 112 | go timerForClean(messageChannel[roomId]) 113 | //如果是大厅的话,启动大厅清理协程 114 | if roomId == "" { 115 | go CleanHall(roomObj) 116 | } 117 | } 118 | */ 119 | return nil 120 | } 121 | -------------------------------------------------------------------------------- /counter.go: -------------------------------------------------------------------------------- 1 | package roomManager 2 | 3 | var ( 4 | LinkedCounter uint64 //建立连接总数 5 | ClosedCounter uint64 //关闭连接总数 6 | SendCounter uint64 //发送总数 7 | ) 8 | -------------------------------------------------------------------------------- /daemon_change_room.go: -------------------------------------------------------------------------------- 1 | package roomManager 2 | 3 | //更换房间标记 4 | //因为前面已经修改了RoomID,所以这里只需要把接受者节点的指针重新对接到新房间的节点上,就可以了 5 | //新的房间更新逻辑: 6 | //1.扫描所有列,找到长度小于最大长度的,加入队尾 7 | //2.修改结点中索引信息 8 | func changeRoom(roomInfo *RoomInfo, node *ReciveNode) { 9 | roomInfo.Lock.Lock() 10 | defer roomInfo.Lock.Unlock() 11 | //如果房间没有列,则创建一个列 12 | if len(roomInfo.Rows) == 0 { 13 | row := &RowList{} 14 | row.Nodes = make([]*ReciveNode, 0, ROW_LENGTH) 15 | roomInfo.Rows = append(roomInfo.Rows, row) 16 | } 17 | //设定是否添加完成标记 18 | addSuccess := false 19 | //寻找一个未满的列,把新的节点加进去 20 | for _, v := range roomInfo.Rows { 21 | if len(v.Nodes) < ROW_LENGTH { 22 | v.Nodes = append(v.Nodes, node) 23 | addSuccess = true 24 | break 25 | } 26 | } 27 | //如果前面所有的列都是满的,则创建一个新列,把节点添加进去 28 | if !addSuccess { 29 | row := &RowList{} 30 | row.Nodes = make([]*ReciveNode, 0, ROW_LENGTH) 31 | roomInfo.Rows = append(roomInfo.Rows, row) 32 | row.Nodes = append(row.Nodes, node) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /daemon_clean.go: -------------------------------------------------------------------------------- 1 | package roomManager 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | //清理当前房间里面不属于自己房间的节点 9 | func cleanRoom(roomInfo *RoomInfo) { 10 | roomInfo.Lock.Lock() 11 | defer roomInfo.Lock.Unlock() 12 | 13 | //打印整理前的房间信息 14 | fmt.Println("========================整理前", time.Now().Format("2006-01-02 15:04:05"), "========================") 15 | 16 | startTime := time.Now() 17 | //创建一个空的列组,准备装整理过的节点 18 | colList := make([]*RowList, 0, 5) 19 | 20 | //创建行 21 | rowList := &RowList{} 22 | rowList.Nodes = make([]*ReciveNode, 0, ROW_LENGTH) 23 | 24 | //本次调整时间 25 | currentUpdateTime := time.Now() 26 | 27 | //列加入列组 28 | colList = append(colList, rowList) 29 | 30 | //列和行当前索引 31 | colIndex := 0 32 | 33 | //循环列表内的节点 34 | for _, row := range roomInfo.Rows { 35 | for _, node := range row.Nodes { 36 | if node.RoomID != roomInfo.RoomID || node.IsAlive == false || node.UpdateTime == currentUpdateTime { 37 | continue 38 | } 39 | node.UpdateTime = currentUpdateTime 40 | //添加节点到正常节点 41 | tmpIndex := len(rowList.Nodes) 42 | if tmpIndex >= ROW_LENGTH { 43 | tmpIndex = 0 44 | //创建行 45 | rowList = &RowList{} 46 | rowList.Nodes = make([]*ReciveNode, 0, ROW_LENGTH) 47 | colIndex++ 48 | 49 | //列加入列组 50 | colList = append(colList, rowList) 51 | colList[colIndex].Nodes = append(colList[colIndex].Nodes, node) 52 | } else { 53 | colList[colIndex].Nodes = append(colList[colIndex].Nodes, node) 54 | } 55 | } 56 | } 57 | //整理完毕之后,把新的整理结果赋予房间 58 | roomInfo.Rows = colList 59 | roomInfo.LastChangeTime = time.Now() 60 | endTime := time.Now() 61 | 62 | fmt.Println("行数:", len(roomInfo.Rows)) 63 | for _, row := range roomInfo.Rows { 64 | fmt.Println("总列:", len(row.Nodes), "|", cap(row.Nodes)) 65 | } 66 | fmt.Println("耗时:", endTime.Sub(startTime)) 67 | fmt.Println("========================整理完毕", time.Now().Format("2006-01-02 15:04:05"), "========================") 68 | } 69 | 70 | //定时对房间进行清理 71 | func timerForClean(c chan nodeMessage) { 72 | for { 73 | nm := nodeMessage{} 74 | nm.messageType = NODE_MESSAGE_TYPE_CLEAN_ROOM 75 | c <- nm 76 | time.Sleep(CLEAN_TIMER) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /daemon_close.go: -------------------------------------------------------------------------------- 1 | package roomManager 2 | 3 | import "fmt" 4 | 5 | //关闭当前房间 6 | func closeRoom(roomInfo *RoomInfo) { 7 | fmt.Println("Close room ",roomInfo.RoomID," Doing nothing.") 8 | } 9 | -------------------------------------------------------------------------------- /daemon_main_reciver.go: -------------------------------------------------------------------------------- 1 | package roomManager 2 | 3 | import "fmt" 4 | 5 | //守护进程,获取进程信息后工作 6 | func daemonReciver(c chan nodeMessage, roomInfo *RoomInfo) { 7 | fmt.Println("房间Reciver:", roomInfo.RoomID) 8 | for { 9 | s := <-c 10 | switch s.messageType { 11 | case NODE_MESSAGE_TYPE_IN_HALL: 12 | changeRoom(roomInfo, s.body.(*ReciveNode)) 13 | case NODE_MESSAGE_TYPE_CLOSE_ROOM: 14 | closeRoom(roomInfo) 15 | case NODE_MESSAGE_TYPE_CHANGE_ROOM: 16 | changeRoom(roomInfo, s.body.(*ReciveNode)) 17 | case NODE_MESSAGE_TYPE_SEND_MESSAGE: 18 | sendMessage(roomInfo, s.body) 19 | case NODE_MESSAGE_TYPE_CLEAN_ROOM: 20 | cleanRoom(roomInfo) 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /daemon_send_message.go: -------------------------------------------------------------------------------- 1 | package roomManager 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func sendMessage(roomInfo *RoomInfo, message interface{}) { 9 | counter := 0 10 | realCounter := 0 11 | currentTime := time.Now() 12 | 13 | for _, rows := range roomInfo.Rows { 14 | for _, node := range rows.Nodes { 15 | counter++ 16 | if node.RoomID == roomInfo.RoomID && node.IsAlive { 17 | realCounter++ 18 | node.SendMessage(message, currentTime) 19 | } 20 | } 21 | } 22 | 23 | endTime := time.Now() 24 | fmt.Println("[发送消息] - 房间", roomInfo.RoomID, ":遍历节点", counter, "个,发送节点", realCounter, "个,耗时:", endTime.Sub(currentTime).String()) 25 | } 26 | -------------------------------------------------------------------------------- /deamon_in_hall.go: -------------------------------------------------------------------------------- 1 | package roomManager 2 | 3 | func init() { 4 | //每分钟清理大厅 5 | } 6 | 7 | func CleanHall(roomObj *RoomInfo) { 8 | } 9 | -------------------------------------------------------------------------------- /error.go: -------------------------------------------------------------------------------- 1 | package roomManager 2 | 3 | import "errors" 4 | 5 | var ( 6 | ERROR_ROOM_NOT_EXISTS = errors.New("room is not exists") 7 | ) 8 | -------------------------------------------------------------------------------- /log.go: -------------------------------------------------------------------------------- 1 | package roomManager 2 | 3 | import redis "gopkg.in/redis.v5" 4 | import json "github.com/json-iterator/go" 5 | 6 | var redisConn *redis.Client 7 | 8 | func init() { 9 | opt := redis.Options{} 10 | opt.Addr = "localhost:6379" 11 | 12 | redisConn = redis.NewClient(&opt) 13 | } 14 | 15 | const ( 16 | LOG_TYPE_SERVER_RECIVE = iota //服务器端接收到消息数量 17 | LOG_TYPE_SERVER_INHALL //服务器端接收到连接数 18 | LOG_TYPE_SERVER_INROOM //服务器端接收到进入房间的数量(分房间) 19 | LOG_TYPE_SERVER_INROOM_LEFT_CONNS //服务器端房间每次介入链接之后,剩余的链接数量总和(不是length,而是遍历之后的节点数量) 20 | ) 21 | 22 | func Log(logType int, data interface{}) { 23 | switch logType { 24 | case LOG_TYPE_SERVER_RECIVE: 25 | rs, _ := json.Marshal(data) 26 | redisConn.SAdd("LOG_TYPE_SERVER_RECIVE", string(rs)) 27 | case LOG_TYPE_SERVER_INHALL: 28 | redisConn.Incr("LOG_TYPE_SERVER_INHALL") 29 | case LOG_TYPE_SERVER_INROOM: 30 | redisConn.Incr(data.(string)) 31 | case LOG_TYPE_SERVER_INROOM_LEFT_CONNS: 32 | rs, _ := json.Marshal(data) 33 | redisConn.SAdd("LOG_TYPE_SERVER_INROOM_LEFT_CONNS_"+data.(ReciveNode).RoomID, string(rs)) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /node_message.go: -------------------------------------------------------------------------------- 1 | package roomManager 2 | 3 | type nodeMessage struct { 4 | messageType int `json:"type"` //消息类型,类型为NODE_MESSAGE_TYPE组 5 | rommId string `json:"room_id"` 6 | body interface{} `json:"body"` //消息体 7 | } 8 | 9 | const ( 10 | _ = iota 11 | NODE_MESSAGE_TYPE_IN_HALL //添加节点入大厅 12 | NODE_MESSAGE_TYPE_CLOSE_ROOM //关闭节点服务 13 | NODE_MESSAGE_TYPE_CHANGE_ROOM //节点房间变更 14 | NODE_MESSAGE_TYPE_SEND_MESSAGE //节点发送消息 15 | NODE_MESSAGE_TYPE_CLEAN_ROOM //清理房间垃圾节点 16 | NODE_MESSAGE_TYPE_FILL_USERID //完善用户ID 17 | NODE_MESSAGE_TYPE_RELOAD_BANNED_IP //重载IP地址黑名单 18 | NODE_MESSAGE_TYPE_RELOAD_BANNED_USER //重载用户ID黑名单 19 | NODE_MESSAGE_TYPE_RELOAD_BANNED_WORDS //重载敏感词列表 20 | ) 21 | -------------------------------------------------------------------------------- /public_run.go: -------------------------------------------------------------------------------- 1 | package roomManager 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net/http" 7 | "strings" 8 | 9 | "github.com/gorilla/websocket" 10 | ) 11 | 12 | var upgrader = websocket.Upgrader{ 13 | ReadBufferSize: 1024, 14 | WriteBufferSize: 1024, 15 | CheckOrigin: func(r *http.Request) bool { return true }, 16 | } 17 | 18 | func Run() error { 19 | if processFunc == nil { 20 | fmt.Println("信息处理方法未注册......") 21 | return nil 22 | } 23 | go ProcessSignals() 24 | if useBroadcasting { 25 | go ConnBroadcasting() 26 | } 27 | http.HandleFunc("/"+REQUEST_URI, handler) 28 | return http.ListenAndServe(LISTEN_PORT, nil) 29 | } 30 | 31 | func handler(w http.ResponseWriter, r *http.Request) { 32 | conn, err := upgrader.Upgrade(w, r, nil) 33 | if err != nil { 34 | return 35 | } 36 | 37 | node := &ReciveNode{} 38 | 39 | //获取目标IP地址 40 | addrSlice := strings.Split(r.RemoteAddr, ":") 41 | node.IP = addrSlice[0] 42 | 43 | node.IsAlive = true 44 | node.Conn = conn 45 | node.Add() 46 | processMessage(node) 47 | } 48 | 49 | func processMessage(node *ReciveNode) { 50 | for { 51 | mType, reader, err := node.Conn.NextReader() 52 | 53 | if mType == -1 { 54 | node.Close() 55 | return 56 | } 57 | 58 | if err != nil { 59 | continue 60 | } 61 | 62 | //如果已经被关闭,则不允许发送消息 63 | if node.DisableRead { 64 | continue 65 | } 66 | 67 | //如果用户ID在黑名单内,则关闭发消息功能 68 | if CheckUserID(node.UserID) { 69 | node.DisableRead = true 70 | continue 71 | } 72 | 73 | switch mType { 74 | case websocket.BinaryMessage: 75 | continue 76 | case websocket.CloseMessage: 77 | node.Close() 78 | return 79 | case websocket.PingMessage: 80 | node.Conn.WriteMessage(websocket.PongMessage, []byte("")) 81 | continue 82 | case websocket.PongMessage: 83 | continue 84 | } 85 | //如果是文本内容的话 86 | //获取文本内容 87 | msg := make([]byte, 0, 1024) 88 | for { 89 | tmp := make([]byte, 1024) 90 | length, err := reader.Read(tmp) 91 | if err == io.EOF || length < 1024 { 92 | msg = append(msg, tmp[:length]...) 93 | break 94 | } 95 | msg = append(msg, tmp...) 96 | } 97 | //交给注册的方法处理 98 | processFunc(msg, node) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /public_settings.go: -------------------------------------------------------------------------------- 1 | package roomManager 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "time" 7 | ) 8 | 9 | var ( 10 | DETAILED_LOG_FLAG = false //详细日志开关 11 | NORMAL_LOG_FLAG = false //常规日志开关 12 | TRACE_FLAG = false //是否打开trace开关 13 | TRACE_LOG_PATH = os.ExpandEnv("$GOPATH/trace_logs/trace_log.out") //trace日志地址 14 | LISTEN_PORT = ":8080" //监听端口 15 | REQUEST_URI = "websocket" //请求URI 16 | CLEAN_TIMER = 5 * time.Minute //房间清理定时器 17 | HALL_TIMEOUT = 30 * time.Second //大厅房间的连接超过多久之后会被断开 18 | ROW_LENGTH = 1024 //单列节点最大长度 19 | IS_CHECK_USER_WHEN_SEND_MSG = true //发送弹幕时,是否检查用户信息(如果用户ID不存在,则不会发送消息,也不会接收到消息) 20 | ) 21 | 22 | //设定监听端口号 23 | func SetListenPort(port int) { 24 | LISTEN_PORT = fmt.Sprint(":", port) 25 | } 26 | 27 | //设定接收请求URI 28 | func SetRequestURI(uri string) { 29 | REQUEST_URI = uri 30 | } 31 | 32 | //设定定时清理房间垃圾的间隔时长,单位:分钟 33 | func SetCleanTimes(t int64) { 34 | CLEAN_TIMER = time.Duration(t) * time.Minute 35 | } 36 | 37 | //打开Trace日志开关 38 | func OpenTraceFlag() { 39 | TRACE_FLAG = true 40 | } 41 | 42 | //关闭Trace日志开关 43 | func CloseTraceFlag() { 44 | TRACE_FLAG = false 45 | } 46 | 47 | //打开常规日志开关 48 | func OpenNormalFlag() { 49 | NORMAL_LOG_FLAG = true 50 | } 51 | 52 | //关闭常规日志开关 53 | func CloseNormalFlag() { 54 | NORMAL_LOG_FLAG = false 55 | } 56 | 57 | //打开详细日志开关 58 | func OpenDetailFlag() { 59 | DETAILED_LOG_FLAG = true 60 | } 61 | 62 | //关闭详细日志开关 63 | func CloseDetailFlag() { 64 | DETAILED_LOG_FLAG = false 65 | } 66 | 67 | //设定弹幕发送时是否检查用户身份 68 | func SetCheckUserWhenSendMessage(setting bool) { 69 | IS_CHECK_USER_WHEN_SEND_MSG = setting 70 | } 71 | -------------------------------------------------------------------------------- /readme.go: -------------------------------------------------------------------------------- 1 | package roomManager 2 | 3 | /** 4 | 整个房间链接管理项目围绕着接收节点的管理来进行管理 5 | 6 | 服务器端接收到一个连接的时候,产生一个receveNode,加入到空串的房间中(大厅) 7 | 8 | 然后开始接收信息 9 | 10 | reciveNode能够提供的功能有: 11 | 12 | 1.加入大厅(隐藏房间,不可以对外调用) 13 | 把节点信息发送给RoomID为空串的房间(大厅) 14 | 2.更换房间 15 | 把节点信息和目标RoomID(非空串)发送给后端协程 16 | 3.发送消息 17 | 其实是发送给消息通道 18 | 4.关闭节点 19 | 只要把当前节点的IsAlive变成false就可以了 20 | 21 | 内部功能有: 22 | 23 | 1.检查和创建房间对应的消息通道 24 | 25 | 2.清理不属于当前房间的链接 26 | 27 | 3.关闭并清理已经变为关闭的节点 28 | 29 | 4. 30 | 31 | 32 | **/ 33 | -------------------------------------------------------------------------------- /recive_node.go: -------------------------------------------------------------------------------- 1 | /** 2 | 整个房间链接管理项目围绕着接收节点的管理来进行管理 3 | **/ 4 | package roomManager 5 | 6 | import ( 7 | "time" 8 | 9 | "github.com/gorilla/websocket" 10 | json "github.com/json-iterator/go" 11 | ) 12 | 13 | //用于接收用户消息的节点 14 | type ReciveNode struct { 15 | RoomID string //房间ID 16 | ClientID int64 //客户端ID 17 | IP string //当前IP地址 18 | UserID string //用户标识 19 | DisableRead bool //是否停止接收该链接内容 20 | Conn *websocket.Conn //websocket链接 21 | UpdateTime time.Time //最后一次整理时间 22 | LastSendTime time.Time //最后一次发送消息时间 23 | IsAlive bool //是否存活 24 | } 25 | 26 | func (this *ReciveNode) Add() { 27 | this.IsAlive = true 28 | this.RoomID = "" 29 | nm := nodeMessage{ 30 | messageType: NODE_MESSAGE_TYPE_IN_HALL, 31 | body: this, 32 | } 33 | sendMessageToChannel(this.RoomID, nm) 34 | if DETAILED_LOG_FLAG { 35 | LinkedCounter++ 36 | } 37 | } 38 | 39 | func (this *ReciveNode) ChangeRoom(roomId string) { 40 | if this.RoomID == roomId { 41 | return 42 | } 43 | this.RoomID = roomId 44 | nm := nodeMessage{ 45 | messageType: NODE_MESSAGE_TYPE_CHANGE_ROOM, 46 | body: this, 47 | } 48 | sendMessageToChannel(this.RoomID, nm) 49 | } 50 | 51 | //给当前节点所在房间发送消息 52 | func (this *ReciveNode) SendMessageToRoom(message interface{}) { 53 | nm := nodeMessage{ 54 | messageType: NODE_MESSAGE_TYPE_SEND_MESSAGE, 55 | body: message, 56 | } 57 | if IS_CHECK_USER_WHEN_SEND_MSG { 58 | if this.UserID == "" { 59 | return 60 | } 61 | } 62 | if this.DisableRead { 63 | return 64 | } 65 | if this.RoomID == "" { 66 | return 67 | } 68 | if useBroadcasting { 69 | sendMessageToBroadcast(this.RoomID, nm) 70 | } else { 71 | sendMessageToChannel(this.RoomID, nm) 72 | } 73 | if DETAILED_LOG_FLAG { 74 | SendCounter++ 75 | } 76 | } 77 | 78 | //给当前节点连接发送消息 79 | func (this *ReciveNode) SendMessage(message interface{}, sendTime time.Time) { 80 | if this.LastSendTime == sendTime { 81 | return 82 | } 83 | w, err := this.Conn.NextWriter(websocket.TextMessage) 84 | if err != nil { 85 | return 86 | } 87 | defer w.Close() 88 | msg, err := json.Marshal(message) 89 | if err != nil { 90 | return 91 | } 92 | w.Write(msg) 93 | this.LastSendTime = sendTime 94 | } 95 | 96 | //更改用户ID 97 | func (this *ReciveNode) ChangeUserID(userId string) { 98 | this.UserID = userId 99 | } 100 | 101 | func (this *ReciveNode) Close() { 102 | this.IsAlive = false 103 | this.Conn.Close() 104 | } 105 | 106 | func SendMessageFromOuter(roomID string, message interface{}) { 107 | if roomID == "" { 108 | return 109 | } 110 | nm := nodeMessage{ 111 | messageType: NODE_MESSAGE_TYPE_SEND_MESSAGE, 112 | body: message, 113 | } 114 | sendMessageToChannel(roomID, nm) 115 | if DETAILED_LOG_FLAG { 116 | SendCounter++ 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /register.go: -------------------------------------------------------------------------------- 1 | package roomManager 2 | 3 | var processFunc func([]byte, *ReciveNode) 4 | var proMessageFromBroadcast func([]byte) 5 | 6 | //注册消息处理方法 7 | func RegisterProcessFunc(pFunc func([]byte, *ReciveNode)) { 8 | processFunc = pFunc 9 | } 10 | 11 | //注册广播站地址 12 | func RegisterBroadcastStation(domain string, port int, uri string) { 13 | broadcastingHost = domain 14 | broadcastingPort = port 15 | broadcastingURI = uri 16 | useBroadcasting = true 17 | } 18 | 19 | //注册从广播站收到消息后处理的 20 | func RegisterProcessMessageFromBroadcast(pFunc func([]byte)) { 21 | proMessageFromBroadcast = pFunc 22 | } 23 | -------------------------------------------------------------------------------- /room.go: -------------------------------------------------------------------------------- 1 | package roomManager 2 | 3 | import ( 4 | "sync" 5 | "time" 6 | ) 7 | 8 | type RoomInfo struct { 9 | RoomID string //房间ID 10 | Lock *sync.Mutex //房间操作锁 11 | Rows []*RowList //房间多行Slice 12 | Length uint64 //当前房间总节点数 13 | LastChangeTime time.Time //最后一次更新时间 14 | } 15 | -------------------------------------------------------------------------------- /row_list.go: -------------------------------------------------------------------------------- 1 | package roomManager 2 | 3 | type RowList struct { 4 | Nodes []*ReciveNode //节点列表 5 | } 6 | -------------------------------------------------------------------------------- /signals.go: -------------------------------------------------------------------------------- 1 | package roomManager 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "os/signal" 7 | "syscall" 8 | ) 9 | 10 | func ProcessSignals() { 11 | if DETAILED_LOG_FLAG { 12 | sig := make(chan os.Signal, 1) 13 | signal.Notify(sig, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGHUP) 14 | 15 | _ = <-sig 16 | fmt.Println("[共建立连接]:", LinkedCounter) 17 | fmt.Println("[共断开连接]:", ClosedCounter) 18 | fmt.Println("[共发送消息]:", SendCounter) 19 | os.Exit(1) 20 | } 21 | } 22 | --------------------------------------------------------------------------------