服务端代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| package main
import ( "encoding/json" "fmt" "net" "sync" )
var ( Clients = make(map[string]net.Conn) ClientMu = &sync.Mutex{} MessageCh = make(chan []byte) )
func HandlerConn(conn net.Conn) { ClientMu.Lock() nickname := conn.RemoteAddr().String() Clients[nickname] = conn ClientMu.Unlock() defer func() { ClientMu.Lock() fmt.Println("删除", nickname) delete(Clients, nickname) ClientMu.Unlock() }() systemMessage := MessageInfo{ From: "系统", Text: fmt.Sprintf("欢迎%s加入聊天室", conn.RemoteAddr().String()), } message, _ := json.Marshal(systemMessage) MessageCh <- message ReadMessage(conn, MessageCh) }
func HandlerMessage() { for { message := <-MessageCh messageInfo := MessageToStruct(message) for addr, otherConn := range Clients { if addr == messageInfo.From { continue } SendMessage(otherConn, message) } } } func main() {
tcp, err := net.Listen("tcp", ":8080") if err != nil { fmt.Println(err) } fmt.Println("服务器启动,监听 8080 端口") go HandlerMessage() for { conn, err := tcp.Accept() if err != nil { fmt.Println("连接失败:", err) continue } fmt.Println("连接成功:", conn.RemoteAddr()) go HandlerConn(conn) } }
|
客户端代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| package main
import ( "bufio" "encoding/json" "fmt" "net" "os" "strings" "time" )
func InputAndSend(conn net.Conn) { var text string for { fmt.Print("发送信息: ") reader := bufio.NewReader(os.Stdin) text, _ = reader.ReadString('\n') text = strings.TrimSpace(text) messageInfo := MessageInfo{ From: conn.LocalAddr().String(), Text: text, } message, _ := json.Marshal(messageInfo) SendMessage(conn, message) } }
func main() { var MessageCh = make(chan []byte) conn, err := net.Dial("tcp", "127.0.0.1:8080") if err != nil { fmt.Println("连接服务器失败:", err) return } fmt.Println("连接服务器成功:", conn.LocalAddr()) go ReadMessage(conn, MessageCh)
go InputAndSend(conn) for { message := <-MessageCh time.Sleep(100 * time.Millisecond) fmt.Print("\r\033[K") messageInfo := MessageToStruct(message) fmt.Printf("[%s]: %s\n", messageInfo.From, messageInfo.Text) fmt.Print("发送信息: ") } }
|
公共代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| package main
import ( "encoding/json" "fmt" "net" )
type MessageInfo struct { From string `json:"from"` Text string `json:"text"` }
func ReadMessage(conn net.Conn, ch chan []byte) { defer conn.Close() for { buf := make([]byte, 1024) n, err := conn.Read(buf) if err != nil { fmt.Print("\r\033[K") fmt.Println("断开连接:", conn.RemoteAddr()) systemMessage := MessageInfo{ From: "系统", Text: fmt.Sprintf("%s 退出了聊天室", conn.RemoteAddr().String()), } message, _ := json.Marshal(systemMessage) ch <- message return }
message := buf[:n] ch <- message } }
func SendMessage(conn net.Conn, message []byte) { _, err := conn.Write(message) if err != nil { fmt.Println("发送消息失败:", err) return } }
func MessageToStruct(message []byte) MessageInfo { var messageInfo MessageInfo err := json.Unmarshal(message, &messageInfo) if err != nil { fmt.Println("解码失败", string(message)) } return messageInfo }
|
启动代码
1 2
| go run server.go utils.go # 服务端 go run client.go utils.go # 客户端
|
核心逻辑
服务端收到连接后异步调用HandlerConn,把连接对象存储到Clients中,并广播欢迎信息到其他客户端,使用From排除消息来源者。
HandlerConn中同步调用ReadMessage收消息,ReadMessage返回后关闭连接, 在Clients中删除连接对象。
客户端异步调用InputAndSend,监听输入并发送,同步监听MessageCh,打印收到的消息。
项目使用MessageCh通道来处理消息,能输入消息的同时打印新消息。有个bug:消息打一半来消息就会清除掉正在输入的消息。