GO:为什么从客户正在收听的地址拨号不起作用,但相反呢?

I wonder why dialing from the address on which the client is also listening does not work (Version A) but listening on the connection address the client is dialing to the server does actually work (Version B)?!

Can someone explain this to me. Go is new to me and I still learning a lot of things.

Here is an example:

Server Programm:

package main

import . "fmt"
import "net"
import "os"

func main() {
    Println("server")

    var listener, listenerError = net.Listen("tcp", "localhost:8080")

    if listenerError != nil {

        Println(listenerError)
        os.Exit(1)
    }

    for {
        con, _ := listener.Accept() // I don't care about the error in this example
        Printf("LocalAddr: %v
", con.LocalAddr())
        Printf("RemoteAddr: %v
", con.RemoteAddr())
    }
}

Client version A (not working):

package main

import "net"
import . "fmt"
import "os"

func main() {

    var listener, listenerError = net.Listen("tcp", "localhost:0")

    if listenerError != nil {

        Println(listenerError)
        os.Exit(1)
    }

    var dialer = new(net.Dialer)
    dialer.LocalAddr = listener.Addr()

    con, err := dialer.Dial("tcp", "localhost:8080")
    if err != nil {

        // dial tcp 127.0.0.1:60229->127.0.0.1:8080: bind: address already in use
        Println(err)
        os.Exit(2)
    }

    Printf("LocalAddr: %v
", con.LocalAddr())
    Printf("RemoteAddr: %v
", con.RemoteAddr())
}

Client version B (working):

package main

import "net"
import . "fmt"
import "os"

func main() {
    Println("client")

    con, err := net.Dial("tcp", "localhost:8080")
    if err != nil {

        Println(err)
        os.Exit(2)
    }

    // magic happens here
    var listener, listenerError = net.Listen("tcp", con.LocalAddr().String())

    if listenerError != nil {

        Println(listenerError)
        os.Exit(1)
    }

    Println("LISTENING")
    conn, _ := listener.Accept() // will accept on con.LocalAddr()
    Printf("LocalAddr: %v
", conn.LocalAddr())
    Printf("RemoteAddr: %v
", conn.RemoteAddr())
}

"Version B" works as a side effect of the Go's POSIX default of setting SO_REUSEADDR, which will allow binding to an addr:port pair even if it's in use by an existing connection. The 2 sockets can be differentiated, because the established connection is identified by the 4-tuple of (LocalAddr, LocalPort, RemoteAddr, RemotePort).

"Version A" doesn't work, because when setting up the connection it needs to call bind to set the requested local address, and there is already a listening socket bound to that port.

There's no need to try and exploit this loophole, and you should use 2 ports for your client and server connections.