Reputation: 363
I have the following piece of code that implements a simple tcp chat server written in go. I have trouble understanding where in the code the connection is sent through the "partner channel". I see that when the first user connects, the select statement just waits until the next user joins. But when the second user joins, how does the code send information through the channel and know to select which case?
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)
}
Upvotes: 1
Views: 515
Reputation: 31751
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")
}
The match function is called once, every time a client connects. I believe that much is clear.
The first case of the select statement wants to sends the TCP connection down the partner channel. The second case wants to receive a TCP connection from the partner channel.
If multiple cases of a select statements are ready to proceed, the runtime picks one at random.
When match is called for the first time (let's call this M1), neither case can proceed because the channel is unbuffered; M1 blocks and waits. When match is called for the second time (M2), it is actually unpredictable what exactly happens next, but the effect is the same.
Let's assume that M2 tries to proceed with the first case, sending the second connection to partner. This will work, because M1 is ready to receive it. So M2 proceeds with the first case, and M1 proceeds with the second case and calls chat
.
Let's now go back and assume that M2 tries to proceed with the second case, receiving another connection from partner. This also works, because M1 is ready to send the first connection. So in this case M1 proceeds with the first case, and M2 proceeds with the second case and calls chat
.
So chat is called in both cases, but the order of the arguments is not deterministic. In this case it works either way, because the communication is bi-directional and we don't care who came first.
And then it just starts all over again.
Upvotes: 2