Table Of Contents
Table Of Contents

Using Sockets

Overview

The INET Socket API provides special C++ abstractions on top of the standard OMNeT++ message passing interface for several communication protocols.

Sockets are most often used by applications and routing protocols to acccess the corresponding protocol services. Sockets are capable of communicating with the underlying protocol in a bidirectional way. They can assemble and send service requests and packets, and they can also receive service indications and packets.

Applications can simply call the socket class member functions (e.g. bind(), connect(), send(), close()) to create and configure sockets, and to send and receive packets. They may also use several different sockets simulatenously.

The following sections first introduce the shared functionality of sockets, and then list all INET sockets in detail, mostly by shedding light on many common usages through examples.

Note

Code fragments in this chapter have been somewhat simplified for brevity. For example, some virtual modifiers and override qualifiers have been omitted, and some algorithms have been simplified to ease understanding.

Socket Interfaces

Although sockets are always implemented as protocol specific C++ classes, INET also provides C++ socket interfaces. These interfaces allow writing general C++ code which can handle many different kinds of sockets all at once.

For example, the ISocket interface is implemented by all sockets, and the INetworkSocket interface is implemented by all network protocol sockets.

Identifying Sockets

All sockets have a socket identifier which is unique within the network node. It is automatically assigned to the sockets when they are created. The identifier can accessed with getSocketId() throughout the lifetime of the socket.

The socket identifier is also passed along in SocketReq and SocketInd packet tags. These tags allow applications and protocols to identify the socket to which Packet’s, service Request’s, and service Indication’s belong.

Configuring Sockets

Since all sockets work with message passing under the hoods, they must be configured prior to use. In order to send packets and service requests on the correct gate towards the underlying communication protocol, the output gate must be configured:

socket.setOutputGate(gate("socketOut")); // configure socket output gate
socket.setCallback(this); // set callback interface for message processing

In contrast, incoming messages such as service indications from the underlying communication protocol can be received on any application gate.

To ease application development, all sockets support storing a user specified data object pointer. The pointer is accessible with the setUserData(), getUserData() member functions.

Another mandatory configuration for all sockets is setting the socket callback interface. The callback interface is covered in more detail in the following section.

Other socket specific configuration options are also available, these are discussed in the section of the corresponding socket.

Callback Interfaces

To ease centralized message processing, all sockets provide a callback interface which must be implemented by applications. The callback interface is usually called ICallback, and it’s defined as an inner class of the socket it belongs to. These interfaces often contain some generic notification methods along with several socket specific methods.

For example, the most common callback method is the one which processes incoming packets:

class ICallback // usually the inner class of the socket
{
    void socketDataArrived(ISocket *socket, Packet *packet);
};

Processing Messages

In general, sockets can process all incoming messages which were sent by the underlying protocol. The received messages must be processed by the socket where they belong to.

For example, an application can simply go through each knonwn socket in any order, and decide which one should process the received message as follows:

if (socket.belongsToSocket(message)) // match message and socket
    socket.processMessage(message); // invoke callback interface

Sockets usually deconstruct the received messages and update their state accordingly if necessary. They also automatically dispatch received packets and service indications for further processing to the appropriate functions in the corresponding ICallback interface.

Sending Data

All sockets provide one or more send() functions which send packets using the current configuration of the socket. The actual means of packet delivery depends on the underlying communication protocol, but in general the state of the socket is expected to affect it.

For example, after the socket is properly configured, the application can start sending packets without attaching any tags, because the socket takes care of the necessary technical details:

socket.send(packet); // by means of the underlying communication protocol

Receiving Data

For example, the application may directly implement the ICallback interface of the socket and print the received data as follows:

class App : public cSimpleModule, public ICallback
{
    void socketDataArrived(ISocket *socket, Packet *packet);
};

void App::socketDataArrived(ISocket *socket, Packet *packet)
{
    EV << packet->peekData() << endl;
}

Closing Sockets

Sockets must be closed before deleting them. Closing a socket allows the underlying communication protocol to release allocated resources. These resources are often allocated on the local network node, the remote nework node, or potentially somewhere else in the network.

For example, a socket for a connection oriented protocol must be closed to release the allocated resources at the peer:

socket.close(); // release allocated local and remote network resources

Using Multiple Sockets

If the application needs to manage a large number of sockets, for example in a server application which handles multiple incoming connections, the generic SocketMap class may be useful. This class can manage all kinds of sockets which implement the ISocket interface simultaneously.

