我无法将WebSocket服务器放置在Docker容器中。
这是服务器代码,它使用“ connected”写入新连接。
// server.go
func RootHandler(w http.ResponseWriter, r *http.Request) {
upgrader := websocket.Upgrader{ // (Uses gorilla/websocket)
ReadBufferSize: 4096,
WriteBufferSize: 4096,
}
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
panic(err)
}
if err = conn.WriteMessage(websocket.TextMessage, []byte("connected")); err != nil {
panic(err)
}
}
func main() {
fmt.Println("server is running")
// For graceful shutdown
stop := make(chan os.Signal, 1)
signal.Notify(stop, os.Interrupt)
server := http.Server{Addr: "localhost:8000"}
defer server.Close()
http.HandleFunc("/", RootHandler)
go func() {
err := server.ListenAndServe()
if err != nil && err != http.ErrServerClosed {
panic(err)
}
}()
<-stop
}
这里是客户:
// client.go
func main() {
connection, _, err := websocket.DefaultDialer.Dial("ws://localhost:8000", nil)
if err != nil {
panic(err)
}
_, b, err := connection.ReadMessage()
if err != nil {
panic(err)
}
fmt.Println(string(b)) // "connected"
}
先运行server.go
,然后运行client.go
,然后显示“已连接”,表明代码正在运行。现在,我想将服务器放入Docker容器中。这是dockerfile
:
FROM golang:1.11.4-alpine3.8
COPY . $GOPATH/src/websocket-test
WORKDIR $GOPATH/src/websocket-test
RUN ["go", "build", "server.go"]
EXPOSE 8000
CMD ["./server"]
我使用这些命令来构建和启动容器,暴露8000 / tcp。
docker build -t websocket-test .
docker run -p 8000:8000 websocket-test
我可以确认服务器正在运行,因为它会显示“服务器正在运行”。如果我在容器外启动client.go
,它会慌乱:
panic: read tcp [::1]:60328->[::1]:8000: read: connection reset by peer
我期望的结果与以前相同-在客户端打印“已连接”。该错误消息表示服务器在握手之前断开了连接。我不明白“ 60328”的数字。据我所知,WebSocket不会在升级时更改端口,因此我可以只暴露8000。
我不知道我必须更改什么才能通过WebSocket连接到我的服务器。
答案 0 :(得分:2)
当您指定要监听的主机名或IP地址(在本例中为localhost
解析为127.0.0.1
的情况下)时,服务器将仅监听该IP地址。
在Docker容器之外时,在localhost
上侦听不是问题。如果您的服务器仅在127.0.0.1:8000
上侦听,则您的客户端可以轻松连接到该服务器,因为该连接也是通过127.0.0.1
建立的。
当您在Docker容器中运行服务器时,它只会像以前一样在127.0.0.1:8000
上进行监听。 127.0.0.1
是本地环回地址,在容器外部无法访问。
当您使用-p 8000:8000
启动Docker容器时,它将把流向127.0.0.1:8000
的流量转发到该容器的IP地址,在我的情况下为172.17.0.2
。
容器在docker0网络接口内获取IP地址(您可以使用ip addr ls
命令看到此地址)
因此,当您的流量转发到172.17.0.2:8000
上的容器时,那里没有任何监听,并且连接尝试失败。
问题出在监听地址上
server := http.Server{Addr: "localhost:8000"}
要解决您的问题,请将其更改为
server := http.Server{Addr: ":8000"}
这将使您的服务器侦听其容器的所有IP地址。
当您在Docker容器中公开端口时,Docker将创建iptables规则进行实际转发。看到这个。您可以通过以下方式查看这些规则:
iptables -n -L
iptables -t nat -n -L