我有以下代码可以正常运行,问题是当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及其完成时间获取进程的日志,当然还有句柄可能会发生的问题,例如网络问题或运行过程出现错误
答案 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)