通常聊天室的架构分为服务器端和客户端:
服务器端:
接受来自于客户端的连接请求并建立连接;
所有客户端的连接会放进连接池中,用于广播消息;
客户端:
连接服务器;
向服务器发送消息;
接收服务器的广播消息;
注意事项:
某一个客户端断开连接后需要从连接池中摘除,并不再接收广播消息;
某一个客户端断开连接后不能影响服务器端或别的客户端的连接;
详细的代码如下,文档看注释就好了,不再细说:
服务器:
server.go
package mainimport ( "net" "log" "fmt")func main() { port := "9090" Start(port)}// 启动服务器func Start(port string) { host := ":" + port // 获取tcp地址 tcpAddr, err := net.ResolveTCPAddr("tcp4", host) if err != nil { log.Printf("resolve tcp addr failed: %v\n", err) return } // 监听 listener, err := net.ListenTCP("tcp", tcpAddr) if err != nil { log.Printf("listen tcp port failed: %v\n", err) return } // 建立连接池,用于广播消息 conns := make(map[string]net.Conn) // 消息通道 messageChan := make(chan string, 10) // 广播消息 go BroadMessages(&conns, messageChan) // 启动 for { fmt.Printf("listening port %s ...\n", port) conn, err := listener.AcceptTCP() if err != nil { log.Printf("Accept failed:%v\n", err) continue } // 把每个客户端连接扔进连接池 conns[conn.RemoteAddr().String()] = conn fmt.Println(conns) // 处理消息 go Handler(conn, &conns, messageChan) }}// 向所有连接上的乡亲们发广播func BroadMessages(conns *map[string]net.Conn, messages chan string) { for { // 不断从通道里读取消息 msg := <-messages fmt.Println(msg) // 向所有的乡亲们发消息 for key, conn := range *conns { fmt.Println("connection is connected from ", key) _, err := conn.Write([]byte(msg)) if err != nil { log.Printf("broad message to %s failed: %v\n", key, err) delete(*conns, key) } } }}// 处理客户端发到服务端的消息,将其扔到通道中func Handler(conn net.Conn, conns *map[string]net.Conn, messages chan string) { fmt.Println("connect from client ", conn.RemoteAddr().String()) buf := make([]byte, 1024) for { length, err := conn.Read(buf) if err != nil { log.Printf("read client message failed:%v\n", err) delete(*conns, conn.RemoteAddr().String()) conn.Close() break } // 把收到的消息写到通道中 recvStr := string(buf[0:length]) messages <- recvStr }}
客户端:
client.go
package mainimport ( "net" "log" "fmt" "os")func main() { Start(os.Args[1])}func Start(tcpAddrStr string) { tcpAddr, err := net.ResolveTCPAddr("tcp4", tcpAddrStr) if err != nil { log.Printf("Resolve tcp addr failed: %v\n", err) return } // 向服务器拨号 conn, err := net.DialTCP("tcp", nil, tcpAddr) if err != nil { log.Printf("Dial to server failed: %v\n", err) return } // 向服务器发消息 go SendMsg(conn) // 接收来自服务器端的广播消息 buf := make([]byte, 1024) for { length, err := conn.Read(buf) if err != nil { log.Printf("recv server msg failed: %v\n", err) conn.Close() os.Exit(0) break } fmt.Println(string(buf[0:length])) }}// 向服务器端发消息func SendMsg(conn net.Conn) { username := conn.LocalAddr().String() for { var input string // 接收输入消息,放到input变量中 fmt.Scanln(&input) if input == "/q" || input == "/quit" { fmt.Println("Byebye ...") conn.Close() os.Exit(0) } // 只处理有内容的消息 if len(input) > 0 { msg := username + " say:" + input _, err := conn.Write([]byte(msg)) if err != nil { conn.Close() break } } }}
测试方法:
编译server.go和client.go;
打开终端,启动server,默认会监听9090端口;
再打开多个终端,启动client,client启动命令:client 服务器IP:9090;
在client中输入字符并回车,可以看到别的终端都会收到消息;
点击查看更多内容
为 TA 点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