golang 聊天 server/select 声明

golang chat server/select statement

我有下面一段代码实现了一个简单的用 go 编写的 tcp 聊天服务器。我无法理解代码中通过 "partner channel" 发送连接的位置。我看到当第一个用户连接时,select 语句只是等待下一个用户加入。但是当第二个用户加入时,代码如何通过通道发送信息并知道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)
}

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")
}

匹配函数被调用一次,每次客户端连接时。相信大家已经很清楚了。

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

如果 select 语句的多个案例已准备好继续,the runtime picks one at random

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

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

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

所以在这两种情况下都会调用 chat,但是参数的顺序是不确定的。在这种情况下,两种方式都有效,因为通信是 bi-directional,我们不关心谁先来。

然后又重新开始。