Golang net.Listen绑定到已经使用的端口 [英] Golang net.Listen binds to port that's already in use

查看:156
本文介绍了Golang net.Listen绑定到已经使用的端口的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

端口8888已通过在docker容器中运行的进程绑定到我的(OS X 10.13.5)系统上:

Port 8888 is already bound on my (OS X 10.13.5) system, by a process running inside a docker container:

$ netstat -an | grep 8888
tcp6       0      0  ::1.8888               *.*                    LISTEN
tcp4       0      0  *.8888                 *.*                    LISTEN

试图绑定到该端口的python程序(使用我可以管理的与golang尽可能接近的socket选项),以我期望的方式失败:

A python program which tries to bind to that port (using as close to the socket options of golang as I can manage), fails in the way I expect:

import socket
import fcntl
import os


def main():
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    flag = fcntl.fcntl(sock.fileno(), fcntl.F_GETFL)
    fcntl.fcntl(sock.fileno(), fcntl.F_SETFL, flag | os.O_NONBLOCK)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    sock.bind(("0.0.0.0", 8888))
    sock.listen(5)


main()

失败:

$ python test.py
Traceback (most recent call last):
  File "test.py", line 15, in <module>
    main()
  File "test.py", line 11, in main
    sock.bind(("0.0.0.0", 8888))
OSError: [Errno 48] Address already in use

但是,通过 net.Listen 创建连接的go程序不会失败,正如我期望的那样:

But a go program creating a connection via net.Listen does not fail, as I expect it to:

package main

import (
    "fmt"
    "net"
)

func main() {
    _, err := net.Listen("tcp", "0.0.0.0:8888")
    if err != nil {
        fmt.Printf("Connection error: %s\n", err)
    } else {
        fmt.Println("Listening")
    }
}

成功:

$ go run test.go
Listening

一个同事报告说,使用相同的设置,他的Ubuntu系统正确地使go程序失败.

A coworker reports that with the same setup, his Ubuntu system correctly fails the go program.

为什么在Mac上会成功,如何获取网络.听一听显示绑定到端口8888时出现错误?

Why does this succeed on a Mac, and how can I get the net.Listen to show an error in binding to port 8888?

如果我使用简单的go程序占用端口8888:

edit: If I occupy port 8888 with a simple go program like:

package main

import (
    "log"
    "net/http"
)

func main() {
    log.Fatal(http.ListenAndServe("0.0.0.0:8888", nil))
}

然后正确地将 test.go 绑定到端口.但是docker进程(基本上在运行^^^)不会导致它失败.

Then test.go correctly fails to bind to the port. However the docker process (which is running basically that ^^^) does not cause it to fail.

edit 2:如果我指定"tcp4",则该程序确实确实会按我的预期失败.如果我指定"tcp6",它会成功,但是netstat表示它绑定到 * 而不是 :: 1 :

edit 2: If I specify "tcp4", then the program does indeed fail as I expect. If I specify "tcp6", it succeeds but netstat says it binds to * instead of ::1:

$ netstat -an | grep 8888
tcp6       0      0  *.8888                 *.*                    LISTEN
tcp6       0      0  ::1.8888               *.*                    LISTEN
tcp4       0      0  *.8888                 *.*                    LISTEN

因此,指定"tcp4"将解决我的实际问题,但我真的很想了解"tcp46"连接类型到底在发生什么,而我找不到任何文档.救命!

So, specifying "tcp4" will solve my actual problem, but I really want to understand what the heck is going on with the "tcp46" connection type, and I can't find any documentation. Help!

推荐答案

好的,我想我有一个故事来讲述为什么发生这种情况:

OK, I think I have a story to tell about why this happens:

  1. 在Mac上的Docker映射端口时,绑定到IPv4 0.0.0.0:<port> 和IPv6 [:: 1]:< port> .请注意,在IPv6上,它映射到 localhost 的等效项,而不是 0.0.0.0 ,即 [::]
  2. Golang在打开套接字进行侦听时,默认情况下会打开一个IPv6套接字,该套接字以某种方式映射为也可以侦听IPv4.(我仍然不完全了解这种 tcp46 类型,因此,如果您有好的文档请指向我!)
  3. 所以我的golang程序正在 [::]:8888 上打开一个IPv6套接字,相当于IPv6中的 0.0.0.0:8888 .之所以成功是因为docker正在监听 [:: 1] (相当于127.0.0.1),而不是 not [::]
  4. 就是这样!因此,即使golang程序仅通过从非环回地址在IPv6上连接的客户端进行连接(我认为,我也太累了,无法测试它,让我休息一下),该程序还是成功的.
  1. Docker on mac, when mapping a port, binds to IPv4 0.0.0.0:<port> and IPv6 [::1]:<port>. Note that on IPv6 it maps to the equivalent of localhost rather than 0.0.0.0, which would be [::]!
  2. Golang, when opening a socket to listen on, by default opens an IPv6 socket that is mapped in some way to also listen to IPv4. (I still don't completely understand this tcp46 type, so if you have good docs please point me to them!).
  3. So my golang program was opening an IPv6 socket on [::]:8888, the equivalent of 0.0.0.0:8888 in IPv6. This succeeded because docker was listening on [::1] (the equivalent of 127.0.0.1), not [::]
  4. That's it! So the golang program succeeded even though it will only get connected to by a client connecting on IPv6 from a non-loopback address (I think, I got too tired to test this, give me a break)

我的同事报告说,在Ubuntu上,docker监听 [::] ,这就是为什么他无法重现我看到的问题的原因.这似乎是明智的行为!而且我不知道为什么在Mac上它不这样做.

My coworker reports that on Ubuntu, docker listens on [::], which is why he was unable to reproduce the problem I was seeing. This seems like the sensible behavior! And I have no idea why it doesn't do so on mac.

我还认为,即使Go创建的套接字实际上很难访问,但它在这种情况下成功地获得成功是令人惊讶的并且可能有点错误.但是我不能说这绝对是一个错误,而且我绝对不想尝试将其报告给go项目.

I also think it's surprising and possibly a bit wrong that Go happily succeeds in this instance, even though it's creating a socket that's very difficult to actually access? But I can't say that it's definitely a bug, and I definitely don't feel like trying to report it as such to the go project.

这篇关于Golang net.Listen绑定到已经使用的端口的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