Skip to end of metadata
Go to start of metadata


Sockets have been a part of the iOS networking infrastructure for over a year, and while their implementation has been satisfactory enough to allow users to add more advanced networking support beyond single-session HTTP requests to their products, there is room for improvement. Currently there are many problems with the iOS implementation of TCP sockets, most notabily with data chunking, the usage of an event-based system, insufficient distinction between 'listening' (accepts incoming connections) and 'connecting' (represents outbound connection) types, and inconsistent representation of sockets (some are objects, some are BSD file descriptors).

With the advent of sockets for Android, this is an ideal opportunity to address these issues and make sockets able to integrate future technologies, such as I/O streaming (or to represent an I/O stream themselves as a ducktype) and zero-configuration networking support for Android.

Where possible, comparisons will be drawn to the existing socket implementation on iOS and the proposed spec.

It should be noted that this spec is in flux based on the pending definition of the IO stream spec. Major changes are not expected, but further changes are possible.

Concepts and Definitions

  • Socket: A socket is essentially a data stream that is connected to a host/port pair.
  • Transport layer: The portion of a networking protocol which determines the parameters of data transmission (reliability, streaming, multiplexing, etc.) The two most common are UDP and TCP.
  • BSD sockets: The standard POSIX implementation of sockets.
  • INADDR_ANY: A specialized BSD host identifier for listening sockets which indicates that ALL available local network interfaces should be listened on. Practically, for us, this means sockets would listen on loopback (, wifi, and data network.
  • Listener: A socket which actively listens on a specified host/port for incoming connections.
  • Connector: A socket which connects to a specified host/port.

Throughout this document, except where specified, the word "socket" indicates a socket that uses the TCP transport layer.

Basis of spec

The basis of the spec, from an outward facing functionality standpoint, is the BSD socket specification. Functions, where possible, correspond directly to their BSD counterparts.

The reason for this is to keep the interface simple, flexible, minimal, and familiar to developers who may already have experience with developing networking code. Events fired based on the socket lifecycle state (see below) provide the opportunity for more advanced handling than the standard C paradigm and allow a BSD-like interface to fit comfortably into Titanium.

There is precedence for this, as the iOS TCPSocket presents a stripped-down version of the BSD interface with some magic.


It is proposed that we create a new Ti.Network.Socket namespace within Ti.Network, which will house socket objects which correspond to different transport layers. The rationale for this is as follows:

  • Certain socket transport types (in particular, UDP) are incompatible with TCP from an interface standpoint. In particular, UDP sockets use a "datagram" model in which there are no listening/connecting sockets, and they do not behave as I/O streams.
  • The name Ti.Network.TCPSocket is currently reserved on iOS and cannot be deprecated until a later time (see below). This makes it impractical to use the Ti.Network namespace as a container for TCPSocket, UDPSocket, etc.
  • This allows us to reserve a specific namespace for implementation of further transport layers, and give users a convenient space to present any custom transport layers they implement.

Transport layer support

As per this spec, only Ti.Network.Socket.TCP (TCP support) will be initially available. There are plans to introduce UDP sockets, but that will be covered in a separate spec.

Socket lifecycle

A socket goes through three distinct states through its lifecycle, with one additional state to represent errors. 2a and 2b are mutually exclusive.

As follows:

1. INITIALIZED : The socket is ready to have either connect() or listen() called.
2a. CONNECTED : The socket is connected to its specified host/port. Set before the connected callback is called.
2b. LISTENING : The socket is listening on its specified host/port.
3. CLOSED : The socket has been cleaned up via a call to close(). A socket in this state may be re-initialized via a new call to connect() or listen().
4. ERROR : The socket encountered an error. A socket in this state may be re-initialized via a new call to connect() or listen(). The error callback is called if the socket was in either the LISTENING or CONNECTED state when the error occurred.  When a socket enters an ERROR state, the socket is closed.

Currently, iOS TCPSocket only has the concept of a socket being valid. Distinct states provide the user with more information regarding the socket lifecycle, give clearly defined points at which callbacks are triggered, and also allow us to prevent reuse of sockets (undesirable in the case of connections accepted by a listener).

Connecting (outbound) sockets

Connecting sockets are straightforward; they connect to the specified host/port endpoint and act as I/O streams (both read and write) to that endpoint. A user-created socket enters the CONNECTED state by calling connect() on it.

All operations other than listen() and accept() are valid on a connecting socket.

This is equivalent to how a connecting socket functions under iOS TCPSocket.

Listening (accepting) sockets

Listening sockets accept incoming connections. A user-created socket enters the LISTENING state by calling listen() on it. The user is responsible for manually accepting incoming connections with the accept() command, which flags the socket to accept the next incoming connection (however, unlike BSD, it does not block). The rationale behind giving users control over inbound connection acceptance is to allow fine-grained control over system resources; in particular, due to the limitations of mobile device network device speeds or the OS, the user may want to only allow a limited number of concurrent connections at one time.

A listening socket only may have the accept() and close() operations called on it.

This is distinctly different from how iOS TCPSocket handles listening right now. In particular, iOS listening sockets both auto accept all incoming connections and act as a "hub" for all connections, rather than creating distinct socket objects for them; this introduces peculiarities such as associating file descriptors, rather than objects, with read/write data, and allowing the listener to "broadcast" information to all of its connected sockets. No such functionality will exist in the new socket implementation.

This current implementation is inefficient, cumbersome, presents a fragmented interface, and does not conform to what developers with network experience would consider a standard socket interface. These are all excellent reasons for changing existing behavior.

Accepted sockets

Sockets which are connecting to the local host as an endpoint are created when a listening socket accepts a new connection. These sockets arrive in the CONNECTED state and are functionally equivalent to outbound sockets.

Currently, iOS TCPSocket represents inbound sockets as file descriptors. This is unacceptable.

Changes to existing Ti.Network.TCPSocket on iOS

For the present, Ti.Network.TCPSocket will remain as-is on iOS, and continue to be the only way to interface with Ti.Network.BonjourClient and Ti.Network.BonjourBrowser as these are iOS-only features. For release 1.7.0 Ti.Network.TCPSocket will not become deprecated (due to it being the exclusive way to interface with other iOS-only features). There will not be a Ti.Network.TCPSocket namespace alias backported to Android as these sockets behave in a fundamentally different manner.

Ti.Network.TCPSocket will not be removed until there is Bonjour/Zero-configuration networking support for Android. At this point it will become deprecated for removal in the following release.

Due to the fundamental differences in interface and operation, there is no plan to provide a transitional bridge from Ti.Network.TCPSocket to Ti.Network.Socket at any point on iOS.

Security issues

Presenting sockets to the world introduces a host of security issues, including but not limited to malicious data injection via a connection over the CDN (celluar data network) via a host listening on the IP address assigned to the radio (or, equivalently, via INADDR_ANY). In order to try and reduce the potency of these attacks, or the ability to conduct them, we should consider:

  • Revoking access to any connection that comes in directly to the CDN. NOTE: This is not necessarily feasible; socket implementations on both iOS and Android contain information about the originating host, but not necessarily the interface the connection came in over.
  • Disallowing INADDR_ANY
  • Limiting the possibility for standard attacks (i.e. buffer overflow)
  • Providing specialized training for end developers specifically for advanced network programming

It is worth noting that on iOS, an application listening over the CDN is considered grounds for rejection.


As above, we do not support BSD's INADDR_ANY. References to it are included in the spec for historical reasons and to clarify why it is not supported. In particular, we have no way to filter connections (inbound or outbound) based on the CDN, and INADDR_ANY is global with no scoping, meaning we can't have it translate to "all interfaces except for CDN." For these reasons, support for this feature is removed.

I/O Layer

I/O is intended to be handled entirely through Streams and Buffers. For this reason no I/O operations are specified in this document.

The I/O Layer is intended to only be available on CONNECTED sockets. I/O does not make sense on LISTENING sockets, and sockets in the ERROR state do not have an "active" buffer.

The current iOS TCPSocket does not present a unified I/O layer with any other interface, and also confuses availability of I/O on LISTENING and CONNECTED sockets, and handles it in a clumsy way.

Proposed API


  • Namespace
    • Ti.Network.Socket : Namespace for all socket types and related constants.


  • Properties
    • INITIALIZED : Constant representing the "initialized" state a socket
    • CONNECTED : Constant representing the "connected" state for a socket
    • LISTENING : Constant representing the "listening" state for a socket
    • CLOSED : Constant representing the "closed" state for a socket
    • ERROR : Constant representing the "error" state for a socket
  • Functions
    • Ti.Network.Socket.TCP createTCP(Object args)}} : Creates a new TCP socket.
    • Ti.Network.Socket.UDP createUDP(Object args)}} : Creates a new UDP socket. RESERVED; not intended to be implemented immediately.


