Skip to content

Bridge Documentation

Adam Janin edited this page May 6, 2016 · 3 revisions

TL;DR:

If two Transports can't talk to each other, it's probably because they're on different subnets. Run one instance of bridge_client.py on each subnet to get them to communicate.

If you want to use C++ on Windows to communicate with Transport, see below for a class to do that.

NOTE: Multiple federations on the same subnets will not currently work without a fix from the author of Pyre.

Details

The current design of Transport requires all instances to be on the same subnet. A "bridge" allows multiple subnets to be connected and act as if they're a single subnet.

The bridge consists of a bridge server and one or more bridge clients.

Bridge Server

The bridge server must run on a host that's accessible to all clients (i.e. a static IP address with an open port). There's a command line argument to set the port, which defaults to 7417. You can use any open port.

The server is really simple. It just echos everything it receives to all clients. Because of this, you should use a different server for each federation.

It's quite easy to set up servers on e.g. Amazon. You can easily run multiple servers on the same instance. I suggest running one on the default port (7417) and another for debugging (e.g. 8856).

If you're at ICSI, contact Adam Janin for details on where the server is running.

Bridge Clients

You need to run one (and only one) bridge_client.py on each subnet that you want to connect. With no command line arguments, it will use the default server on port 7417. Use command line arguments -host and -port to set other values for the host and port.

You should start bridge clients BEFORE you start any other Transport objects. When you want to quit, calling Transport.quit_federation() will also quit the local bridge client. You can also type "quit" into the terminal where you started the bridge client, which will cause the bridge client to quit but will not quit the federation.

Note that there's a bug in Pyre that makes using multiple federations on the same subnet problematic. If we need to do this, I can try to fix it myself or try (again) to get the Pyre guy to fix it.

Communicating directly with a bridge server using C++ on Windows

Since one of our application (StarCraft) uses C++, we've written a C++ class that "fakes" some of the Transport functionality by communicating directly with a bridge server.

TransportBridge is a C++ class that communicates directly with the Bridge Server in order to allow you to communicate with python Transports. It uses the library rapidjson (http://rapidjson.org) for JSON encoding and decoding. Note that TransportBridge uses a lower level protocol (known as Pyre) than Transport, so it's a bit more complicated to use. For example, unlike the python version, TransportBridge gets ALL events, not just the events that are directed at it.

Example Usage:

// Create a TransportBridge object with default info
TransportBridge tb;

// Create a rapidjson document for storing the results.
rapidjson::Document doc;

// Block until there's a message from the BridgeServer.
// Store it in doc.
tb.recv_json(&doc);

// doc now contains a Pyre message from the BridgeServer.

You will receive ALL Pyre events on any of the local networks that communicate with the Bridge Server. Here's an example event:

["SHOUT", "chat1", "StarCraft", "message from chat1"]

This message means that chat1 sent a message to StarCraft, and the message was the simple string "message from chat1" (the message could itself be a json object).

Since the only messages you probably care about are SHOUTs sent to yourself, you should do something like:

if (!strcmp(doc[0].GetString(), "SHOUT") &&
    !strcmp(doc[2].GetString(), "StarCraft")) {
 	     ... handle message ...

TransportBridge::data_available()

Another function you'll use is tb.data_available(). It checks if there's any data from the Bridge Server waiting to be processed. By default, tb.data_available() returns immediately. This is useful if you're calling it in a periodic event loop (e.g. per frame in StarCraft). You can also pass it an integer timeout, which is the number of seconds to block waiting to see if data is available.

TransportBridge::send_*

You can send a message to any Transport by using tb.send_string() or tb.send_json(). Note that you must format the message as a JSON encoded Pyre message. For example:

tb.send_string("[\"SHOUT\", \"StarCraft\", \"chat1\", \"a message\"]")

This will send the simple string "a message" FROM StarCraft TO chat1. As long as chat1 has subscribed to StarCraft messages (and the bridge is set up correctly), the message will be delivered.

tb.send_json() is similar, but you can pass it a rapidjson document instead of a string.

(We'll probably want to add some convenience routines, e.g. printf-like or cout-like variants).

Installing

Just add TransportBridge.cpp and TransportBridge.h to your project. The assumption is that they're in the same directory. You also need the header files from rapidjson (http://rapidjson.org). They're assumed to be in a directory named rapidjson in the same place as TransportBridge.cpp and TransportBridge.h. Edit the #include "rapidjson" lines in the C++ files if you change the location.

NOTES

  1. You must initialize/finalize winsock yourself. See testmain.cpp or TransportBridge.h for examples.

  2. You should not allow too many messages to queue up. In other words, be sure to call tb.recv_json every once in a while. Otherwise, you can fill up OS queues and cause everything to grind to a halt.

  3. The remote Bridge Server and Bridge Clients must already be running when you create a TransportBridge.

  4. The tb.recv_json() call blocks until a full message is available. Either call tb.recv_json() from an independent thread, or combine with e.g.:

    if (tb.data_available()) { tb.recv_json(doc); ...

  5. See TransportBridge.h for more code-level documentation and testtb.cpp for a complete console example.

  6. I strongly suspect Unicode will cause everything to die a horrible death. Try to stick with ascii.