英文:
GO: Why does dialing from the address on which the client is listening not work, but the opposite does?
问题
我想知道为什么从客户端正在侦听的地址拨号(版本A)不起作用,但是在客户端拨号到服务器的连接地址上侦听确实起作用(版本B)?!
有人可以向我解释一下吗?对我来说,Go是新的,我还在学习很多东西。
这里有一个例子:
服务器程序:
package main
import (
	"fmt"
	"net"
	"os"
)
func main() {
	fmt.Println("server")
	listener, listenerError := net.Listen("tcp", "localhost:8080")
	if listenerError != nil {
		fmt.Println(listenerError)
		os.Exit(1)
	}
	for {
		con, _ := listener.Accept() // 在这个例子中,我不关心错误
		fmt.Printf("LocalAddr: %v\n", con.LocalAddr())
		fmt.Printf("RemoteAddr: %v\n", con.RemoteAddr())
	}
}
客户端版本A(不起作用):
package main
import (
	"fmt"
	"net"
	"os"
)
func main() {
	listener, listenerError := net.Listen("tcp", "localhost:0")
	if listenerError != nil {
		fmt.Println(listenerError)
		os.Exit(1)
	}
	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
		fmt.Println(err)
		os.Exit(2)
	}
	fmt.Printf("LocalAddr: %v\n", con.LocalAddr())
	fmt.Printf("RemoteAddr: %v\n", con.RemoteAddr())
}
客户端版本B(起作用):
package main
import (
	"fmt"
	"net"
	"os"
)
func main() {
	fmt.Println("client")
	con, err := net.Dial("tcp", "localhost:8080")
	if err != nil {
		fmt.Println(err)
		os.Exit(2)
	}
	// 在这里进行了一些魔法
	listener, listenerError := net.Listen("tcp", con.LocalAddr().String())
	if listenerError != nil {
		fmt.Println(listenerError)
		os.Exit(1)
	}
	fmt.Println("LISTENING")
	conn, _ := listener.Accept() // 将在 con.LocalAddr() 上接受连接
	fmt.Printf("LocalAddr: %v\n", conn.LocalAddr())
	fmt.Printf("RemoteAddr: %v\n", conn.RemoteAddr())
}
英文:
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\n", con.LocalAddr())
		Printf("RemoteAddr: %v\n", 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\n", con.LocalAddr())
	Printf("RemoteAddr: %v\n", 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\n", conn.LocalAddr())
	Printf("RemoteAddr: %v\n", conn.RemoteAddr())
}
答案1
得分: 2
"Version B"是Go语言的默认设置SO_REUSEADDR的副作用,它允许绑定到一个已经被现有连接使用的addr:port对。这两个套接字可以通过已建立连接的4元组(LocalAddr, LocalPort, RemoteAddr, RemotePort)来区分。
"Version A"不起作用,因为在建立连接时需要调用bind来设置请求的本地地址,而已经有一个绑定到该端口的监听套接字。
没有必要尝试利用这个漏洞,你应该为客户端和服务器连接使用两个不同的端口。
英文:
"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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论