duanli0687 2018-06-27 20:35
浏览 477
已采纳

Golang net.Listen绑定到已使用的端口

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

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()

fails with:

$ 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

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
", err)
    } else {
        fmt.Println("Listening")
    }
}

Succeeds with:

$ go run test.go
Listening

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

Why does this succeed on a Mac, and how can I get the net.Listen to show an error in binding to port 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))
}

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: 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

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!

  • 写回答

2条回答 默认 最新

  • dongxian4531 2018-06-28 02:36
    关注

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

    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)

    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.

    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.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 求解 yolo算法问题
  • ¥15 虚拟机打包apk出现错误
  • ¥30 最小化遗憾贪心算法上界
  • ¥15 用visual studi code完成html页面
  • ¥15 聚类分析或者python进行数据分析
  • ¥15 三菱伺服电机按启动按钮有使能但不动作
  • ¥15 js,页面2返回页面1时定位进入的设备
  • ¥50 导入文件到网吧的电脑并且在重启之后不会被恢复
  • ¥15 (希望可以解决问题)ma和mb文件无法正常打开,打开后是空白,但是有正常内存占用,但可以在打开Maya应用程序后打开场景ma和mb格式。
  • ¥20 ML307A在使用AT命令连接EMQX平台的MQTT时被拒绝