While not currently used in this proposal, the "options" property name would be reserved for setting socket options in the future.

  • Properties
    • host : The host to connect to. Cannot be modified when not in the INITIALIZED state. Supports both IPv4 and IPv6.
    • port : The port to connect to. Cannot be modified when not in the INITIALIZED state.
    • listenQueueSize : Max number of pending incoming connections to be allowed when listen() is called. Any incoming connections received while the max number of pending connections has been reached will be rejected.
    • timeout : The timeout for connect() and all I/O write() operations. Cannot be modified when not in the INITIALIZED state.
    • options : Options for the socket (such as reuse, multicast, etc.) RESERVED; not to be implemented immediately.
    • connected : The callback to be fired after the socket enters the "connected" state. Only invoked following a successful connect()call.
      • Argument parameters:
        • socket : The socket which was connected
    • error : The callback to be fired after the socket enters the ERRORstate.
      • Argument parameters:
        • socket : The socket that experienced the error
        • error : A stringified description of the error
        • errorCode : The error code of the error (potentially system-dependent)
    • accepted: The callback to be fired when a listener accepts a connection.
      • Argument parameters:
        • socket : The socket which received the connection
        • inbound : A Ti.Network.Socket object which represents the inbound connection; this should be considered a "connected" socket and is created in the CONNECTED state.
    • state[spe:read-only] : The current state of the socket.
  • Functions
    • void connect() : Attempts to connect the socket to its host/port. Throws exception if the socket is in a CONNECTED or LISTENING state. Throws exception if a valid host and port has not been set on the proxy. Nonblocking; connection attempts are asynchronous.
    • void listen() : Attempts to start listening on the socket's host/port. listen() call will attempt to listen on the specified host and/or port property for the socket if they are set. This function blocks execution and throws an exception on error (and sets the socket state to ERROR) but does not fire the error callback in this event. Throws exception if the socket is in a LISTENING or CONNECTED state.
    • void accept(Object params) : Tells a LISTENING socket to accept a connection request at the top of a listener's request queue when one becomes available. Takes an argument, a box object which assigns callbacks to the created socket. Note that the connected callback is not called (the socket does not "transition to" the CONNECTED state - it's created in the CONNECTED state) on the newly created socket.  The accepted callback is called when a new connection is accepted as a result of calling accept().  If the socket is already flagged to accept the next connection, the existing accept options will be update to use the newly specified options object. Throws an exception if the socket is not in a LISTENING state.
    • void close() : Closes a socket. Throws exception if the socket is not in a CONNECTED or LISTENING state. Blocking.