For example, processing an incoming packet or service indication can be done as follows:

auto socket = socketMap.findSocketFor(message); // lookup socket to process
socket->processMessage(message); // dispatch message to callback interface

In order for the SocketMap to operate properly, sockets must be added to and removed from it using the addSocket() and removeSocket() methods respectively.

UDP Socket

The UdpSocket class provides an easy to use C++ interface to send and receive UDP datagrams. The underlying UDP protocol is implemented in the Udp module.

Callback Interface

Processing packets and indications which are received from the Udp module is pretty simple. The incoming message must be processed by the socket where it belongs as shown in the general section.

The UdpSocket deconstructs the message and uses the UdpSocket::ICallback interface to notify the application about received data and error indications:

class ICallback // inner class of UdpSocket
{
    void socketDataArrived(UdpSocket *socket, Packet *packet);
    void socketErrorArrived(UdpSocket *socket, Indication *indication);
};

Configuring Sockets

For receiving UDP datagrams on a socket, it must be bound to an address and a port. Both the address and port is optional. If the address is unspecified, than all UDP datagrams with any destination address are received. If the port is -1, then an unused port is selected automatically by the Udp module. The address and port pair must be unique within the same network node.

Here is how to bind to a specific local address and port to receive UDP datagrams:

socket.bind(Ipv4Address("10.0.0.42"), 42); // local address/port

For only receiving UDP datagrams from a specific remote address/port, the socket can be connected to the desired remote address/port:

socket.connect(Ipv4Address("10.0.0.42"), 42); // remote address/port

There are several other socket options (e.g. receiving broadcasts, managing multicast groups, setting type of service) which can also be configured using the UdpSocket class:

socket.setTimeToLive(16); // change default TTL
socket.setBroadcast(true); // receive all broadcasts
socket.joinMulticastGroup(Ipv4Address("224.0.0.9")); // receive multicasts

Sending Data

After the socket has been configured, applications can send datagrams to a remote address and port via a simple function call:

socket.sendTo(packet42, Ipv4Address("10.0.0.42"), 42); // remote address/port

If the application wants to send several datagrams, it can optionally connect to the destination.

The UDP protocol is in fact connectionless, so when the Udp module receives the connect request, it simply remembers the remote address and port, and use it as default destination for later sends.

socket.connect(Ipv4Address("10.0.0.42"), 42); // remote address/port
socket.send(packet1); // send packets via connected socket
// ...
socket.send(packet42);

The application can call connect several times on the same socket.

Receiving Data

For example, the application may directly implement the UdpSocket::ICallback interface and print the received data as follows:

class UdpApp : public cSimpleModule, public UdpSocket::ICallback
{
    void socketDataArrived(UdpSocket *socket, Packet *packet);
};

void UdpApp::socketDataArrived(UdpSocket *socket, Packet *packet)
{
    EV << packet->peekData() << endl;
}

TCP Socket

The TcpSocket class provides an easy to use C++ interface to manage TCP connections, and to send and receive data. The underlying TCP protocol is implemented in the Tcp, TcpLwip, and TcpNsc modules.

Callback Interface

Messages received from the various Tcp modules can be processed by the TcpSocket where they belong to. The TcpSocket deconstructs the message and uses the TcpSocket::ICallback interface to notify the application about the received data or service indication:

class ICallback // inner class of TcpSocket
{
    void socketDataArrived(TcpSocket* socket, Packet *packet, bool urgent);
    void socketAvailable(TcpSocket *socket, TcpAvailableInfo *info);
    void socketEstablished(TcpSocket *socket);
    // ...
    void socketClosed(TcpSocket *socket);
    void socketFailure(TcpSocket *socket, int code);
};

Configuring Connections

The Tcp module supports several TCP different congestion algorithms, which can also be configured using the TcpSocket:

socket.setTCPAlgorithmClass("TcpReno");

Upon setting the individual parameters, the socket immediately sends sevice requests to the underlying Tcp protocol module.

Setting up Connections

Since TCP is a connection oriented protocol, a connection must be established before applications can exchange data. On the one side, the application listens at a local address and port for incoming TCP connections:

socket.bind(Ipv4Address("10.0.0.42"), 42); // local address/port
socket.listen(); // start listening for incoming connections

On the other side, the application connects to a remote address and port to establish a new connection:

