看了無聞老師的一節關於 goroutine 與 channel 的講解課堂,感受不是很明白,因此決定來實現一個聊天室的功能服務器
爲何是羣聊呢?tcp
由於羣聊相對邏輯簡單些函數
注:本栗子只用到了 goroutine 並無用到 channel學習
聊天室分爲兩個部分,分別是:spa
而後,通常狀況下咱們互相聊天使用的都只是客戶端而已,服務端只是起到調度的做用線程
假設咱們有 服務端(S)
客戶端(C1)
客戶端(C2)
客戶端(C3)
code
而且 S 已經 與 C1 C2 C3 創建了鏈接blog
理論上的流程是這樣的:圖片
1) 代碼ip
package main import ( "time" "fmt" "net" ) // 客戶端 map var client_map = make(map[string]*net.TCPConn) // 監聽請求 func listen_client(ip_port string) { tcpAddr, _ := net.ResolveTCPAddr("tcp", ip_port) tcpListener, _ := net.ListenTCP("tcp", tcpAddr) for {// 不停地接收 client_con, _ := tcpListener.AcceptTCP()// 監聽請求鏈接 client_map[client_con.RemoteAddr().String()] = client_con// 將鏈接添加到 map go add_receiver(client_con) fmt.Println("用戶 : ", client_con.RemoteAddr().String(), " 已鏈接.") } } // 向鏈接添加接收器 func add_receiver(current_connect *net.TCPConn) { for { byte_msg := make([]byte, 2048) len, err := current_connect.Read(byte_msg) if err != nil { current_connect.Close() } fmt.Println(string(byte_msg[:len])) msg_broadcast(byte_msg[:len], current_connect.RemoteAddr().String()) } } // 廣播給全部 client func msg_broadcast(byte_msg []byte, key string) { for k, con := range client_map { if k != key { con.Write(byte_msg) } } } // 主函數 func main() { fmt.Println("服務已啓動...") time.Sleep(1 * time.Second) fmt.Println("等待客戶端請求鏈接...") go listen_client("127.0.0.1:1801") select{} }
b) 描述
能夠看到,撇開 main 函數,一共有 2 個 routine,分別是:
a) 代碼
package main import ( "fmt" "net" "os" "bufio" ) // 用戶名 var login_name string // 本機鏈接 var self_connect *net.TCPConn // 讀取行文本 var reader = bufio.NewReader(os.Stdin) // 創建鏈接 func connect(addr string) { tcp_addr, _ := net.ResolveTCPAddr("tcp", addr) // 使用tcp con, err := net.DialTCP("tcp", nil, tcp_addr) // 撥號 self_connect = con if err != nil { fmt.Println("服務器鏈接失敗") os.Exit(1) } go msg_sender() go msg_receiver() } // 消息接收器 func msg_receiver() { buff := make([]byte, 2048) for { len, _ := self_connect.Read(buff) // 讀取消息 fmt.Println(string(buff[:len])) } } // 消息發送器 func msg_sender() { for { read_line_msg, _, _ := reader.ReadLine() read_line_msg = []byte(login_name + " : " + string(read_line_msg)) self_connect.Write(read_line_msg) } } // 主函數 func main() { fmt.Println("請問您怎麼稱呼?") name, _, _ := reader.ReadLine() login_name = string(name) connect("127.0.0.1:1801") select{} }
b) 描述
一樣,客戶端也是有兩個 routine 組成:
創建鏈接在主線程完成
a) 服務端
b) 客戶端_1
c) 客戶端_2
這裏並無用到 channel
小栗子僅爲經驗總結,學習交流而記