fszmq


"Hello, World" with fszmq

As a gentle introduction to using fszmq, we'll create a simple client\server example. Specifically, we'll create two sockets. One socket, acting as the server, will bind to a TCP endpoint. Whenever it gets a request message it will take some action. Meanwhile, the other socket will act as a client. It will connect to the server's TCP endpoint and issue a number of requests. For each reply, it will take some action.

To start, we'll reference our library and open the fszmq namespace, which defines the Socket type and the Context type. (Note: don't worry too much about contexts just yet. For now, assume every node in your network has one Context instance which owns one, or more, Socket instances.) We'll also open some modules which contain functions for working with contexts and sockets.

1: 
2: 
3: 
4: 
#r "fszmq.dll"
open fszmq
open fszmq.Context
open fszmq.Socket

Now here's our basic server that takes "hello" requests and replies with "world". Any other message causes it to exit.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
let server () =
  // create a ZMQ context
  use context = new Context()

  // create reply socket
  use server  = rep context
  // begin receiving connections
  bind server "tcp://*:5555"

  let rec loop () =
    // process request (i.e. 'recv' a message from our 'server')
    // NOTE: it's convenient to 'decode' the (binary) message into a string
    match server |> recv |> decode with
    | "hello"   ->  // valid request; send a reply back
                    // NOTE: "..."B is short-hand for a byte array of ASCII-encoded chars
                    "world"B |>> server
                    // wait for next request
                    loop()
    | _         ->  // invalid request; stop receiving connections
                    "goodbye"B |>> server

  // wait for next request
  loop ()

And here's a simple client that makes 10 requests and prints the server's reply each time.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
let client () =
  // create a ZMQ context
  use context = new Context()

  // create a request socket
  use client  = req context
  // connect to the server
  "tcp://localhost:5555" |> connect client

  for i in 1 .. 10 do
    // 'send' a request to the server
    let request = if i = 10 then "goodbye" else "hello"
    // NOTE: we need to 'encode' a string to binary (before transmission)
    request |> encode |> send client
    printfn "(%i) sent: %s" i request
    // receive and print a reply from the server
    let reply = (recv >> decode) client
    printfn "(%i) got: %s" i reply

If you run this example in F# Interactive, you should see the following:

(1) sent: hello
(1) got: world
(2) sent: hello
(2) got: world
(3) sent: hello
(3) got: world
(4) sent: hello
(4) got: world
(5) sent: hello
(5) got: world
(6) sent: hello
(6) got: world
(7) sent: hello
(7) got: world
(8) sent: hello
(8) got: world
(9) sent: hello
(9) got: world
(10) sent: goodbye
(10) got: goodbye

Notice how our two sockets are communicating synchronously. In other words, the client sends one request and must wait for a reply. Conversely, the server waits for a single request and immediately responds to it. If this were not the case, the parenthetical numbers at the start of each output line could be out of order.

But don't think this is the only way to use fszmq! Go look at the other samples, or read the zguide, to see examples of asynchronous client\server, publish\subscribe, map\reduce, and many other distributed computing patterns. And, most of all, have fun using F# ("Simple code for complex problems") and ZeroMQ ("Distributed computing made simple")!

module docs
module PATH

from docs
val hijack : unit -> unit

Full name: docs.PATH.hijack
namespace fszmq
Multiple items
module Context

from fszmq

--------------------
type Context =
  interface IDisposable
  new : unit -> Context
  private new : __:obj -> Context
  member private Attach : socket:Socket -> unit
  override Equals : obj:obj -> bool
  override Finalize : unit -> unit
  override GetHashCode : unit -> int
  override ToString : unit -> string
  member Handle : nativeint

Full name: fszmq.Context

--------------------
new : unit -> Context
Multiple items
module Socket

from fszmq

--------------------
type Socket =
  interface IDisposable
  private new : context:HANDLE * socketType:int -> Socket
  override Equals : obj:obj -> bool
  override Finalize : unit -> unit
  override GetHashCode : unit -> int
  override ToString : unit -> string
  member Handle : nativeint

Full name: fszmq.Socket
val server : unit -> unit

Full name: Tutorial.server
val context : Context
val server : Socket
val rep : context:Context -> Socket

Full name: fszmq.Context.rep
val bind : socket:Socket -> address:string -> unit

Full name: fszmq.Socket.bind
val loop : (unit -> unit)
val recv : socket:Socket -> byte []

Full name: fszmq.Socket.recv
val decode : value:byte [] -> string

Full name: docs.decode
val client : unit -> unit

Full name: Tutorial.client
val client : Socket
val req : context:Context -> Socket

Full name: fszmq.Context.req
val connect : socket:Socket -> address:string -> unit

Full name: fszmq.Socket.connect
val i : int32
val request : string
val encode : value:'a -> byte []

Full name: docs.encode
val send : socket:Socket -> frame:byte [] -> unit

Full name: fszmq.Socket.send
val printfn : format:Printf.TextWriterFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
val reply : string
val spawn : fn:(unit -> unit) -> unit

Full name: docs.spawn
val release : unit -> unit

Full name: docs.PATH.release
Fork me on GitHub