Golang 中国
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
// See page 254.

// Chat is a server that lets clients chat with each other.
package main
import (
"bufio"
"fmt"
"log"
"net"
)

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
)

//负责广播的goroutine,抱有疑问,这个函数用一个for死循环嵌套了一个select,通过select去选择性地匹配某一个case,但这些case中好像没有与clientWriter的goroutine通过channel之间进行交流呢,我不太理解,从handleConn中接受的messages是如何通过broadcaster与clientWriter广播写入连接conn中去的呢?
//entering与leaving在整个过程中又起到什么作用呢?如果去掉这两个通道又会如何?
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 {
                cli <- msg
            }
        case cli := <-entering:
            clients[cli] = true
        case cli := <-leaving:
            delete(clients, cli)
            close(cli)
        }
    }
}

//处理连接的goroutine,每一个客户端与服务器建立的连接都会新开一个goroutine,相互独立。在这个函数中,又开了一个goroutine,并发执行clientWriter,将handleConn的局部变量写入conn中。
//1.用A客户端建立连接,“You are ‘主机ip’”传入局部通道ch,此时执行clientWriter的goroutine将结束阻塞,将传入通道的“You are ‘主机ip’”写入连接conn,“You are ‘主机ip’”会返回至A客户端终端中显示。
//2.“‘主机ip’ has arrived”传入全局通道messages中,“‘主机ip’ has arrived”不会返回至A客户端终端中,如果此时有B客户端与服务器保持连接,那么“‘主机ip’ has arrived”会返回至B客户端,这是为什么?
//3.ch中的消息传给entering这个通道去,我不是很理解这一句话,ch是一个无缓冲通道,即容量为1,刚才注释第一点说了,clientWriter会将通道ch中的消息接收,这样ch中不就没有值了么,这样enter <- ch不就阻塞了么?
//4.通过扫描conn中的客户端A写入conn中的信息,将其取出,传入messages通道中,即“‘主机ip’:‘A客户端写入conn的信息’”;最后这个字符串“‘主机ip’:‘A客户端写入conn的信息’”会在A,B客户端的终端显示,这是为什么呢?执行broadcaster()的goroutine并没有将messages的内容发送至某处并将其写入conn的操作啊?
//5.若客户端A写入连接“control+z”,那么ch会发送至通道leaving,但ch里面不是没有值么,不是会阻塞么?"主机A has left"会发送至通道messages,最后B客户端终端会显示"主机A has left"。

func handleConn(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)
    for input.Scan() {
        messages <- who + ": " + input.Text()
    }
// NOTE: ignoring potential errors from input.Err()
    leaving <- ch
    messages <- who + " has left"
    conn.Close()
}

//负责写入conn的goroutine,将单向in通道ch的消息写入conn中(每条连接是相互独立的goroutine)
func clientWriter(conn net.Conn, ch <-chan string) {
    for msg := range ch {
        fmt.Fprintln(conn, msg) // NOTE: ignoring network errors
    }
}

//三个goroutine(broadcaster,handleConn和clientWriter)通过channel进行通信。
func main() {
    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(conn)
    }
}

Kwper 于 2018-03-07 00:36 修改
2 回复
Kwper
#1 Kwper • 2018-03-07 00:40

总结一下我的问题:
1.(Line 54)“‘主机ip’ has arrived”传入全局通道messages中,“‘主机ip’ has arrived”不会返回至A客户端终端中,如果此时有B客户端与服务器保持连接,那么“‘主机ip’ has arrived”会返回至B客户端,这是为什么?
2.(Line56-59)通过扫描conn中的客户端A写入conn中的信息,将其取出,传入messages通道中,即“‘主机ip’:‘A客户端写入conn的信息’”;最后这个字符串“‘主机ip’:‘A客户端写入conn的信息’”会在A,B客户端的终端显示,这是为什么呢?执行broadcaster()的goroutine并没有将messages的内容发送至某处并将其写入conn的操作啊?
3.(Line23-40,function broadcaster)负责广播的goroutine,抱有疑问,这个函数用一个for死循环嵌套了一个select,通过select去选择性地匹配某一个case,但这些case中好像没有与clientWriter的goroutine通过channel之间进行交流呢,我不太理解,从handleConn中接受的messages是如何通过broadcaster与clientWriter广播写入连接conn中去的呢?
4.(Line55)ch中的消息传给entering这个通道去,我不是很理解这一句话,ch是一个无缓冲通道,即容量为1,而clientWriter会将通道ch中的消息接收,这样ch中不就没有值了么,这样enter <- ch不就阻塞了么?
5.(Line61)若客户端A写入连接“control+z”,那么ch会发送至通道leaving,但ch里面不是没有值么,不是会阻塞么?”主机A has left”会发送至通道messages,最后B客户端终端会显示”主机A has left”。
6.entering与leaving这两个全局通道在整个过程中又起到什么作用呢?如果去掉这两个通道又会如何?
谢谢,希望有哪位能回答我的疑惑~感激不尽!!

bigbear
#2 bigbear • 2018-03-08 17:27

需要 登录 后方可回复, 如果你还没有账号你可以 注册 一个帐号。