ws无法连接时停止频道

时间:2019-06-20 18:53:54

标签: multithreading go websocket channel

我有以下代码可以正常运行,问题是当socket.Connect() 失败无法连接时,我想停止该过程,我尝试使用以下代码 但它不起作用,即如果套接字连接失败,则程序仍然运行。 我想发生的是,如果connect失败,进程停止 channe …我在这里缺少什么?

func run (appName string) (err error) {


        done = make(chan bool)
        defer close(done)


        serviceURL, e := GetContext().getServiceURL(appName)

        if e != nil {
            err = errors.New("process failed" + err.Error())
            LogDebug("Exiting %v func[err =%v]", methodName, err)
            return err
        }

        url := "wss://" + serviceURL + route


        socket := gowebsocket.New(url)
        addPass(&socket, user, pass)


        socket.OnConnectError = OnConnectErrorHandler
        socket.OnConnected = OnConnectedHandler
        socket.OnTextMessage = socketTextMessageHandler
        socket.OnDisconnected = OnDisconnectedHandler

        LogDebug("In %v func connecting to URL  %v", methodName, url)
        socket.Connect()

        jsonBytes, e := json.Marshal(payload)
        if e != nil {
            err = errors.New("build process failed" + e.Error())
            LogDebug("Exiting %v func[err =%v]", methodName, err)
            return err
        }

        jsonStr := string(jsonBytes)

        LogDebug("In %v Connecting to payload JSON is  %v", methodName, jsonStr)
        socket.SendText(jsonStr)

        <-done
        LogDebug("Exiting %v func[err =%v]", methodName, err)
        return err

    }


    func OnConnectErrorHandler(err error, socket gowebsocket.Socket) {
        methodName := "OnConnectErrorHandler"
        LogDebug("Starting %v parameters [err = %v , socket = %v]", methodName, err, socket)
        LogInfo("Disconnected from server ")
        done <- true
    }

该进程应为运行大约60-90秒的进程打开一个ws连接(例如执行npm install),并通过web socke t及其完成时间获取进程的日志,当然还有句柄可能会发生的问题,例如网络问题或运行过程出现错误

2 个答案:

答案 0 :(得分:3)

因此@Slabgorb是正确的-如果您在此处(https://github.com/sacOO7/GoWebsocket/blob/master/gowebsocket.go#L87)看到OnConnectErrorHandler是在对Connect()的调用执行期间被同步调用的。在完全建立连接并且完成Connect()回调之后,OnConnected函数不会启动单独的goroutine来处理websocket。因此,当您尝试写入未缓冲的通道done时,您将阻止 same goroutine,该goroutine首先调用了run()函数,并且使自己死锁,因为没有goroutine将能够从频道中读取内容以解除对您的阻止。

因此,您可以采用他的解决方案并将其转换为缓冲通道,这将起作用,但是我的建议是不要为此类一次性标志行为写通道,而应使用{{1} }代替。为您要终止的每个条件定义一个通道close,并在相应的websocket处理函数中,在条件发生时run()定义该通道。在close的底部,您可以在所有通道上run(),并在第一个关闭时退出。看起来像这样:

select

这有一些优点:

1)您无需猜测哪些回调发生在进程中,哪些回调发生在后台goroutine中(并且您不必使所有通道都以“以防万一”的方式缓冲)

2)在多个通道上进行选择可让您找出要退出的原因,并可能以不同的方式处理清理或日志记录。

注意1:如果您选择使用package main import "errors" func run(appName string) (err error) { // first, define one channel per socket-closing-reason (DO NOT defer close these channels.) connectErrorChan := make(chan struct{}) successDoneChan := make(chan struct{}) surpriseDisconnectChan := make(chan struct{}) // next, wrap calls to your handlers in a closure `https://gobyexample.com/closures` // that captures a reference to the channel you care about OnConnectErrorHandler := func(err error, socket gowebsocket.Socket) { MyOnConnectErrorHandler(connectErrorChan, err, socket) } OnDisconnectedHandler := func(err error, socket gowebsocket.Socket) { MyOnDisconectedHandler(surpriseDisconnectChan, err, socket) } // ... declare any other handlers that might close the connection here // Do your setup logic here // serviceURL, e := GetContext().getServiceURL(appName) // . . . // socket := gowebsocket.New(url) socket.OnConnectError = OnConnectErrorHandler socket.OnConnected = OnConnectedHandler socket.OnTextMessage = socketTextMessageHandler socket.OnDisconnected = OnDisconnectedHandler // Prepare and send your message here... // LogDebug("In %v func connecting to URL %v", methodName, url) // . . . // socket.SendText(jsonStr) // now wait for one of your signalling channels to close. select { // this will block until one of the handlers signals an exit case <-connectError: err = errors.New("never connected :( ") case <-successDone: socket.Close() LogDebug("mission accomplished! :) ") case <-surpriseDisconnect: err = errors.New("somebody cut the wires! :O ") } if err != nil { LogDebug(err) } return err } // *Your* connect error handler will take an extra channel as a parameter func MyOnConnectErrorHandler(done chan struct{}, err error, socket gowebsocket.Socket) { methodName := "OnConnectErrorHandler" LogDebug("Starting %v parameters [err = %v , socket = %v]", methodName, err, socket) LogInfo("Disconnected from server ") close(done) // signal we are done. } 信令,则必须 为每个信号源使用不同的通道,以避免可能导致通道被关闭两次的竞争状况不同的goroutine(例如,当您返回响应时就会发生超时,并且两个处理程序都将触发;第二个处理程序关闭同一通道会导致close。)这也是为什么您不想{{1 }}所有通道均位于函数顶部。

注2:与您的问题没有直接关系,但是-您不需要 关闭每个通道-一旦所有句柄都超出范围,该通道将被垃圾回收是否已关闭。

答案 1 :(得分:1)

好的,正在发生的是,当您尝试向其中添加内容时,该频道正在阻塞。尝试使用这样的缓冲区(我使用1)初始化done通道:

done = make(chan bool, 1)