cpp2ethernet - Ethernet Abstractions
Overview
cpp2ethernet
provides functionality to send and receive data through sockets. Also it contains features that help in managing network interfaces.
This module contains following features:
API for UDP and TCP socket handling
Wrapper for IP addresses
Registry for network interface handling
This module has the following dependencies:
common
Public API
UDP
AbstractDatagramSocket
The
AbstractDatagramSocket
is used to setup a UDP connection, reading data and sending data.
Receive: An application that is using an
AbstractDatagramSocket
and wants to receive UDP data shall implement theIDataListener
interface. Additionally,setDataListener()
has to be set with the implementedIDataListener
object. Also a connection has to be properly set up by callingbind()
orconnect()
. Once the UDP data is received, thedataReceived()
function will be called. The application is then responsible to read the data withread()
.It is also possible to receive multicasts by joining a multicast group with
join()
.Send: In order to send UDP data, there are two options.
connect()
andsend(slice<uint8_t& data)
send(DatagramPacket& packet)
enum { INVALID_PORT = 0xFFFF }; /** * all ErrorCodes used by socket * \enum ErrorCode */ enum class ErrorCode : uint8_t { /** everything ok */ UDP_SOCKET_OK, /** an error occurred */ UDP_SOCKET_NOT_OK, /** no IDataListener registerd */ UDP_SOCKET_NO_DATA_LISTENER }; AbstractDatagramSocket(); AbstractDatagramSocket(AbstractDatagramSocket const&) = delete; AbstractDatagramSocket& operator=(AbstractDatagramSocket const&) = delete; /** * Binds this UDPSocket to a specific port & local address * \param port local port * \return status of method * - UDP_SOCKET_OK when everything was fine * - UDP_SOCKET_NOT_OK when something went wrong */ virtual ErrorCode bind(ip::IPAddress const* pIpAddress, uint16_t port) = 0; virtual ErrorCode join(ip::IPAddress const& groupAddr) = 0; /** * Returns the binding state of the socket. * \return binding state of the socket * - false: not bound * - true: bound */ virtual bool isBound() const = 0; virtual void close() = 0; /** * Returns whether the socket is closed or not. * \return state of the socket * - false: not closed * - true: closed */ virtual bool isClosed() const = 0; /** * Connects the socket to a remote address for this socket. * \param address remote IPAddress * \param port remote port * \return status of method * - UDP_SOCKET_OK when everything was fine * - UDP_SOCKET_NOT_OK when something went wrong */ virtual ErrorCode connect(ip::IPAddress const& address, uint16_t port, ip::IPAddress* pLocalAddress) = 0; /** * Disconnects the socket */ virtual void disconnect() = 0; /** * Returns the connection state of the socket. * \return connection state of the socket * - false: not connected * - true: connected */ virtual bool isConnected() const = 0; /** * reads a given number of bytes from the socket * \param buffer buffer to receive data to. If 0L is passed the n * bytes should be skipped, i.e. the input stream has to advance by * n bytes. * \param n max number of bytes to receive * \return number of bytes really read * - 0: an error occurred reading from the socket * - else: bytes read */ virtual size_t read(uint8_t* buffer, size_t n) = 0; /** * sends an amount of data * \param data data to send * \return status of transmission * - UDP_SOCKET_OK when data was sent to UDP stack * - UDP_SOCKET_NOT_OK when socket has not been opened * \note * sending the data may be asynchronous. for this reason the * IDataSendNotificationListener has a appropriate callback. */ virtual ErrorCode send(::etl::span<uint8_t const> const& data) = 0; /** * Sends a DatagramPacket. * \return status of transmission * - UDP_SOCKET_OK when data was sent to UDP stack * - UDP_SOCKET_NOT_OK when socket has not been opened */ virtual ErrorCode send(DatagramPacket const& packet) = 0; /** * sets the listener to this socket instance * \param pListener IDataListener to attach */ void setDataListener(IDataListener* pListener); void setDataSentListener(IDataSentListener* pListener); /** * \return Local IPAddress, 0L if this operation is not allowed */ virtual ip::IPAddress const* getIPAddress() const = 0; virtual ip::IPAddress const* getLocalIPAddress() const = 0; /** * Returns remote port if connected, INVALID_PORT otherwise */ virtual uint16_t getPort() const = 0; virtual uint16_t getLocalPort() const = 0;
DatagramPacket
A
DatagramPacket
is a wrapper for UDP payload and its destination IP and destination port. Using aDatagramPacket
allows to send data over aAbstractDatagramSocket
without usingconnect()
./** * Constructs a UDP datagram packet. * \param data array of data * \param length length of data * \param address target IPAddress * \param port target port */ DatagramPacket(uint8_t const data[], uint16_t length, ::ip::IPAddress address, uint16_t port); DatagramPacket(uint8_t const data[], uint16_t length, ::ip::IPEndpoint const& endpoint); DatagramPacket(DatagramPacket const&) = delete; DatagramPacket& operator=(DatagramPacket const&) = delete; uint8_t const* getData() const { return _data; } uint16_t getLength() const { return _length; } ::ip::IPEndpoint getEndpoint() const { return _endpoint; } ::ip::IPAddress getAddress() const { return _endpoint.getAddress(); } uint16_t getPort() const { return _endpoint.getPort(); } bool operator==(DatagramPacket const& other) const;
IDataListener
This interface shall be used in order to receive UDP data.
dataReceived()
will be called once Data has arrived for the listening Socket.Note
If
dataReceived()
is called, the application is responsible to read the data. Depending on the Ethernet Driver implementation, not reading all data, which is received, could lead to packet drops, because of unfreed packets in the driver./** * callback that gets called when data has been received * \param length number of bytes received * * \note * It is expected that the data is read from the AbstractSocket within this * callback. The AbstractSocket may overwrite its internal buffers after * this callback is done. */ virtual void dataReceived( AbstractDatagramSocket& socket, ::ip::IPAddress sourceAddress, uint16_t sourcePort, ::ip::IPAddress destinationAddress, uint16_t length) = 0;
TCP
AbstractServerSocket
An
AbstractServerSocket
is the base class for server sockets that may open a specific port. To accept incoming connections theAbstractServerSocket
needs aISocketProvidingConnectionListener
. On an incoming connection request theAbstractServerSocket
requests anAbstractSocket
object from theISocketProvidingConnectionListener
to bind the connection to via itsgetSocket()
method. In case of success the listenersconnectionAccepted()
method is called passing the boundAbstractSocket
to the application./** * constructor taking the AbstractServerSockets port * \param port port of socket * \param providingListener ISocketProvidingConnectionListener which * provides the AbstractServerSocket with AbstractSocket objects * and gets notified when a connection is successfully established * * As long as the ISocketProvidingConnectionListener returns valid pointers * to AbstractSocket objects connections are accpeted and delegated to the * ISocketProvidingConnectionListener. When NULL is returned, the connection * is refused. */ AbstractServerSocket(uint16_t port, ISocketProvidingConnectionListener& providingListener); /** * \return port the AbstractServerSocket is listening for */ uint16_t getLocalPort() const { return _port; } void setPort(uint16_t const port) { _port = port; } /** * accepts a connection to the AbstractServerSockets port */ virtual bool accept() = 0; /** * Binds this server socket to a given netif. */ virtual bool bind(ip::IPAddress const& localIpAddress, uint16_t port) = 0; /** * closes the accepted port */ virtual void close() = 0; virtual bool isClosed() const = 0;
AbstractSocket
The
AbstractSocket
is designed to be a socket abstraction to a TCP stack. The socket class can be used to connect to a remote system. It is also returned to an ISocketConnectionListener byAbstractServerSocket
(using itsISocketProvider
) once a connection from a remote system is accepted. An active socket is used to receive data from and transmit data to a remote system.
Receive: The application that wants to receive data from a socket has to register through the
setDataListener
method as anIDataListener
. After that the application is notified through itsdataReceived
method.Send: Sending data through a socket is usually asynchronous. Because of that it is possible to register an
IDataSendNotificationListener
who is notified when the data passed to the send method is written to the TCP-stack.Note
This is not the time when the ACK package from the remote system is received!
/** * all ErrorCodes used by socket * \enum ErrorCode */ enum class ErrorCode : uint8_t { /** everything ok */ SOCKET_ERR_OK, /** an error occurred */ SOCKET_ERR_NOT_OK, /** socket not open to send */ SOCKET_ERR_NOT_OPEN, /** no more outgoing buffer available */ SOCKET_ERR_NO_MORE_BUFFER, /** socket is full, flush before continuing */ SOCKET_FLUSH }; using ConnectedDelegate = ::etl::delegate<void(ErrorCode)>; /** * constructor * \post fpDataListener == 0L * \post fpSendNotificationListener == 0L */ AbstractSocket(); AbstractSocket(AbstractSocket const&) = delete; AbstractSocket& operator=(AbstractSocket const&) = delete; /** * binds to a local address and port * \param ipAddr local ip address * \param port local port */ virtual ErrorCode bind(ip::IPAddress const& ipAddr, uint16_t port) = 0; /** * connects to a remote host * \param ipAddr remote ip address * \param port remote port */ virtual ErrorCode connect(ip::IPAddress const& ipAddr, uint16_t port, ConnectedDelegate delegate) = 0; /** * closes the socket by sending a FIN packet */ virtual ErrorCode close() = 0; /** * aborts the socket by sending a RST packet */ virtual void abort() = 0; /** * flushes enqueued data */ virtual ErrorCode flush() = 0; /** * discards any received data without ack */ virtual void discardData() = 0; /** * \return number of bytes available in outgoing buffer */ virtual size_t available() = 0; /** * reads a single byte * \param byte byte to read * \return number of bytes read * - 0: an error occurred reading from the socket * - 1: byte read */ virtual uint8_t read(uint8_t& byte) = 0; /** * reads a given number of bytes from the socket * \param buffer buffer to receive data to. If 0L is passed the n * bytes should be skipped, i.e. the input stream has to advance by * n bytes. * \param n number of bytes to receive * \return number of bytes really read * - 0: an error occurred reading from the socket * - else: bytes read */ virtual size_t read(uint8_t* buffer, size_t n) = 0; /** * sends an amount of data * \param data data to send * \return status of transmission * - SOCKET_OK when data was sent to TCP stack * - SOCKET_CLOSED when socket has not been opened * \note * Sending the data may be asynchronous. For this reason the * IDataSendNotificationListener has an appropriate callback. */ virtual ErrorCode send(::etl::span<uint8_t const> const& data) = 0; /** * sets the listener to this socket instance * \param pListener IDataListener to attach */ void setDataListener(IDataListener* pListener); IDataListener* getDataListener() const; /** * sets the IDataSendNotificationListener of this socket * \param pListener listener that gets notified when data has been * copied completely to TCP stack */ void setSendNotificationListener(IDataSendNotificationListener* pListener); IDataSendNotificationListener* getSendNotificationListener() const; virtual ip::IPAddress getRemoteIPAddress() const = 0; virtual ip::IPAddress getLocalIPAddress() const = 0; virtual uint16_t getRemotePort() const = 0; virtual uint16_t getLocalPort() const = 0; virtual bool isClosed() const = 0; virtual bool isEstablished() const = 0; /** * Permanently disables NAGLE algorithm for this socket. */ virtual void disableNagleAlgorithm() = 0; /** * Enables TCP keepalive for this socket. * \param idle the length of time to keep an idle TCP connection active * \param interval the interval between packets sent to validate the TCP connection * \param probes the number of keepalive probes to be sent before terminating the connection */ virtual void enableKeepAlive(uint32_t idle, uint32_t interval, uint32_t probes) = 0; /** * Disables TCP keepalive for this socket. */ virtual void disableKeepAlive() = 0;
ISocketProvidingConnectionListener
Incoming connection requests for an
AbstractServerSocket
will need a preallocated socket from the application. It can be provided by thegetSocket()
method. The connection request can be rejected, because of e.g. no more available sockets in the application, with a return of a nullptr./** * returns a pointer to a AbstractSocket object * \param ipAddr Ip address of client which tries to connect. * \param port Port of client from which it tries to connect. * \return * - pointer to AbstractSocket * - NULL if no more sockets are available */ virtual AbstractSocket* getSocket(ip::IPAddress const& ipAddr, uint16_t port) = 0; /** * callback being called when a TCP connection gets accepted * \param socket the AbstractSocket representing the connection * * Usually the ISocketConnectionListener sets an IDataListener to the * socket when the connection is established. */ virtual void connectionAccepted(AbstractSocket& socket) = 0;