使用go语言编写一个简易版的聊天室,新建server.go,这个是服务端的代码,后续贴上客户端的代码
- 建立一个用户结构体用于存储进来的用户信息,里面包含用户id,ip地址,进入的时间,还有一个channel用来发送消息的
type User struct {
ID int
Addr string
EnterAt time.Time
MessageChannel chan string
}
- 然后初始化三个channel,分别是用户进入的时候,用户离开的时候,用户发送消息的时候的用来通信的
var (
enteringChannel = make(chan *User)
leavingChannel = make(chan *User)
messageChannel = make(chan string, 5)
)
- 然后建一个发送消息的方法
// 新开一个goroutine给用户发送消息
func sendMessage(conn net.Conn, ch <-chan string) {
for msg := range ch {
fmt.Fprintln(conn, msg)
}
}
- 新增一个用于广播用户消息给所有人的方法, 新用户进来,用户消息,用户离开
func broadcaster() {
// 使用结构体map来存储所有用户信息
users := make(map[*User]struct{})
for {
select {
case user := <-enteringChannel:
// 新用户进入保存到map里面
users[user] = struct{}{}
case user := <-leavingChannel:
// 用户退出后,删除掉这个用户
delete(users, user)
case msg := <-messageChannel:
// 给在线的用户发送消息
for user := range users {
user.MessageChannel <- msg
}
}
}
}
- 处理用户连接和发送消息方法
func handleConn(conn net.Conn) {
defer conn.Close()
// 新用户进来创建实例
user := &User{
ID: int(time.Now().Unix()),
Addr: conn.RemoteAddr().String(),
EnterAt: time.Now(),
MessageChannel: make(chan string, 8),
}
// 由于当前是在一个新的goroutine中进行的,所以需要开一个goroutine用于写操作
go sendMessage(conn, user.MessageChannel)
// 给当前用户发送欢迎消息
user.MessageChannel <- "welcome, " + user.Addr
messageChannel <- "user:`" + strconv.Itoa(user.ID) + "`has enter"
// 记录到全局用户列表中,避免用锁
enteringChannel <- user
//循环读取用户输入
input := bufio.NewScanner(conn)
for input.Scan() {
messageChannel <- strconv.Itoa(user.ID) + ":" + input.Text()
}
if err := input.Err(); err != nil {
log.Println("读取错误:", err)
}
leavingChannel <- user
messageChannel <- "user:`" + strconv.Itoa(user.ID) + "`has left"
}
- 主函数方法
func main() {
// 简历tcp连接
lintener, err := net.Listen("tcp", ":9090")
if err != nil {
panic(err)
}
// 调用广播来监听
go broadcaster()
for {
conn, err := lintener.Accept()
if err != nil {
log.Println(err)
continue
}
// 处理连接请求
go handleConn(conn)
}
}