Pseudo Code Examples

Create a socket to connect
Create a socket to listen
Current KS->Platform->Sockets example, using new sockets


  1. I am pretty happy with this first draft. I do have a few comments/suggestions...

    I would prefer using an event emitter model for dispatching connected, error, accepted, etc events.

    It follows our current model of dispatching events and allows for multiple listeners per event.


    Adding event listener to socket

    I don't think for any of the callbacks we don't need the socket emitting the event included w/ the arguments. This can be fetched

    from the closure's scope or maybe from this.

    For the error event there should be 1 parameter which is an exception object from which you can get the description text, error code, etc.

    NOTE: EventEmitter.on() is just a short cut for addEventListener() we have on Desktop we borrowed from Node.js. Saves a bit of typing and reads nicely. :)

    The read event probably does not need the length as an additional parameter. We should just pass the buffer/blob from which you can query the length.

    The accepted event I think we should just pass the accepted socket as the argument.

    Overall socket example
    1. The original iOS TCPSocket used the eventing model (addEventListener('read',f(e)), "readError', 'writeError', etc.) and it was determined early on in the spec process that eventing presents too many problems and that we should NOT allow multiple data readers.  This is partially to conform to the emerging I/O spec.

    2. Stephen already mentioned the multiple listeners for sockets, but aside from the complexity there are usage concerns that make this less than desirable for sockets.  If you have multiple listeners handling close or accepted events then you start running into race conditions and other synchronization issues.  Ultimately, the outlined behavior is inline with how sockets are generally handled and should help to cut down on problems that will arise when developers start using multiple listeners for socket activity.

      The "on()" style while just an alias, is still not inline with how the rest of callbacks in mobile work with the "addEventListener()".  I think we should keep this consistent with the rest of mobile.

      For read(), this call is part of the stream spec (sockets will implement the stream interface) which is designed to allow the developer to read a specified amount of data since they may have a large buffer and only want to read a smaller chunk of data into their overall buffer at a time.  There are actually several overloaded versions of read() defined in the stream spec (including the variant you mention) that will provide the developer with the flexibility to handle the read operations however they need.  I know this is slightly confusing since the Stream spec is not yet finalized (in process) but this spec will be updated once the stream spec if finalized in order to accurately represent the read / write operations.

      When the accepted callback is invoked we specify the socket so that the socket in question is clear to the developer in the source and the documentation.  This clarity grows in value with the accepted callback since there are two sockets references that are returned.

  2. This looks good -- a few follow up questions:

    •  Is the "type" attribute for createSocket optional (i.e. is TCP the default?). If not, can it be?
    • This isn't specifically called out but I wanted to verify -- specifying "port: 0" in createSocket will listen on the first available socket correct?
    1. We can easily make it so that both of these are supported.  Good suggestions.

  3. For IOS platforms, the use of INADDR_ANY should be restricted to loopback or wifi only, not the Cellular Data Network as this is grounds for rejection from the app store.

    1. This was a major concern of mine, but INADDR_ANY does not allow fine-grained control like this (it would involve "magic detection" of all available networks that aren't the CDN - we want to keep behavior like this to a minimum, and also this is not the BSD definition of INADDR_ANY).  Instead what we'll have to do is simply remove the property from the spec, as proposed as an alternative.