【问题标题】:golang chat server/select statementgolang 聊天服务器/select 语句
【发布时间】:2018-04-17 09:22:01
【问题描述】:

我有以下一段代码,它实现了一个用 go 编写的简单 tcp 聊天服务器。我无法理解代码中的连接是通过“合作伙伴通道”发送的。我看到当第一个用户连接时,select 语句只是等到下一个用户加入。但是当第二个用户加入时,代码如何通过通道发送信息并知道选择哪种情况?

package main

import (
	"io"
  "net"
  "log"
  "fmt"
)
const listnAddr = "localhost:4000"

func main(){
  l , err := net.Listen("tcp",listnAddr)
  if err != nil {
    log.Fatal(err)
  }
  for {
    c , err := l.Accept()
    if c!= nil{
      fmt.Printf("ok")
    }
    if err != nil {
      log.Fatal(err)
    }
    go match(c)
  }
}
var partner = make(chan io.ReadWriteCloser)

func match(c io.ReadWriteCloser){
  fmt.Fprint(c,"waiting for a partner...")
  select{
    case partner <- c:
      //now handled by the other goroutine
    case p := <-partner:
      chat(p,c)
  }
  fmt.Printf("waiting")

}

func chat(a, b io.ReadWriteCloser) {
    fmt.Fprintln(a, "Found one! Say hi.")
    fmt.Fprintln(b, "Found one! Say hi.")
    go io.Copy(a, b)
    io.Copy(b, a)
}

【问题讨论】:

    标签: go channel goroutine


    【解决方案1】:
    var partner = make(chan io.ReadWriteCloser)
    
    func match(c io.ReadWriteCloser) {
        fmt.Fprint(c, "waiting for a partner...")
        select {
        case partner <- c:
            //now handled by the other goroutine
        case p := <-partner:
            chat(p, c)
        }
        fmt.Printf("waiting")
    }
    

    每次客户端连接时,都会调用一次 match 函数。我相信这很清楚。

    select 语句的第一种情况想要将 TCP 连接发送到伙伴通道。第二种情况希望从合作伙伴通道接收 TCP 连接。

    如果选择语句的多个情况准备好继续,the runtime picks one at random

    当第一次调用 match 时(我们称之为 M1),这两种情况都不能继续,因为通道是无缓冲的; M1 阻塞并等待。当第二次调用match(M2)时,实际上无法预测接下来究竟会发生什么,但效果是一样的。

    假设 M2 尝试继续第一种情况,将第二个连接发送给合作伙伴。这将起作用,因为 M1 已准备好接收它。所以M2继续第一种情况,M1继续第二种情况并调用chat

    现在让我们回过头来假设 M2 尝试继续第二种情况,接收来自伙伴的另一个连接。这也有效,因为 M1 已准备好发送第一个连接。所以在这种情况下,M1 继续第一种情况,M2 继续第二种情况并调用chat

    所以在这两种情况下都会调用 chat,但是参数的顺序是不确定的。在这种情况下,无论哪种方式都可以,因为通信是双向的,我们不在乎谁先来。

    然后一切重新开始。

    【讨论】:

    • chat 在这两种情况下都不会被调用,只有第二个连接会触发 chat fn。一旦调用了chat fn,服务器就会在两个连接中写入motd。该代码依赖于一个连接是所有三个的事实:可写/可读和可关闭以发送/接收并终止通信。恕我直言,我真的不明白为什么代码可能是不确定的,即使选择的顺序不是确定的,代码将以特定的方式运行。
    • 对于每个连接对,代码将以两种特定方式之一运行,并且在这两种情况下都会调用 chat。我想从技术上讲它是确定性的,因为选择是 pseudo-随机的,但是出于所有意图和目的,如果第一个或第二个 goroutine 调用 chat 是不可预测的,因此参数顺序是不可预测的好吧。
    • 我改写我最初的帖子,两个都会阻止合作伙伴推送,一个会赢,另一个会从合作伙伴那里拉然后开始聊天。确实,它可能是在合作伙伴推动的比赛中赢得比赛的一个或另一个,但是从逻辑上讲,该代码中还会发生其他任何事情吗? (感谢评论)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-25
    • 1970-01-01
    • 2018-06-05
    • 2015-11-05
    • 2011-12-01
    相关资源
    最近更新 更多