前段时间用go写了一个简单的聊天室,后来考虑到任何人都可以随意加入聊天室这个问题,我就加上了一个服务端控制客户接入的功能,调整了服务端的代码。
原文章:go socket 实现聊天室功能
相比原聊天室,调整了一下几个方面:
①增加了获取客户端验证信息,控制是否允许接入聊天室。
②增加服务器监听客户端聊天记录、广播信息。
③抽取了控制管道信息输出的handleMsg方法和控制客户端输入的handleClient方法。
package main
import (
"bufio"
"fmt"
"log"
"net"
"os"
"strings"
"time"
)
//创建结构体存储用户名称、信息和时间
type chatMsg struct {
name string
msg string
t string
}
var (
msg = make(chan string) //用于新用户加入时广播信息
chatMsgs = make(chan chatMsg) //发送聊天信息
exit = make(chan chatMsg) //离开信息
login = make(chan string)
clients = make(map[string]net.Conn) //定义一个map,使用用户名作为key,value该用户的链接
logins = make(map[string]net.Conn) //定义一个map,使用用户名作为key,value该用户的链接
)
func main() {
conn, err := net.Listen("tcp", "127.0.0.0:9090") //监听本机9090端口
if err != nil {
log.Fatal(err)
}
fmt.Println("大会准备就绪,正在等待各路侠客到访...")
go handleMsg() //开启一个线程,不断地从通道中读取数据
for { //循环读取客户端接入
accept, err := conn.Accept()
if err != nil {
log.Fatal(err)
}
go handleClient(accept)
}
}
func handleMsg() {
for {
select {
case chat := <-chatMsgs: //有用户发送消息
for name, accept := range clients {
msg := chat.t + " " + chat.name + " 说:" + chat.msg
if name == chat.name { //当发送者是本人
fmt.Fprintln(accept, chat.t+" 你说:"+chat.msg)
} else {
fmt.Fprintln(accept, msg)
}
fmt.Println(msg) //客户端输出用户之间的对话
}
case exitMsg := <-exit: //有用户离开了
for name, accept := range clients {
if name == exitMsg.name {
accept.Write([]byte("exit"))
} else {
fmt.Fprintln(accept, "<"+exitMsg.name+exitMsg.msg+">")
}
}
delete(clients, exitMsg.name)
case m := <-msg: //上线
for _, accept := range clients {
fmt.Fprintln(accept, m)
}
}
}
}
func handleClient(conn net.Conn) {
//是否允许接入
acceptEnter := false
client := conn.RemoteAddr().String() //客户端地址
name := "无名小辈" + client[strings.LastIndex(client, ":")+1:len([]rune(client))] //使用端口号作为名称后缀
fmt.Fprintln(conn, "正在申请加入群嘤荟,请阁下出示请帖:")
go func() {
for {
loginMsg := <-login
fmt.Println(name+"到访,拜帖信息<"+loginMsg+">", "☞输入“编号+请”允许加入", "☜输入“编号+滚”将此人赶出会场")
loginInput := bufio.NewScanner(os.Stdin)
acceptEnter = true
logins[name] = conn //将用户存入map
for loginInput.Scan() { //循环读取客户端控制台输入内容
text := loginInput.Text()
if strings.HasSuffix(text, "请") {
nameArr := strings.Split(text, "请")
if len(nameArr) == 2 {
key := "无名小辈" + nameArr[0]
m := "<" + key + "已加入群嘤荟" + ">"
clients[key] = logins[key]
msg <- m
fmt.Println(m)
}
} else if strings.HasSuffix(text, "滚") {
nameArr := strings.Split(text, "滚")
if len(nameArr) == 2 {
key := "无名小辈" + nameArr[0]
clients[key].Write([]byte("exit"))
delete(logins, key)
delete(clients, key)
}
} else {
fmt.Fprintln(conn, "【告示】"+text)
}
}
}
}()
go func() {//开启新的协程,判断客户端是否关闭了
for {
time.Sleep(time.Second)
buf := make([]byte, 1024)
_, err := conn.Read(buf)
if err != nil {
fmt.Println(name, "已悄悄离场...")
delete(clients, name)
break
}
}
}()
input := bufio.NewScanner(conn)
for input.Scan() { //循环读取客户端控制台输入内容
text := input.Text()
if !acceptEnter { // 不允许接入,持续等待
login <- text
continue
}
if text == "exit" { //客户端输入"exit"消息时,退出聊天室
exit <- chatMsg{name, "退出会场,青山不改绿水长流,各位有缘再见!", ""}
break //跳出循环读取
} else {
t := time.Now() //获取当前时间
chat := chatMsg{name, input.Text(), t.Format("02 Jan 2006 15:04")} //格式化时间
chatMsgs <- chat
}
}
}
效果图:
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)