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,我们不关心谁先来。
然后又重新开始。
我有下面一段代码实现了一个简单的用 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,我们不关心谁先来。
然后又重新开始。