socket.connect(Ipv4Address("10.0.0.42"), 42); // remote address/port

Accepting Connections

The Tcp module automatically notifies the TcpSocket about incoming connections. The socket in turn notifies the application using the ICallback::socketAvailable method of the callback interface. Finally, incoming TCP connections must be accepted by the application before they can be used:

class TcpServerApp : public cSimpleModule, public TcpSocket::ICallback
{
    TcpSocket serverSocket; // server socket listening for connections
    SocketMap socketMap; // container for all accepted connections

    void socketAvailable(TcpSocket *socket, TcpAvailableInfo *info);
};

void TcpServerApp::socketAvailable(TcpSocket *socket, TcpAvailableInfo *info)
{
    auto newSocket = new TcpSocket(info); // create socket using received info
    // ...
    socketMap.addSocket(newSocket); // store accepted connection
    serverSocket.accept(info->getNewSocketId()); // notify Tcp module
}

After the connection is accepted, the Tcp module notifies the application about the socket being established and ready to be used.

Sending Data

After the connection has been established, applications can send data to the remote application via a simple function call:

socket.send(packet1);
// ...
socket.send(packet42);

Packet data is enqueued by the local Tcp module and transmitted over time according to the protocol logic.

Receiving Data

Receiving data is as simple as implementing the corresponding method of the TcpSocket::ICallback interface. One caveat is that packet data may arrive in different chunk sizes (but the same order) than they were sent due to the nature of TCP protocol.

For example, the application may directly implement the TcpSocket::ICallback interface and print the received data as follows:

class TcpApp : public cSimpleModule, public TcpSocket::ICallback
{
    void socketDataArrived(TcpSocket *socket, Packet *packet, bool urgent);
};

void TcpApp::socketDataArrived(TcpSocket *socket, Packet *packet, bool urgent)
{
    EV << packet->peekData() << endl;
}

SCTP Socket

The SctpSocket class provides an easy to use C++ interface to manage SCTP connections, and to send and receive data. The underlying SCTP protocol is implemented in the Sctp module.

Callback Interface

Messages received from the Sctp module can be processed by the SctpSocket where they belong to. The SctpSocket deconstructs the message and uses the SctpSocket::ICallback interface to notify the application about the received data or service indication:

class ICallback // inner class of SctpSocket
{
    void socketDataArrived(SctpSocket* socket, Packet *packet, bool urgent);
    void socketEstablished(SctpSocket *socket);
    // ...
    void socketClosed(SctpSocket *socket);
    void socketFailure(SctpSocket *socket, int code);
};

Configuring Connections

The SctpSocket class supports setting several SCTP specific connection parameters directly:

socket.setOutboundStreams(2);
socket.setStreamPriority(1);
socket.setEnableHeartbeats(true);
// ...

Upon setting the individual parameters, the socket immediately sends sevice requests to the underlying Sctp protocol module.

Setting up Connections

Since SCTP is a connection oriented protocol, a connection must be established before applications can exchange data. On the one side, the application listens at a local address and port for incoming SCTP connections:

socket.bind(Ipv4Address("10.0.0.42"), 42); // local address/port
socket.listen(true); // start listening for incoming connections

On the other side, the application connects to a remote address and port to establish a new connection:

socket.connect(Ipv4Address("10.0.0.42"), 42);

Accepting Connections

The Sctp module automatically notifies the SctpSocket about incoming connections. The socket in turn notifies the application using the ICallback::socketAvailable method of the callback interface. Finally, incoming SCTP connections must be accepted by the application before they can be used:

class SctpServerApp : public cSimpleModule, public SctpSocket::ICallback
{
    SocketMap socketMap;
    SctpSocket serverSocket;

    void socketAvailable(SctpSocket *socket, SctpAvailableInfo *info);
};

void SctpServerApp::socketAvailable(SctpSocket *socket, SctpAvailableInfo *info)
{
    auto newSocket = new SctpSocket(info);
    // ...
    socketMap.addSocket(newSocket);
    serverSocket.accept(info->getNewSocketId());
}

Sending Data

After the connection has been established, applications can send data to the remote applica- tion via a simple function call:

socket.send(packet1);
// ...
socket.send(packet42);

Packet data is enqueued by the local Sctp module and transmitted over time according to the protocol logic.

Receiving Data

