go语言易于实现高并发,简易聊天室如下:
package main
import (
"fmt"
"net"
)
/*
主go程:创建监听socket,for循环accept获取到客户端的conn,开启goroutine HandlerConnect
HandlerConnect:创建用户结构体,存入onlineMap,发送用户登录广播
Manager:监听全局的channel message,将读到的消息广播给onlineMap中的所有用户
WriteMsgToClient:读取每个用户自带channel C上消息,回写给用户
*/
// 创建用户结构体类型
type Client struct {
C chan string
Name string
Addr string
}
// 创建全局map,将用户存储到这里
var onlineMap map[string]Client
// 创建全局channel穿递用户消息
var message = make(chan string)
func WriteMsgToClient(clnt Client, conn net.Conn) {
// 监听用户自带Channel上是否有消息
for msg := range clnt.C {
conn.Write([]byte(msg + "\n"))
}
}
func Manager() {
// 初始化map,onlineMap
onlineMap = make(map[string]Client)
for { // 循环从message中读取是否有数据
// 监听channel中是否有数据,有数据存储至message,无数据就阻塞
msg := <-message
// 循环发送消息给所有在线用户
for _, clnt := range onlineMap {
clnt.C <- msg
}
}
}
func MakeMsg(clnt Client, msg string) (buf string) {
buf = "[" + clnt.Addr + "]" + clnt.Name + ":" + msg
return
}
func HandlerConnect(conn net.Conn) {
defer conn.Close()
// 获取用户网络地址
netAddr := conn.RemoteAddr().String()
// 创建新连接用户的结构体
clnt := Client{make(chan string), netAddr, netAddr}
// 将新连接用户添加到在线用户map中,key:IP+port value:client
onlineMap[netAddr] = clnt
// 创建专门用来给当前用户发送消息的goroutine
go WriteMsgToClient(clnt, conn)
// 发送用户上线消息到全局通道中
//message <- "[" + netAddr + "]" + clnt.Name + "login"
message <- MakeMsg(clnt, "login")
// 创建一个匿名goroutine,专门处理用户发送的消息
go func() {
buf := make([]byte, 4096)
for {
n, err := conn.Read(buf)
if n == 0 {
fmt.Printf("检测到客户端%s退出\n", clnt.Name)
return
}
if err != nil {
fmt.Println("conn Read err", err)
return
}
// 将读到的用户消息保存到msg中,string类型
msg := string(buf[:n])
// 将读到的用户消息广播给所用在线用户(写入到message中)
message <- MakeMsg(clnt, msg)
}
}()
// 保证不退出
for {
}
}
func main() {
// 创建监听套接字
listener, err := net.Listen("tcp", "127.0.0.1:9001")
if err != nil {
fmt.Println("Listen err", err)
return
}
defer listener.Close()
// 创建管理者go程
go Manager()
// 循环监听客户端请求
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println("accept err", err)
return
}
go HandlerConnect(conn)
}
}