Go 语言圣经 8.10 示例: 聊天服务

8.10 示例: 聊天服务

代码

func test_ex_chat()  {
    listener, err := net.Listen("tcp", "localhost:8000")
    if err != nil {
        log.Fatal(err)
    }
    go broadcaster()
    for {
        conn, err := listener.Accept()
        if err != nil {
            log.Print(err)
            continue
        }
        go handleConn_ex813(conn)
    }
}
type client chan<- string // an outgoing message channel

var (
    entering = make(chan client)
    leaving  = make(chan client)
    messages = make(chan string) // all incoming client messages
    //练习 8.12: 使broadcaster能够将arrival事件通知当前所有的客户端。
    // 为了达成这个目的,你需要有一个客户端的集合,
    // 并且在entering和leaving的channel中记录客户端的名字。
    clientMap = make(map[string]string)
    oldKey = int64(10001)
)
func broadcaster() {
    clients := make(map[client]bool) // all connected clients
    for {
        select {
        case msg := <-messages:
            // Broadcast incoming message to all
            // clients outgoing message channels.
        for cli := range clients {
            //练习 8.15: 如果一个客户端没有及时地读取数据可能会导致所有的客户端被阻塞。
            // 修改broadcaster来跳过一条消息,而不是等待这个客户端一直到其准备好写
            if !clients[cli] {
                continue
            }
            cli <- msg
        }
        case cli := <-entering:
            clients[cli] = true

        case cli := <-leaving:
            delete(clients, cli)
            close(cli)
        }
    }
}
func handleConn_ex(conn net.Conn) {
    ch := make(chan string) // outgoing client messages
    go clientWriter(conn, ch)

    who := conn.RemoteAddr().String()
    //练习 8.14:
    // 修改聊天服务器的网络协议这样每一个客户端就可以在entering时可以提供它们的名字。
    // 将消息前缀由之前的网络地址改为这个名字。
    if len(clientMap[who]) <= 0 {
        str := "num" + fmt.Sprint(oldKey)
        clientMap[who] =  str
        oldKey++
    }
    ch <- "You are " + clientMap[who]
    messages <- clientMap[who] + " has arrived"
    entering <- ch

    input := bufio.NewScanner(conn)
    for input.Scan() {
        messages <- clientMap[who] + ": " + input.Text()
    }
    // NOTE: ignoring potential errors from input.Err()

    leaving <- ch
    messages <- clientMap[who] + " has left"
    conn.Close()
}
//练习 8.13: 使聊天服务器能够断开空闲的客户端连接,
// 比如最近五分钟之后没有发送任何消息的那些客户端。
// 提示:可以在其它goroutine中调用conn.Close()来解除Read调用,
// 就像input.Scanner()所做的那样
func handleConn_ex813(conn net.Conn) {
    ch := make(chan string) // outgoing client messages
    go clientWriter(conn, ch)

    who := conn.RemoteAddr().String()
    ch <- "You are " + who
    messages <- who + " has arrived"
    entering <- ch

    input := bufio.NewScanner(conn)

    abort := make(chan string)

    go func() {
        for  {
            select {
            case <-time.After(5 * time.Minute):
                conn.Close()
            case  str := <-abort:
                messages <- str
            }
        }
    }()

    for input.Scan() {
        str := input.Text()
        if str == "exit" {
            break
        }
        if len(str) > 0 {
            abort <- who + ": " + input.Text()
        }
    }
    // NOTE: ignoring potential errors from input.Err()

    leaving <- ch
    messages <- who + " has left"
    conn.Close()
}

func clientWriter(conn net.Conn, ch <-chan string) {
    for msg := range ch {
        fmt.Fprintln(conn, msg) // NOTE: ignoring network errors
    }
}
——不足之处,欢迎补充——
备注

《Go 语言圣经》

  • 学习记录所使用的GO版本是1.8
  • 学习记录所使用的编译器工具为GoLand
  • 学习记录所使用的系统环境为Mac os
  • 学习者有一定的C语言基础

代码仓库

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值