这一篇将继续实现go语言的多房间聊天室,第一篇实现了基本的功能,今天就继续完善,实现各种功能。
广播功能:就是游戏里的全服通告,
信息过滤:对言论信息处理,类似于不能发黄赌毒信息,
禁言功能:此处警告并禁言5分钟;
踢出群聊功能:三次不合法信息后,提醒以后并踢出群聊;
功能代码:
由于内容较多,分成两个文件来写了;
hub.go文件:
package main
import (
"github.com/gorilla/websocket"
"log"
"net/http"
"strings"
"time"
)
const (
writeWait = 10 * time.Second
pongWait = 60 * time.Second
pingPeriod = (pongWait * 9) / 10
maxMessageSize = 512
)
var( upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool {
return true
},
}
)
type connection struct {
ws *websocket.Conn
send chan []byte
numberv int
forbiddenword bool
timelog int64
}
func (m message) readPump() {
c := m.conn
defer func() {
h.unregister <- m
c.ws.Close()
}()
c.ws.SetReadLimit(maxMessageSize)
c.ws.SetReadDeadline(time.Now().Add(pongWait))
c.ws.SetPongHandler(func(string) error { c.ws.SetReadDeadline(time.Now().Add(pongWait)); return nil })
for {
_, msg, err := c.ws.ReadMessage()
if err != nil {
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
log.Printf("error: %v", err)
}
//log.Printf("error: %v", err)
break
}
go m.Kickout(msg)
}
}
// 信息处理,不合法言论 禁言警告,超过3次,踢出群聊;
func (m message) Kickout(msg []byte) {
c := m.conn
// 判断是否有禁言时间,并超过5分钟禁言时间,没有超过进入禁言提醒
nowT:=int64(time.Now().Unix())
if nowT-c.timelog<300{
h.warnmsg <- m
}
// 不合法信息3次,判断是否有不合法信息,没有进行信息发布
if c.numberv < 3 {
// 信息过滤,设置含有此字符串内字符为不合法信息,
basestr := "死亡崩薨"
teststr := string(msg[:])
for _, ev := range teststr {
// 判断字符串中是否含有某个字符,true/false
reslut := strings.Contains(basestr, string(ev))
if reslut == true {
c.numberv += 1
c.forbiddenword = true // 禁言为真
// 记录禁言开始时间,禁言时间内任何信息不能发送
c.timelog = int64(time.Now().Unix())
h.warnings <- m
break
}
}
// 不禁言,消息合法 可以发送
if c.forbiddenword != true {
// 设置广播消息, 所有房间内都可以收到信息;给广播消息开头加一个特定字符串为标识,当然也有其他方法;
// 此例 设置以开头0为标识, 之后去掉0 ;
if msg[0] == 48 {
head := string("所有玩家请注意:")
data := head + string(msg[1:])
m := message{[]byte(data), m.roomid, c}
h.broadcastss <- m
} else if msg[0] != 48 { //不是0,就是普通消息
m := message{msg, m.roomid, c}
h.broadcast <- m
}
}
// 不合法信息超过三次,踢出群
} else {
h.kickoutroom <- m
log.Println("要被踢出群聊了...")
c.ws.Close() // 此处关闭了踢出的连接,也可以不关闭做其他操作,
}
}
func (c *connection) write(mt int, payload []byte) error {
c.ws.SetWriteDeadline(time.Now().Add(writeWait))
return c.ws.WriteMessage(mt, payload)
}
func (s *message) writePump() {
c := s.conn
ticker := time.NewTicker(pingPeriod)
defer func() {
ticker.Stop()
c.ws.Close()
}()
for {
select {
case message, ok := <-c.send:
if !ok {
c.write(websocket.CloseMessage, []byte{})
return
}
if err := c.write(websocket.TextMessage, message); err != nil {
return
}
case <-ticker.C:
if err := c.write(websocket.PingMessage, []byte{}); err != nil {
return
}
}
}
}
func serverWs(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
roomid := r.Form["roomid"][0]
ws, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println(err)
return
}
c := &connection{send: make(chan []byte, 256), ws: ws}
m := message{nil, roomid, c}
h.register <- m
go m.writePump()
go m.readPump()
}
server.go文件:
package main
import (
"fmt"
"log"
"net/http"
)
type message struct {
data []byte
roomid string
conn *connection
}
type hub struct {
rooms map[string]map[*connection]bool
broadcast chan message
broadcastss chan message
warnings chan message
register chan message
unregister chan message
kickoutroom chan message
warnmsg chan message
}
var h = hub{
broadcast: make(chan message),
broadcastss:make(chan message),
warnings: make(chan message),
warnmsg: make(chan message),
register: make(chan message),
unregister: make(chan message),
kickoutroom: make(chan message),
rooms: make(map[string]map[*connection]bool),
}
func (h *hub) run() {
for {
select {
case m := <-h.register: //传输链接
conns:=h.rooms[m.roomid]
if conns == nil { // 链接保存到相应的房间
conns = make(map[*connection]bool)
h.rooms[m.roomid] = conns
fmt.Println("在线人数:==",len(conns))
fmt.Println("rooms:==",h.rooms)
}
h.rooms[m.roomid][m.conn] = true
fmt.Println("在线人数:==",len(conns))
fmt.Println("rooms:==",h.rooms)
for con := range conns {
delmsg := "系统消息:欢迎新伙伴加入" + m.roomid + "聊天室!!!"
data := []byte(delmsg)
select {
case con.send <- data:
}
}
case m := <-h.unregister: //断开链接
conns:=h.rooms[m.roomid]
if conns != nil {
if _, ok := conns[m.conn]; ok {
delete(conns, m.conn) //删除链接
close(m.conn.send)
for con := range conns {
delmsg := "系统消息:有小伙伴离开了" + m.roomid + "聊天室!欢送!!!"
data := []byte(delmsg)
select {
case con.send <- data:
}
if len(conns) == 0 { // 链接都断开,删除房间
delete(h.rooms, m.roomid)
}
}
}
}
case m := <-h.kickoutroom: //3次不合法信息后,被踢出群聊
conns:=h.rooms[m.roomid]
notice:="由于您多次发送不合法信息,已被踢出群聊!!!"
select {
case m.conn.send <- []byte(notice):
}
if conns != nil {
if _, ok := conns[m.conn]; ok {
delete(conns, m.conn)
close(m.conn.send)
if len(conns) == 0 {
delete(h.rooms, m.roomid)
}
}
}
case m := <-h.warnings: //不合法信息警告
conns:=h.rooms[m.roomid]
if conns != nil {
if _, ok := conns[m.conn]; ok {
notice := "警告:您发布不合法信息,将禁言5分钟,三次后将被踢出群聊!!!"
//starttime:=
select {
case m.conn.send <- []byte(notice):
}
}
}
case m := <-h.warnmsg: //禁言中提示
conns:=h.rooms[m.roomid]
if conns != nil {
if _, ok := conns[m.conn]; ok {
notice := "您还在禁言中,暂时不能发送信息!!!"
select {
case m.conn.send <- []byte(notice):
}
}
}
case m := <-h.broadcast: //传输群信息/房间信息
conns := h.rooms[m.roomid]
for con := range conns {
if con==m.conn{ //自己发送的信息,不用再发给自己
continue
}
select {
case con.send <- m.data:
default:
close(con.send)
delete(conns, con)
if len(conns) == 0 {
delete(h.rooms, m.roomid)
}
}
}
case m := <-h.broadcastss: //传输全员广播信息
for _,conns := range h.rooms {
for con:=range conns{
if con==m.conn{ //自己发送的信息,不用再发给自己
continue
}
select {
case con.send <- m.data:
default:
close(con.send)
delete(conns, con)
if len(conns) == 0 {
delete(h.rooms, m.roomid)
}
}
}
}
}
}
}
// 要运行同包下的几个文件,都要执行
//go run server.go hub.go
func main() {
go h.run()
http.HandleFunc("/",serverWs )
err := http.ListenAndServe(":8899", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
结果:
ws://127.0.0.1:8899/?roomid=tang
ws://127.0.0.2:8899/?roomid=tang
ws://127.0.0.3:8899/?roomid=wang
ws://127.0.0.4:8899/?roomid=wang
ws://127.0.0.5:8899/?roomid=wang
记录的打印信息,把请求连接加入多房间聊天室rooms,此处创建了两个房间,
以下是发送信息的显示结果:
发现有一点没有处理好,就是发的消息有的情况不需要让发信息人看到(显示),
只需要看到提醒信息…有兴趣的小伙伴可以进一步改进,
对于 ***github.com/gorilla/websocket***这个库,我了解使用的还不太多, 只是用它实现了基本功能, 大家也可以深入研究一下,