本节, 我们将一步一步的把上一节完成的echo服务器/客户端改造成一个文本信息的聊天室
服务端的改动
服务器为了实现聊天信息的群体广播, 需要记录所有连接到服务器的客户端信息, 所以, 我们需要添加一个集合来保存所有客户端的连接:
var ConnMap map[string]*net.TCPConn
接着, 每次当有新的客户端连接到服务器时, 需要把这个客户端连接行信息加入集合:
ConnMap[tcpConn.RemoteAddr().String()] = tcpConn
当服务器收到客户端的聊天信息时, 需要广播到所有客户端, 所以我们需要利用上面保存TCPConn的map来遍历所有TCPConn进行广播, 用以下方法实现:
func boradcastMessage(message string) {
b := []byte(message)
for _, conn := range ConnMap {
conn.Write(b)
}
}
客户端代码改动
客户端代码改动相对简单, 只是加入了用户自己输入聊天信息的功能, 在连接成功并且 启动了消息接收的gorountine后, 加入以下代码:
for {
var msg string
fmt.Scanln(&msg)
if msg == "quit" {
break
}
b := []byte(msg + "\n")
conn.Write(b)
}
完整的服务端代码如下:
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端, 然后新开两个个终端, 启动客户端, 在其中一个客户端里键入聊天信息后回车, 会发现另外一个客户端收到了刚刚发送的聊下天信息
完事大吉!