go websockets eof

6
我正在尝试制作一个简单的命令转发器,将我的家用计算机连接到我拥有的服务器,以便我可以向服务器推送命令并让我的家用电脑接收它们。这些命令是我下载器的简单暂停/恢复操作。我的设计是在服务器上运行一个中心实例,它创建了一个窗口来传递命令和一个窗口将这些命令传递给我的PC后端。我使用通道将这两个“窗口”绑定在一起,它们运行一个服务器。当客户端连接并向中心发送消息时,它通过通道流式传输到后端窗口,然后传输到真正的后端(在我的家用PC上)。当后端响应中心窗口上的后端窗口时,中心将结果打印回客户端。
通过这种方法,只有第一条消息通过并与我的下载器配合工作。每次收到消息时,我都必须重新连接从我的家用PC到中心的后端才能使其正常工作。我认为这不是使用Websockets的正确方式,所以我在这里。在成功请求一次之后(当后端完成其工作并回复结果时),它将永远循环并出现EOF错误。
代码的重要部分如下:

如果您将源代码放在GOPATH中(我正在开发它以支持现代Websockets的最新版本),则编译它:

go build gosab/cmd,运行它:
  • ./cmd -mode="hub" 中心
  • ./cmd -mode="backend" --address="localhost:8082" 后端

要将消息传递给中心,请使用此JavaScript:

var s = new WebSocket("ws://localhost:8082")
s.send("1 5")

那么我该如何处理呢?使用通道是否是在两个不同请求之间进行通信的好方法?


重新发明轮子,也许只是为了好玩?听起来很像Salt:http://saltstack.org/ 或Puppet Labs Marionette:http://docs.puppetlabs.com/mcollective/index.html - djangofan
好的链接,但我想自己实现类似的东西。 - farnoy
我使用类似的东西(一个具有Websockets浏览器-服务器通信的游戏服务器),因此我知道如何在Go中实现它,但我不明白这里的真正问题是什么。如果只是“通道是否是一种好的通信方式”,那么答案是肯定的。 - Denys Séguret
你想让我发布作为答案(太大了,无法在评论中发布)的我的Websocket Go服务器的主要代码吗?我觉得你需要做一些类似的事情(更简单,因为你不会为特定桌子的所有玩家更新游戏(玩家坐下来玩或观察))。 - Denys Séguret
抱歉长时间未回复,我的真正问题是当您将消息传递给代理时,问题在于如何将它们传递给另一个WebSocket连接(这里是后端)。我认为通道不适用于此,使用一个不可阻塞的队列会更好。我已经使用Faye在node.js和ruby上实现了这个逻辑和程序,所以我不需要这个问题的答案了。我需要在基于队列的消息中继中重新实现这个go代码,但由于有很多引擎可以进行这种通信,我没有时间去做。 - farnoy
1个回答

7
我很惊讶你没有收到对此的答复。
您需要做的是像下面的代码一样。当您收到传入的websocket连接时,将为该连接生成一个新的goroutine。如果让这个goroutine结束,它会断开websocket客户端的连接。
我假设您并不一定会在同一台计算机上运行客户端和服务器。如果您始终如此,最好通过通道或类似方式在内部进行通信,而不是使用websocket或网络端口。我之所以提到这一点,是因为我并不完全确定您使用它的目的是什么。我只希望我回答了您问题的正确部分。
package main

import (
    "code.google.com/p/go.net/websocket"
    "flag"
    "fmt"
    "net/http"
    "os"
    "time"
)

type Message struct {
    RequestID      int
    Command        string
    SomeOtherThing string
    Success        bool
}

var mode *string = flag.String("mode", "<nil>", "Mode: server or client")
var address *string = flag.String("address", "localhost:8080", "Bind address:port")

func main() {
    flag.Parse()

    switch *mode {
    case "server":
        RunServer()
    case "client":
        RunClient()
    default:
        flag.Usage()
    }
}

func RunServer() {
    http.Handle("/", http.FileServer(http.Dir("www")))
    http.Handle("/server", websocket.Handler(WSHandler))
    fmt.Println("Starting Server")
    err := http.ListenAndServe(*address, nil)
    if err != nil {
        fmt.Printf("HTTP failed: %s\n", err.Error())
        os.Exit(1)
    }
}

func WSHandler(ws *websocket.Conn) {
    defer ws.Close()
    fmt.Println("Client Connected")
    for {
        var message Message
        err := websocket.JSON.Receive(ws, &message)
        if err != nil {
            fmt.Printf("Error: %s\n", err.Error())
            return
        }
        fmt.Println(message)

        // do something useful here...

        response := new(Message)
        response.RequestID = message.RequestID
        response.Success = true
        response.SomeOtherThing = "The hot dog left the castle as requested."
        err = websocket.JSON.Send(ws, response)
        if err != nil {
            fmt.Printf("Send failed: %s\n", err.Error())
            os.Exit(1)
        }
    }
}

func RunClient() {
    fmt.Println("Starting Client")
    ws, err := websocket.Dial(fmt.Sprintf("ws://%s/server", *address), "", fmt.Sprintf("http://%s/", *address))
    if err != nil {
        fmt.Printf("Dial failed: %s\n", err.Error())
        os.Exit(1)
    }
    incomingMessages := make(chan Message)
    go readClientMessages(ws, incomingMessages)
    i := 0
    for {
        select {
        case <-time.After(time.Duration(2e9)):
            i++
            response := new(Message)
            response.RequestID = i
            response.Command = "Eject the hot dog."
            err = websocket.JSON.Send(ws, response)
            if err != nil {
                fmt.Printf("Send failed: %s\n", err.Error())
                os.Exit(1)
            }
        case message := <-incomingMessages:
            fmt.Println(message)
        }
    }
}

func readClientMessages(ws *websocket.Conn, incomingMessages chan Message) {
    for {
        var message Message
        err := websocket.JSON.Receive(ws, &message)
        if err != nil {
            fmt.Printf("Error: %s\n", err.Error())
            return
        }
        incomingMessages <- message
    }
}

2
看起来非常有前途,一个简单的事件驱动服务器。我会在有时间的时候尝试这个想法,同时:+1 为了试图帮助。 - farnoy
如果你不想关闭连接,为什么在遇到错误时使用 return?连接上的EOF是否应该表示终止? - Tristian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接