TCP-Socket Transport

The TCP-socket-based transport layers a very simple protocol on top of ordinary TCP sockets:

  • Processes act as either TCP-clients or -servers for a common port. There is one server and zero or more clients. Each client-server connection is a bi-direction stream of notifications which are sent and received by participants in the respective processes.
  • Each process may host zero, one or many participants. Within a process, the participants share a connection.

Schema

See also

URIs
Use of URIs in RSB.

The TCP-socket-based transport uses the tcp URI schema.

Options

See also

Configuration
Specification of configuration mechanism.

The following configuration options are accepted by the TCP-socket-based transport:

Name              Type            Comment
+ transport
+-- socket
+---- host        string          Name of host running server
+---- port        uint            Port on which server listens
+---- server      { 0, 1, auto }  Act as server?
+---- tcpnodelay  boolean         Implementation detail

Addresses and Ports

  Server Client
Default address localhost 0.0.0.0
Default port 55555 55555

It is possible to operate in “auto” mode instead of client or server mode. In that case the following actions are performed:

  1. Try to bind a listen socket to the configured address, port pair
    • If this succeeds, act as server
    • If this fails, goto 2
  2. Try to connect to a socket bound to the configured address, port pair
    • If this succeeds, act as client
    • If this fails, give up

Note

This “auto” mode can only work properly for connections on a single computer: if a host other than the local host was used, it would be impossible to act as server when required.

In special cases, “auto” may still be useful for setups distributed over multiple computers but these cases require a detailed understanding of the above protocol and should generally be avoided.

Messages

The following messages are exchanged:

Name Size [bytes] Content Comment
mzero 4 four 0 bytes only used during handshake
msize 4 size of payload in mpayload little-endian
mpayload variable payload blob size is specified by previous msize

Note

The handshake part of the protocol (explained below) is required to prevent the following scenario from happening:

  1. A client process connects to the TCP-socket of the server (without handshake)
  2. The client process creates a listener waiting for events from some remote participant
  3. The client process causes some remote participant to send an event which the listener should receive
  4. The event is not delivered to the listener since the connection is not yet fully established despite the fact that the listener was established before the event was caused.

Client Perspective

From the client’s perspective, the protocol consist of

  1. connect to the server socket
  2. receive mzero from the server
  3. concurrently send and receive length-delimited (via msize) notification messages mpayload
  4. shutdown; one of:
    1. error shutdown
      1. error in send or receive operation
      2. close socket
    2. passive orderly shutdown
      1. “end of file” in receive operation
      2. shutdown socket for writing
      3. close socket
    3. active orderly shutdown
      1. termination request from client program
      2. shutdown socket for writing
      3. wait for “end of file” from server
      4. close socket

digraph client_states {
fontname=Arial
fontsize=11
node [fontsize=11,fontname=Arial]
edge [fontsize=11,fontname=Arial]

"new";
"setup-handshake";
"established";
"shutdown-handshake-active" [label="shutdown-handshake[active]"];
"shutdown-handshake-passive" [label="shutdown-handshake[passive]"];
"closed";
"new" -> "setup-handshake";
"setup-handshake" -> "established" [label="receive() : m_zero"];
"setup-handshake" -> "closed" [label="error"];
"established" -> "closed" [label="error"];
"established" -> "shutdown-handshake-active" [label="program shutdown => shutdown(socket)"];
"shutdown-handshake-active" -> "closed" [label="error | receive() : end-of-file"];
"established" -> "shutdown-handshake-passive" [label="receive() : end-of-file"];
"shutdown-handshake-passive" -> "closed" [label="error | shutdown(socket)"];

subgraph cluster_established_send {
  label="sending states when in state \"established\""
  "established-send" [label="established"];
  "size-sent";
  "closed-send" [label="closed"];
  "established-send" -> "size-sent" [label="send(m_size)"];
  "size-sent" -> "established-send" [label="send(m_payload)"];
  "established-send" -> "closed-send" [label="error"];
  "size-sent" -> "closed-send" [label="error"];
};

subgraph cluster_established_receive {
  label="receiving states when in state \"established\""
  "established-receive" [label="established"];
  "size-received";
  "closed-receive" [label="closed"];
  "shutdown-handshake-receive" [label="shutdown-handshake[passive]"];
  "established-receive" -> "size-received" [label="receive() : m_size"];
  "size-received" -> "established-receive" [label="receive() : m_payload"];
  "established-receive" -> "closed-receive" [label="error"];
  "size-received" -> "closed-receive" [label="error"];
  "established-receive" -> "shutdown-handshake-receive" [label="receive() : end-of-file"];
};
}

