一般聊天室的架构分为服务器端和客户端:服务器
服务器端:
接受来自于客户端的链接请求并创建链接;
全部客户端的链接会放进链接池中,用于广播消息;架构
客户端:
链接服务器;
向服务器发送消息;
接收服务器的广播消息;tcp
注意事项:
某一个客户端断开链接后须要从链接池中摘除,并再也不接收广播消息;
某一个客户端断开链接后不能影响服务器端或别的客户端的链接;ide
详细的代码以下,文档看注释就行了,再也不细说:测试
服务器:ui
server.gocode
package main import ( "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.goserver
package main import ( "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中输入字符并回车,能够看到别的终端都会收到消息;input