go语言多房间聊天室websocket(二)

这一篇将继续实现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***这个库,我了解使用的还不太多, 只是用它实现了基本功能, 大家也可以深入研究一下,
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值