英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论