Receiving data is as simple as implementing the corresponding method of the SctpSocket::ICallback interface. One caveat is that packet data may arrive in different chunk sizes (but the same order) than they were sent due to the nature of SCTP protocol.

For example, the application may directly implement the SctpSocket::ICallback interface and print the received data as follows:

class SctpApp : public cSimpleModule, public SctpSocket::ICallback
{
    void socketDataArrived(SctpSocket *socket, Packet *packet, bool urgent);
};

void SctpApp::socketDataArrived(SctpSocket *socket, Packet *packet, bool urgent)
{
    EV << packet->peekData() << endl;
}

IPv4 Socket

The Ipv4Socket class provides an easy to use C++ interface to send and receive IPv4 datagrams. The underlying IPv4 protocol is implemented in the Ipv4 module.

Callback Interface

Messages received from the Ipv4 module must be processed by the socket where they belong as shown in the general section. The Ipv4Socket deconstructs the message and uses the Ipv4Socket::ICallback interface to notify the application about the received data:

class ICallback // inner class of Ipv4Socket
{
    void socketDataArrived(Ipv4Socket *socket, Packet *packet);
};

Configuring Sockets

In order to only receive IPv4 datagrams which are sent to a specific local address or contain a specific protocol, the socket can be bound to the desired local address or protocol.

For example, the following code fragment shows how the INET PingApp binds to the ICMPv4 protocol to receive all incoming ICMPv4 Echo Reply messages:

socket.bind(&Protocol::icmpv4, Ipv4Address()); // filter for ICMPv4 messages

For only receiving IPv4 datagrams from a specific remote address, the socket can be connected to the desired remote address:

socket.connect(Ipv4Address("10.0.0.42")); // filter for remote address

Sending Data

After the socket has been configured, applications can immediately start sending IPv4 datagrams to a remote address via a simple function call:

socket.sendTo(packet, Ipv4Address("10.0.0.42")); // remote address

If the application wants to send several IPv4 datagrams to the same destination address, it can optionally connect to the destination:

socket.connect(Ipv4Address("10.0.0.42")); // remote address
socket.send(packet1);
// ...
socket.send(packet42);

The IPv4 protocol is in fact connectionless, so when the Ipv4 module receives the connect request, it simply remembers the remote address, and uses it as the default destination address for later sends.

The application can call connect() several times on the same socket.

Receiving Data

For example, the application may directly implement the Ipv4Socket::ICallback interface and print the received data as follows:

class Ipv4App : public cSimpleModule, public Ipv4Socket::ICallback
{
    void socketDataArrived(Ipv4Socket *socket, Packet *packet);
};

void Ipv4App::socketDataArrived(Ipv4Socket *socket, Packet *packet)
{
    EV << packet->peekData() << endl;
}

IPv6 Socket

The Ipv6Socket class provides an easy to use C++ interface to send and receive IPv6 datagrams. The underlying IPv6 protocol is implemented in the Ipv6 module.

Callback Interface

Messages received from the Ipv6 module must be processed by the socket where they belong as shown in the general section. The Ipv6Socket deconstructs the message and uses the Ipv6Socket::ICallback interface to notify the application about the received data:

class ICallback // inner class of Ipv6Socket
{
    void socketDataArrived(Ipv6Socket *socket, Packet *packet);
};

Configuring Sockets

In order to only receive IPv6 datagrams which are sent to a specific local address or contain a specific protocol, the socket can be bound to the desired local address or protocol.

For example, the following code fragment shows how the INET PingApp binds to the ICMPv6 protocol to receive all incoming ICMPv6 Echo Reply messages:

socket.bind(&Protocol::icmpv6, Ipv6Address()); // filter for ICMPv6 messages

For only receiving IPv6 datagrams from a specific remote address, the socket can be connected to the desired remote address:

socket.connect(Ipv6Address("10:0:0:0:0:0:0:42")); // filter for remote address

Sending Data

After the socket has been configured, applications can immediately start sending IPv6 datagrams to a remote address via a simple function call:

socket.sendTo(packet, Ipv6Address("10:0:0:0:0:0:0:42")); // remote address

If the application wants to send several IPv6 datagrams to the same destination address, it can optionally connect to the destination:

socket.connect(Ipv6Address("10:0:0:0:0:0:0:42")); // remote address
socket.send(packet1);
// ...
socket.send(packet42);

The IPv6 protocol is in fact connectionless, so when the Ipv6 module receives the connect request, it simply remembers the remote address, and uses it as the default destination address for later sends.

