看了无闻老师的一节关于 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
小栗子仅为经验总结,学习交流而记