To start off, it is worth mentioning that within a single F# solution, serializing and deserializing of Bond messages works fine. However, I am having trouble properly handling the sending and/or receiving of the message over ZeroMQ.
There is a runtime error on the subscriber side of the following program. A .bond file is defined and compiled with the bond compiler. Then a dll is created from C# to be called from F# . I then have two F# programs. One that publishes serialized data over a tcp socket and another that is a subscriber. When the message is received on the sub, the line which tries to Unmarshal the raw data is the one that causes the runtime error. Can anyone see the reason for this?
[EDIT] Per Fyodor's comment, I made a change on the publisher side which changes the error on the subscriber side. So the error likely has something to do with how i am packing and unpacking the information.
This is the .bond file
namespace Examples
struct Record
{
0: map<string, double> payload;
}
Here is the publisher:
// publisher
open System
open Bond
open Bond.Protocols
open Bond.IO.Safe
open ZeroMQ
let ctx = new ZContext()
let publisher = new ZSocket(ctx, ZSocketType.PUB)
publisher.Bind("tcp://*:5556")
let src = new Examples.Record()
src.payload.Add("a", 1.)
src.payload.Add("b", 2.)
let output = new OutputBuffer()
let writer = new CompactBinaryWriter<OutputBuffer>(output)
while true do
Marshal.To(writer, src)
//let input = new InputBuffer(output.Data)
//let byteArr = input.ReadBytes(int(input.Length - 1L))
let updateFrame = new ZFrame(System.Text.Encoding.ASCII.GetString output.Data.Array)
publisher.Send(updateFrame)
Here is the subscriber:
// subscriber
open Bond
open Bond.Protocols
open Bond.IO.Safe
open System
open System.Text
open ZeroMQ
let ctx = new ZContext()
let subscriber = new ZSocket(ctx, ZSocketType.SUB)
subscriber.Connect("tcp://127.0.0.1:5556")
subscriber.SubscribeAll()
let output = new OutputBuffer()
while true do
let received = subscriber.ReceiveFrame()
let byteArr = Encoding.ASCII.GetBytes (received.ReadString())
let arrSeg = ArraySegment<byte>(byteArr)
let input = new InputBuffer(arrSeg)
let dst = Unmarshal<Examples.Record>.From(input)
for KeyValue(k, v) in dst.payload do
printfn "%A %A" k v
On the receiving side, when you attempt to decode the marshaled Bond Compact Binary as an ASCII string, you're losing some of the payload. When marshaling a struct like
Recordto Compact Binary, the first four bytes of the payload are0x43 0x42 0x10 0x00. When reading a string from a ZFrame, the first embeddedNUL (0x00)that is encountered signals the end of the string, regardless of the size of the frame. So, the reading side sees only0x43 0x42 0x10instead of the whole payload (29 bytes when I tested).Since Compact Binary is a binary protocol, you'll want to use the
ZFrameconstructor that takes a buffer on the publisher side:On the subscriber side, you'll want to just read the buffer:
Also, on the publisher side, you're constantly accumulating data into the same OutputBuffer. You'll want to reset
output.Positionto 0 before you marshall your next record to re-use the buffer instead of growing it:Another thing to note: the default buffer allocated for an
OutputBufferis 65KiB. Consider making this smaller, once you know about how large your payloads are going to be.NB: I debugged this in a C# application that had similar semantics. Here's what I used: