So, I'm trying to construct a websocket server in go. And i ran into this interesting bug, which i cant for the life of me figure out why its happening.
NOTE: The comments in the code snippets are there only for this post. Read them.
Ive got this function:
func Join(ws *websocket.Conn) {
Log.Connection(ws)
enc := json.NewEncoder(ws)
dec := json.NewDecoder(ws)
var dJ g.DiscussionJoin
var disc g.Discussion
Log.Err(dec.Decode(&dJ), "dec.Decode")
ssD := g.FindDiscussionByID(dJ.DiscussionID)
ssDJ := dJ.Convert(ws)
g.DiscHandle <- &ssDJ
disc = ssD.Convert()
Log.Err(enc.Encode(disc), "enc.Encode")
Log.Activity("Discussion", "Joined", disc.DiscussionID.Subject)
fmt.Println("Listening") //This gets called
g.Listen(dec)
fmt.Println("Stoped Listening") //This DOESN'T get called [IT SHOULD]
ssDJ.SSDiscussion.Leave(ssDJ.SSUserID)
Log.Disconnection(ws)
}
The function thats causing this is (in my opinion) g.Listen(...):
func Listen(dec *json.Decoder) {
timeLastSent := time.Now().Second()
in := Message{}
for ((timeLastSent + ConnTimeout) % 60) != time.Now().Second() {
if err := dec.Decode(&in); err != nil {
continue
} else if in == Ping {
timeLastSent = time.Now().Second()
continue
}
timeLastSent = time.Now().Second()
Messages <- in
in = Message{}
}
fmt.Println("Client timed out!") //This gets called
return
}
Ive tried both with and without the return on the last row of Listen
As response to @SimoEndre, Ive left the main method out of the code example, but since you mentioned it, this is the function that takes g.Messege{} out of the Messeges channel.
NOTE: MessageHandler() runs on its own go routine.
func MessageHandler() {
for msg := range Messages {
for _, disc := range LivingDiscussions {
if disc.DiscussionID.UDID == msg.UDID {
go disc.Push(msg)
break
}
}
}
}
Looking at the Listen
function you will remark that it has a Messages
channel which receive the the Message{}
struct, but in the main goroutine it does not get outputted. Remember that goroutines are two way communication channels, which means that if a channel does receive an input value it must have an output value the channel to not block.
So you need to create a channel with the same struct type as the Message{}
message := make(chan Message{})
Then in the Join function you have to pop out the value pushed to channel:
func Join(ws *websocket.Conn) {
...
<-message
}
Update after new inputs:
It's not enough to iterate over the values coming from a channel, you need to do this inside a go func()
.
Getting the values out of different concurrently executing goroutines can be accomplished with the select
keyword, which closely resembles the switch control statement and is sometimes called the communications switch.
go func() {
for msg := range Messages {
for _, disc := range LivingDiscussions {
if disc.DiscussionID.UDID == msg.UDID {
select {
case disc.Push <- msg: // push the channel value to the stack
default :
// default action
}
}
}
}
}()
I don't know how your disc.Push
method is implemented, but if the idea is to push the received channel values to a stack you have to modify your code in a way to send back the channel value to the array. In the code snippet above i've just wanted to emphasize that it's important to get the values back pushed into the channel.