本節, 咱們將一步一步的把上一節完成的echo服務器/客戶端改形成一個文本信息的聊天室git
服務端的改動服務器
服務器爲了實現聊天信息的羣體廣播, 須要記錄全部鏈接到服務器的客戶端信息, 因此, 咱們須要添加一個集合來保存全部客戶端的鏈接:併發
var ConnMap map[string]*net.TCPConntcp
接着, 每次當有新的客戶端鏈接到服務器時, 須要把這個客戶端鏈接行信息加入集合:ui
ConnMap[tcpConn.RemoteAddr().String()] = tcpConn.net
當服務器收到客戶端的聊天信息時, 須要廣播到全部客戶端, 因此咱們須要利用上面保存TCPConn的map來遍歷全部TCPConn進行廣播, 用如下方法實現:code
func boradcastMessage(message string) { b := []byte(message) for _, conn := range ConnMap { conn.Write(b) } }
客戶端代碼改動server
客戶端代碼改動相對簡單, 只是加入了用戶本身輸入聊天信息的功能, 在鏈接成功而且 啓動了消息接收的gorountine後, 加入如下代碼:ip
for { var msg string fmt.Scanln(&msg) if msg == "quit" { break } b := []byte(msg + "\n") conn.Write(b) }
完整的服務端代碼以下:ci
server.go
package main import ( "bufio" "fmt" "net" ) // 用來記錄全部的客戶端鏈接 var ConnMap map[string]*net.TCPConn func main() { var tcpAddr *net.TCPAddr ConnMap = make(map[string]*net.TCPConn) tcpAddr, _ = net.ResolveTCPAddr("tcp", "127.0.0.1:9999") tcpListener, _ := net.ListenTCP("tcp", tcpAddr) defer tcpListener.Close() for { tcpConn, err := tcpListener.AcceptTCP() if err != nil { continue } fmt.Println("A client connected : " + tcpConn.RemoteAddr().String()) // 新鏈接加入map ConnMap[tcpConn.RemoteAddr().String()] = tcpConn go tcpPipe(tcpConn) } } func tcpPipe(conn *net.TCPConn) { ipStr := conn.RemoteAddr().String() defer func() { fmt.Println("disconnected :" + ipStr) conn.Close() }() reader := bufio.NewReader(conn) for { message, err := reader.ReadString('\n') if err != nil { return } fmt.Println(conn.RemoteAddr().String() + ":" + string(message)) // 這裏返回消息改成了廣播 boradcastMessage(conn.RemoteAddr().String() + ":" + string(message)) } } func boradcastMessage(message string) { b := []byte(message) // 遍歷全部客戶端併發送消息 for _, conn := range ConnMap { conn.Write(b) } }
客戶端完整代碼以下:
client.go
package main import ( "bufio" "fmt" "net" ) func main() { var tcpAddr *net.TCPAddr tcpAddr, _ = net.ResolveTCPAddr("tcp", "127.0.0.1:9999") conn, _ := net.DialTCP("tcp", nil, tcpAddr) defer conn.Close() fmt.Println("connected!") go onMessageRecived(conn) // 控制檯聊天功能加入 for { var msg string fmt.Scanln(&msg) if msg == "quit" { break } b := []byte(msg + "\n") conn.Write(b) } } func onMessageRecived(conn *net.TCPConn) { reader := bufio.NewReader(conn) for { msg, err := reader.ReadString('\n') fmt.Println(msg) if err != nil { quitSemaphore <- true break } } }
最後分別編譯server與client試試效果吧!
go build server.go
go build client.go
先啓動server端, 而後新開兩個個終端, 啓動客戶端, 在其中一個客戶端裏鍵入聊天信息後回車, 會發現另一個客戶端收到了剛剛發送的聊下天信息
完事大吉!
相關源碼: https://git.oschina.net/victoriest/go-simple-tcp-server.git