为什么从客户端正在监听的地址拨号不起作用,但相反的情况却可以?

huangapple go评论81阅读模式
英文:

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.

huangapple
  • 本文由 发表于 2016年1月8日 17:53:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/34674052.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定