Server Perspective

The server establishes a listening TCP socket on the configured port. When a connection is accepted, the server continues to accept other connections and concurrently performs the following protocol on the new connection:

  1. accept client connection
  2. send mzero in to the client
  3. concurrently send and received notifications using length-delimited encoding via msize and mpayload
  4. shutdown; one of:
    1. error shutdown
      1. error in send or receive operation
      2. close socket
    2. passive orderly shutdown
      1. “end of file” in receive operation
      2. shutdown socket for writing
      3. close socket
    3. active orderly shutdown
      1. termination request from client program
      2. shutdown socket for writing
      3. wait for “end of file” from client
      4. close socket

digraph server_states {
fontname=Arial
fontsize=11
node [fontsize=11,fontname=Arial]
edge [fontsize=11,fontname=Arial]

"new";
"setup-handshake";
"established";
"shutdown-handshake-active" [label="shutdown-handshake[active]"];
"shutdown-handshake-passive" [label="shutdown-handshake[passive]"];
"closed";
"new" -> "setup-handshake";
"setup-handshake" -> "established" [label="send(m_zero)"];
"setup-handshake" -> "closed" [label="error"];
"established" -> "closed" [label="error"];
"established" -> "shutdown-handshake-active" [label="program shutdown => shutdown(socket)"];
"shutdown-handshake-active" -> "closed" [label="error | receive() : end-of-file"];
"established" -> "shutdown-handshake-passive" [label="receive() : end-of-file"];
"shutdown-handshake-passive" -> "closed" [label="error | shutdown(socket)"];

subgraph cluster_established_send {
  label="sending states when in state \"established\""
  "established-send" [label="established"];
  "size-sent";
  "closed-send" [label="closed"];
  "established-send" -> "size-sent" [label="send(m_size)"];
  "size-sent" -> "established-send" [label="send(m_payload)"];
  "established-send" -> "closed-send" [label="error"];
  "size-sent" -> "closed-send" [label="error"];
};

subgraph cluster_established_receive {
  label="receiving states when in state \"established\""
  "established-receive" [label="established"];
  "size-received";
  "closed-receive" [label="closed"];
  "shutdown-handshake-receive" [label="shutdown-handshake[passive]"];
  "established-receive" -> "size-received" [label="receive() : m_size"];
  "size-received" -> "established-receive" [label="receive() : m_payload"];
  "established-receive" -> "closed-receive" [label="error"];
  "size-received" -> "closed-receive" [label="error"];
  "established-receive" -> "shutdown-handshake-receive" [label="receive() : end-of-file"];
};
}

Example

# handshake
S -> C 0x00 0x00 0x00 0x00 0x00
# established
C -> S 0x23 0x00 0x00 0x00                          # 35-byte payload follows
C -> S 0x12 0x34 0x56 0x78 0x9a ...                 # 35-byte payload blob
C -> S 0x03 0x00 0x00 0x00                          # 3-byte payload follows
C -> S 0x12 0x34 0x56                               # 3-byte payload blob
...
# shutdown
C -> S end-of-file
S -> C end-of-file

Implementations

Language File(s)
C++ “0.11” branch of https://code.cor-lab.org/git/rsb.git.cpp at src/rsb/transport/socket
Java “0.11” branch of https://code.cor-lab.org/git/rsb.git.java at src/rsb/transport/socket
Python /../rsb-python/rsb/transport/socket/__init__.py
Common Lisp “0.11” branch of https://code.cor-lab.org/git/rsb.git.cl at src/transport/socket