Handling multiple message types with websockets

3,120 views
Skip to first unread message

John

unread,
Jun 12, 2012, 2:02:14 AM6/12/12
to golang-nuts
Hey guys,

I'm using the websockets library and I've run into a problem. I'm
using websockets to receive multiple message types.

In the standard JSON RPC library, you can register multiple calls so
you know what struct to marshal the return data with.

With websockets, that doesn't seem to be the case, it seems as if it
assumes you'll have only one type that is returned.

But say I have two different possible types that can be returned:
type A struct {
...
}

type B struct {
...
}

What is the best way on a receiving end to determine which one the
return data belongs to and marshal into that?

At first I thought you could do something like:

var incoming interface{}

err := websocket.JSON.Receive(conn, &incoming)

switch decoded := incoming.(type){
case A:
...
case B:
...
default:
...
}

The problem is that the data is marshalled into
map[string]interface{} . I've got something working that uses the
reflect package, but that's ugly. Hoping someone has already solved
this.

Thanks!

Andrew Gerrand

unread,
Jun 12, 2012, 2:07:37 AM6/12/12
to John, golang-nuts
If you have control over the wire protocol, you can create a message
struct like this:

type Message struct {
A *A
B *B
}

then structure your JSON data like:

{"A": {...}}
{"B": {...}}

Then you can test that the A and B fields are non-nil to find the type
of message.

If you can't control the wire protocol then you'll end up with
something messy. (Sorry.)

Andrew

John

unread,
Jun 12, 2012, 1:14:00 PM6/12/12
to golang-nuts
I seem to be having trouble getting this to work(I control both
sides).  So what I've got is something like this:

type Reply struct {
  SCR *SendCommandsReply
  Error *ErrorReply
}

type SendCommandsReply struct {
  Device string
  Results []string
}

type ErrorReply struct {
  Error string
}

<Server code>
wrapper.SCR = &SendCommandsReply{Device: req.Device, Results: results}
fmt.Printf("wrapper.SCR.Device: %s\n", wrapper.SCR.Device)
fmt.Printf("Sending: %v\n", wrapper)
websocket.JSON.Send(ws, wrapper)

<Client code>
var reply interface{}
err := websocket.JSON.Receive(conn, &reply)
if err != nil {
 fmt.Printf("Can't receive: %s\n", err)
 return
}

On the client side I see:
Can't receive: EOF

On the server side I see:
012/06/12 09:17:39 Received RPC request.
wrapper.SCR.Device: a_device_name
Sending: {0xf84012b8a0 <nil>}

On the server I'm reading into interface{} just to eliminate any
problems with decoding into the correct struct.  I must be doing
something wrong now to make it completely stop working.

--John

Sanjay

unread,
Jun 12, 2012, 2:03:44 PM6/12/12
to golan...@googlegroups.com
I don't have time to make an example right now, but I can give a quick sketch of a possible solution.

You can have a Message struct with a `Type' field of type int or string, and a `Data' field of type json.RawMessage. Then, depending on the value of the Type field, you can create and decode into the appropriate type of object. I think this is similar to what the jsonrpc package does, if I remember correctly. You'll have to write a function that switches on the Type field to do the decoding appropriately, or write a reflect based method to pick the right type.

Sanjay

Sanjay

unread,
Jun 12, 2012, 2:05:21 PM6/12/12
to golan...@googlegroups.com
Also, I forgot to mention, this will also require control over the wire protocol.

Sanjay

John

unread,
Jun 12, 2012, 4:50:25 PM6/12/12
to golang-nuts
I'll have a try at that.

One of the problems it looks like I'm having is doing the
err := websocket.JSON.Receive(conn, &reply)

on the Client side.

On the server side, the handler is doing all the work on getting stuff
from the websocket.

I guess I though that websocket.JSON.Receive was blocking until it got
something, but that doesn't look to be the case.

On the client side, if I send something, then want to pull a
certain(predictable) number of responses from the websocket, how do I
do that efficiently. Aka, I don't want to do something like:

data := interface{}
for x := 0; x < expecting; x++ {
for {
if err := webcocket.JSON.Receive(conn, &data); err != nil {
time.Sleep(1*time.Second)
continue
}
break
}
}

I want something where Receive blocks until either the socket closes
or I get data.

John

unread,
Jun 12, 2012, 5:12:54 PM6/12/12
to golang-nuts
Nevermind that last thing, I figured out the issue. Receive does
block, I'm just full of it.

Andrew Gerrand

unread,
Jun 12, 2012, 7:13:12 PM6/12/12
to John, golang-nuts
On 13 June 2012 03:14, John <johns...@gmail.com> wrote:
> On the server I'm reading into interface{} just to eliminate any
> problems with decoding into the correct struct.  I must be doing
> something wrong now to make it completely stop working.

I agree. I'm not sure what, though. The code you have posted here works fine.

I'm curious why you're using websockets when a Go program is both the
client and the server. Why not just use a TCP socket?

Andrew

John

unread,
Jun 12, 2012, 8:25:16 PM6/12/12
to golang-nuts
It won't necessarily be a Go program on the other end, the demo app
just happens to be.

I'm using websockets because I need end to end encryption with a trust
model, and websockets let me use TLS with little problems.
This way I can hopefully know via the certs and CA that both the
client and the agent are trusted. Once I have that setup, I then
authenticate the user over the tunnel(unfortunately simple comparison
of the user/pass to a the agent's SHA512 hash store, I really want to
use kerberos to do authentication, alas I don't see a library for
doing this, but will try to get around to providing some client in the
future).



On Jun 12, 4:13 pm, Andrew Gerrand <a...@golang.org> wrote:
Reply all
Reply to author
Forward
0 new messages