1tp
One transport protocol to rule them all -- offering net socket abstraction on top of various communication protocols
Summary
Goal of 1tp is to offer a single solution for connecting any two available endpoints -- public, private, mobile, located behind NAT boxes, ... To accomplish this, 1tp acts as a wrapper around various existing transports such as UDP, TCP, WebRTC and TURN (more to come in later releases). The internal details of these transports are concealed via an API similar to node's net API, using sockets and streams. Furthermore, 1tp always tries to use the 'cheapest' transport when establishing a connection between two endpoints. If both endpoints are sharing the same network, for instance, then 1tp will setup a UDP or a TCP connection. If two endpoints are located behind symmetric NAT boxes, then 1tp will negotiate a TURN or a WebRTC session via a shared relay server.
Features
- stream based API, highly inspired by node's net API
- current version includes UDP, TCP, WebRTC and TURN connectors -- extending UDP with hole punching + integrating other transports such as websockets, GCM and tor is WiP.
- connection setup mechanism tries to select the 'cheapest' transport -- using different schedulers to execute connection handshakes sequential (default) or in parallel (experimental)
- can be browserified (to be used in chrome and cordova apps)
Install
npm install 1tp
Usage
'use strict'
var onetp = require('1tp')
var net = onetp.net
var UdpTransport = onetp.transports.udp
var TcpTransport = onetp.transports.tcp
var TurnTransport = onetp.transports.turn
var WebRtcTransport = onetp.transports.webrtc
var LocalSignaling = onetp.signaling.local
var localSignaling = new LocalSignaling()
var WebSocketSignaling = onetp.signaling.websocket
var serverTransports = []
serverTransports.push(new UdpTransport())
serverTransports.push(new TcpTransport())
serverTransports.push(new TurnTransport({
turnServer: IP_ADDRESS,
turnPort: PORT,
turnUsername: USERNAME,
turnPassword: PASSWORD,
signaling: new WebSocketSignaling({
url: ONETP_REGISTRAR
})
}))
serverTransports.push(new WebRtcTransport({
config: { iceServers: [ { url: 'stun:stun.l.google.com:19305' } ] },
signaling: new WebSocketSignaling({
url: ONETP_REGISTRAR
})
}))
var onetpServer = net.createServer(serverTransports, function (connection) {
console.log('server connection established')
connection.pipe(connection)
})
onetpServer.on('listening', function () {
var clientTransports = []
clientTransports.push(new UdpTransport())
clientTransports.push(new TcpTransport())
clientTransports.push(new TurnTransport({
turnServer: IP_ADDRESS,
turnPort: PORT,
turnUsername: USERNAME,
turnPassword: PASSWORD,
signaling: new WebSocketSignaling({
url: ONETP_REGISTRAR
})
}))
clientTransports.push(new WebRtcTransport({
config: { iceServers: [ { url: 'stun:23.21.150.121' } ] }
signaling: new WebSocketSignaling({
url: ONETP_REGISTRAR
})
}))
var onetpClient = net.createConnection(onetpServer.address(), clientTransports, function () {
console.log('client connection established')
onetpClient.write('hello world')
})
onetpClient.on('data', function (data) {
console.log(data.toString())
})
})
onetpServer.listen()
API
var server = new Server([transports][, connectionListener])
Create a new 1tp server instance.
The optional connectionListener
argument is automatically set as a listener for the connection
event.
The transports
argument specifies an optional array of transport protocols this server instance must activate. The current implementation of 1tp includes the following transports (see also example above):
onetp.transports.udp
-- creates UDP dgram socketonetp.transports.tcp
-- creates TCP net server socketonetp.transports.turn
-- creates TURN socketonetp.transports.webrtc
-- creates WebRTC socket
onetp.transports.udp
and onetp.transports.tcp
don't require additional attributes. onetp.transports.turn
, in contrast, accepts the following specs:
turnServer
(mandatory): IP address of the TURN server to interact withturnPort
(mandatory): port number of that TURN serverturnUserName
(mandatory): username to access this TURN serverturnPassword
(mandatory): user password to access TURN serverturnProtocol
(optional): transport protocol to interact with TURN server -- default is UDP, see example for using TCP insteadsignaling
(mandatory): specify which signaling server to use (loopback or 1tp-registrar). When using 1tp-registrar), you need to specify the URL of the server
onetp.transports.webrtc
also requires to specify what signaling
server it should use. Additionally, this transports accepts all simple-peer (data channel) options.
When creating a server instance without specifying which transports to use, 1tp
- activates the transports that are compatible with the runtime -- see compatibility table below, and
- activates TURN if environment variables
TURN_ADDR
, TURN_PORT
, TURN_USER
, TURN_PASS
and ONETP_REGISTRAR
are all set
server.listen([listeningInfo][, callback])
Instruct the 1tp server to begin accepting connections.
The listeningInfo
parameter specifies an optional array of transport binding attributes. 1tp passes these parameters to the associated transport protocols when instructing them to begin accepting connections. The example below illustrates a listeningInfo
object informing 1tp to bind its udp transport to 127.0.0.1/20000 and its tcp transports to 127.0.0.1/20001
[{
transportType: 'udp',
transportInfo: {
address: '127.0.0.1',
port: 20000
}
}, {
transportType: 'tcp',
transportInfo: {
address: '127.0.0.1',
port: 20001
}
}]
Mind that listeningInfo
data is indicative. If the listen operation fails due to incorrect listeningInfo
data (such as an unavailable address), then the 1tp server retries executing this operation without listeningInfo
.
The optional callback
argument is automatically set as a listener for the listening
event.
server.listenP([listeningInfo])
Instruct the 1tp server to begin accepting connections. Instead of firing a listening
event, this function returns a promise that gets fulfilled once all registered transport protocols are accepting connections. This promise returns the same connectionInfo
as server.address()
-- see below
server.address()
Return the connectionInfo
generated by the active transports. This connectionInfo
is an array of transport specific endpoint information. The example below illustrates the connectionInfo
address of a 1tp server
[ { transportType: 'udp',
transportInfo: { address: '192.168.1.30', port: 61773 },
version: '0.17.4' },
{ transportType: 'udp',
transportInfo: { address: '192.168.241.1', port: 61773 },
version: '0.17.4' },
{ transportType: 'tcp',
transportInfo: { address: '192.168.1.30', port: 61564 },
version: '0.17.4' },
{ transportType: 'tcp',
transportInfo: { address: '192.168.241.1', port: 61564 },
version: '0.17.4' },
{ transportType: 'turn-tcp',
transportInfo:
{ type: 'websocket-signaling',
uid: '1636e5e5437eb1733e1d22d21a50e478',
url: 'http://1.2.3.4/' },
version: '0.17.4' } ]
server.close()
Stops the server from accepting new connections and keeps existing connections. This function is asynchronous, the server is finally closed when all connections are ended and the server emits a 'close' event. The optional callback will be called once the 'close' event occurs. [copied from nodejs docs]
var socket = new Socket([transports][, args])
Create a new 1tp socket (client) object.
The optional transports
argument specifies which transport protocols this socket should use to establish a connection with a 1tp server. See above for more details about the specification of these transport protocols.
The optional args
object includes additional flags that specify the behavior of this socket. Currently, only the parallelConnectionSetup
flag is supported, instructing the socket to schedule all connection attempts in parallel rather than sequential (which is the default behavior). Note that this is still a highly experimental feature.
socket.connect(connectionInfo[, connectListener])
Setup a connection with a 1tp server.
The connectionInfo
argument specifies the 1tp server to connect with. As specified above, this connectionInfo
object is a collection of transport specific endpoint information.
The optional connectListener
argument is automatically set as a listener for the connect
event.
socket.isConnected()
Returns true if one of the transport protocols has established a connection with a 1tp server.
socket.remoteAddress
Contains the connectionInfo
of the connected peer.
socket.destroy()
Closes the socket, no more communication possible after completing this operation. Emits a close
event when connection is closed.
socket.end()
Half-closes the socket, server may still end some data.
socket.setTimeout(timeout[, callback])
Sets the socket to timeout after timeout
milliseconds of inactivity. The socket then fires a timeout
event.
If timeout
is 0, then the existing idle timeout is disabled.
The optional callback
is automatically set as a one time listener for the timeout
event.
socket.write(data[, encoding][, callback])
Send data on the socket.
net.createServer([transports][, connectionListener])
Create and return a new 1tp server instance.
The transports
argument specifies an optional array of transport protocols this server instance must activate. See above for more details.
The optional connectionListener
argument is automatically set as a listener for the connection
event.
net.createConnection(connectionInfo[, transports][, args][, connectListener])
Create and return a new 1tp socket instance.
The connectionInfo
argument specifies the end-point to connect with. As specified above, this connectionInfo
is an array of transport specific endpoint information.
The optional transports
argument specifies which transport protocols this socket should use to establish a connection with a 1tp server. See above for more details about the specification of these transport protocols.
The optional args
object includes additional flags that specify the behavior of this socket. See above for more details about this option.
The optional connectListener
argument is automatically set as a listener for the connect
event.
Events
server.on('connection', function(socket) {})
Emitted when a new connection is made. socket
is an instance of 1tp's net.Socket
.
server.on('error', function(error) {})
Emitted when an error occurs.
server.on('listening', function() {})
Emitted once all registered transport protocols are accepting connections after calling server.listen
.
socket.on('connect', function() {})
Emitted when a socket connection is successfully established -- i.e. one of the transport protocols has established a connection with a 1tp server.
socket.on('data', function(data) {})
Emitted when data is received. The data
argument is a Buffer.
socket.on('end', function() {})
Emitted when the connected socket has ended its write stream.
socket.on('finish', function() {})
Emitted when there is no more data to be consumed from the socket's read stream.
socket.on('close', function() {})
Emitted once the socket is fully closed -- after executing socket.destroy
socket.on('error', function(error) {})
Emitted when an error occurs.
socket.on('timeout', function() {})
Emitted if the socket times out from inactivity -- notifying that the socket has been idle.
Chrome and cordova apps
gulp browserify [--production]
Creates 1tp.debug.js
and 1tp.min.js
in build
folder, which can be used in chrome and cordova apps. When integrating 1tp in a cordova app, use the cordova-plugin-chrome-apps-sockets-udp
and cordova-plugin-chrome-apps-system-network
plugins and cordova-plugin-chrome-apps-sockets-tcp
and cordova-plugin-chrome-apps-sockets-tcpserver plugins
(tcp cordova plugins generate errors):
cordova plugin add cordova-plugin-chrome-apps-sockets-udp
cordova plugin add cordova-plugin-networkinterface
Compatibility -- current status
| UDP | TCP | TURN+UDP | TURN+TCP | WebRTC |
---|
node.js x86 | + | + | + | + | + |
node.js arm | + | + | + | + | - |
chrome browser | - | - | - | - | + |
chrome app | + | + | + | + | + |
cordova android | + | - | + | - | + |
cordova ios | + | - | + | - | - |
Examples
See examples directory.