这一篇将继续实现go语言的多房间聊天室,第一篇实现了基本的功能,今天就继续完善,实现各种功能。
广播功能:就是游戏里的全服通告,
信息过滤:对言论信息处理,类似于不能发黄赌毒信息,
禁言功能:此处警告并禁言5分钟;
踢出群聊功能:三次不合法信息后,提醒以后并踢出群聊;
功能代码: 由于内容较多,分成两个文件来写了;
hub.go文件:
package mainimport (
"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
sendchan []byte
numbervint
forbiddenword bool
timelogint64
}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 mainimport (
"fmt"
"log"
"net/http"
)type message struct {
data []byte
roomid string
conn *connection
}type hub struct {
rooms map[string]map[*connection]bool
broadcastchanmessage
broadcastss chanmessage
warningschanmessage
registerchanmessage
unregisterchanmessage
kickoutroom chanmessage
warnmsgchanmessage}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)
}
}
结果: 【go语言多房间聊天室websocket(二)】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***这个库,我了解使用的还不太多, 只是用它实现了基本功能, 大家也可以深入研究一下,
文章图片
文章图片
文章图片
文章图片
文章图片
推荐阅读
- Go|Docker后端部署详解(Go+Nginx)
- GO|GO,GO,GO!
- Go成长之路|go中判断空字符串、nil和len(t)的用法
- go编译tools
- go grpc安装与使用
- goroutine 调度原理
- Go|Go进阶之路——复杂类型
- Go进阶之路——变量
- Go进阶之路——流程控制语句