The application can call connect() several times on the same socket.

Receiving Data

For example, the application may directly implement the Ipv6Socket::ICallback interface and print the received data as follows:

class Ipv6App : public cSimpleModule, public Ipv6Socket::ICallback
{
    void socketDataArrived(Ipv6Socket *socket, Packet *packet);
};

void Ipv6App::socketDataArrived(Ipv6Socket *socket, Packet *packet)
{
    EV << packet->peekData() << endl;
}

L3 Socket

The L3Socket class provides an easy to use C++ interface to send and receive datagrams using the conceptual network protocols. The underlying network protocols are implemented in the NextHopForwarding, Flooding, ProbabilisticBroadcast, and AdaptiveProbabilisticBroadcast modules.

Callback Interface

Messages received from the network protocol module must be processed by the associated socket where as shown in the general section. The L3Socket deconstructs the message and uses the L3Socket::ICallback interface to notify the application about the received data:

class ICallback // inner class of L3Socket
{
    void socketDataArrived(L3Socket *socket, Packet *packet);
};

Configuring Sockets

Since the L3Socket class is network protocol agnostic, it must be configured to connect to a desired network protocol:

L3Socket socket(&Protocol::flooding);

In order to only receive datagrams which are sent to a specific local address or contain a specific protocol, the socket can be bound to the desired local address or protocol. The conceptual network protocols can work with the ModuleIdAddress class which contains a moduleId of the desired network interface.

For example, the following code fragment shows how the INET PingApp binds to the Echo protocol to receive all incoming Echo Reply messages:

socket.bind(&Protocol::echo, ModuleIdAddress(42));

For only receiving datagrams from a specific remote address, the socket can be connected to the desired remote address:

socket.connect(ModuleIdAddress(42)); // filter for remote interface

Sending Data

After the socket has been configured, applications can immediately start sending datagrams to a remote address via a simple function call:

socket.sendTo(packet, ModuleIdAddress(42)); // remote interface

If the application wants to send several datagrams to the same destination address, it can optionally connect to the destination:

socket.connect(ModuleIdAddress(42)); // remote interface
socket.send(packet1);
//..
socket.send(packet42);

The network protocols are in fact connectionless, so when the protocol module receives the connect request, it simply remembers the remote address, and uses it as the default destination address for later sends.

The application can call connect() several times on the same socket.

Receiving Data

For example, the application may directly implement the L3Socket::ICallback interface and print the received data as follows:

class L3App : public cSimpleModule, public L3Socket::ICallback
{
    void socketDataArrived(L3Socket *socket, Packet *packet);
};

void L3App::socketDataArrived(L3Socket *socket, Packet *packet)
{
    EV << packet->peekData() << endl;
}

TUN Socket

The TunSocket class provides an easy to use C++ interface to send and receive datagrams using a TUN interface. The underlying TUN interface is implemented in the Tun module.

A TUN interface is basically a virtual network interface which is usually connected to an application (from the outside) instead of other network devices. It can be used for many networking tasks such as tunneling, or virtual private networking.

Callback Interface

Messages received from the Tun module must be processed by the socket where they belong as shown in the general section. The TunSocket deconstructs the message and uses the TunSocket::ICallback interface to notify the application about the received data:

class ICallback // inner class of TunSocket
{
    void socketDataArrived(TunSocket *socket, Packet *packet);
};

Configuring Sockets

A TunSocket must be associated with a TUN interface before it can be used:

socket.open(interface->getId());

Sending Packets

As soon as the TunSocket is associated with a TUN interface, applications can immediately start sending datagrams via a simple function call:

socket.send(packet);

When the application sends a datagram to a TunSocket, the packet appears for the protocol stack within the network node as if the packet were received from the network.

Receiving Packets

Messages received from the TUN interface must be processed by the corresponding TunSocket. The TunSocket deconstructs the message and uses the TunSocket::ICallback interface to notify the application about the received data:

class TunApp : public cSimpleModule, public TunSocket::ICallback
{
    void socketDataArrived(TunSocket *socket, Packet *packet);
};

void TunApp::socketDataArrived(TunSocket *socket, Packet *packet)
{
    EV << packet->peekData() << endl;
}

When the protocol stack within the network node sends a datagram to a TUN interface, the packet appears for the application which uses a TunSocket as if the packet were sent to the network.