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.