# Drv::Ip IPv4 Socket Implementations

This package contains utility classes to help interact with standard IPv4 (Berkeley) sockets. These classes implement the
core features of IPv4. This includes a tcp server socket (Drv::TcpServerSocket), a tcp client socket (Drv::TcpClient)
and a udp socket (Drv::UdpSocket).  These are not F´ components, but an F´ component wrapper of each exists:
Drv::TcpClientComponent,  Drv::TcpServerComponent, and Drv::UdpComponent.

In addition to each individual socket there is a Drv::IpSocket base class containing high-level functions for
interacting with sockets generically. Drv::IpSocket delegates protocol specific functions to protocol functions
implemented by the children concrete socket classes. i.e. Drv::IpSocket::open delegates to the functions
Drv::TcpClientSocket::openProtocol to open up specifically a tcp client socket.

Drv::SocketComponentHelper is a virtual base class that comes with the functionality for setting up a generic interaction
between this library and a wrapping component. It exists at the component level and serves as a passthrough for requests
from the F´ component to the IPv4 sockets. This virtual base class is intended to be inherited by an F´ component wrapper
that need to support a receive thread such that this functionality need not be redundantly implemented.

Each of these classes is explained in more detail below.

- [Drv::IpSocket](#drvipsocket-baseclass)
- [Drv::TcpClientSocket](#drvtcpclientsocket-class)
- [Drv::TcpServerSocket](#drvtcpserversocket-class)
- [Drv::UdpSocket](#drvudpsocket-class)
- [Drv::SocketComponentHelper](#drvsocketreadtask-virtual-baseclass)

> [!WARNING]
> The core library is not thread-safe. Users of this library (i.e. F´ components) must provide synchronization calls.

## Drv::IpSocket Baseclass

The Drv::IpSocket class represents the external interface to IPv4 socket components. This class provides a top-level
interface to IPv4 sockets for connecting, disconnecting, sending, and receiving  using the socket. This implements the
IPv4 socket protocol as provided by the unix (Berkeley) sockets implementation.

High-level interfaces are provided for the standard functions of: `open`, `close`, `send`, and `recv`.  FramingProtocol and
implementation specific functionality are implemented in derived classes by implementing the pure virtual functions:
`openProtocol`, `sendProtocol`, and `recvProtocol`.

In order to use a Drv::IpSocket it must first be configured with a call to the `Drv::IpSocket::configure` function.
Once configured, it can be opened using `Drv::IpSocket::open`. Opening the socket will verify arguments, allocate
system resources and form a connection.  In server implementations (Drv::TcpServerSocket) the open call will block until
a client connection has been made.  It is safe to assume that a successfully opened socket is ready to send or receive,
however; those calls may detect an error and close the socket in response.

`Drv::IpSocket::send` will attempt to send data across the socket. It will retry to transmit data a configured
number of times on correctable errors before finally succeeding once all data has been transmitted or failing should the
socket send fail. Interrupts and timeouts are the only recoverable errors. Users must call `close` and `open` to recover
from other errors.

`Drv::IpSocket::recv` will attempt to read data from across the socket. It will block until data is received and
in the case that the socket is interrupted without data, it will retry a configurable number of times. Users must call
`close` and `open` to recover from other errors.

A call to `Drv::IpSocket::close` will specifically shutdown and close the client connection. This has the effect of
stopping any blocking reads on the socket, issuing a formal disconnect, and cleaning up the allocated resources. Once
called, the Drv::IpSocket should be ready for another call to `Drv::IpSocket::open`.

### Example Usage of Drv::IpSocket

This section will show some example usages of the Drv::IpSocket. In this section it is assumed the initialization is
done using a concrete derived class as shown in subsequent sections.

```c++
Drv::IpSocket& socket = ...; // Initialize the component
socket.configure("127.0.0.1", 60210, 0, 100); // Use 127.0.0.1:60210 with a 100us send timeout
...
socket.open();

while (chatting) {
    socket.send(); // Timeout of 100us
    socket.recv(); // Will block
}
socket.close();
socket.open();
...
```

## Configuration: Socket Options

The `IP_SOCKET_OPTIONS` array in `config/IpCfg.hpp` defines socket options that are applied to all sockets (TCP server,
TCP client, and UDP) during setup via `setsockopt`. By default, `SO_REUSEADDR` is enabled (`1`). This prevents
`EADDRINUSE` ("address already in use") errors when restarting flight software that uses a TCP server, by allowing the
socket to bind immediately even if the port is still in `TIME_WAIT` state from a prior connection.

To disable `SO_REUSEADDR`, override the entry in your project's `IpCfg.hpp`:

```c++
static const IpSocketOptions IP_SOCKET_OPTIONS[] = {
    makeIntOption(SO_REUSEADDR, SOL_SOCKET, 0),  // Disable reuse
};
```

> [!NOTE]
> Enabling `SO_REUSEADDR` allows another process on the same machine to bind to the same port. Projects should
> evaluate their threat model before leaving this enabled. See the comments in `IpCfg.hpp` for details.

Additional socket options can be appended to the `IP_SOCKET_OPTIONS` array using the `makeIntOption` and
`makeSizeOption` helper functions defined in `IpCfg.hpp`.

## Drv::TcpClientSocket Class

The Drv::TcpClientSocket class represents an IPv4 TCP client. It inherently provides bidirectional communication with
a remote server tcp server. TCP communications acknowledge transmissions so the remote server will have received a
message or this client will return an error. In other words, dropped packets will cause an error. Usage is as stipulated
above and an example instantiation is shown below.

```c++
Drv::TcpClientSocket socket;
```

## Drv::TcpServerSocket Class

The Drv::TcpServerSocket class represents an IPv4 TCP server. It inherently provides bidirectional communication with
a remote tcp client server. The TCP server must be started up such that it may listen for incoming client requests.
Since this class is intended to communicate with exactly one client, no listen queue is provided and subsequent connects
from clients will be ignored until the primary client has been closed. Like the TCP client packet drops will result in an
error.

> [!NOTE]
> The `Drv::TcpServerSocket::open` call will block until a client connects to the server.

In order to startup the server to listen, the `Drv::TcpServerSocket::startup` method should be called. It will create a
socket that will listen for incoming connections.  `Drv::TcpServerSocket::startup` should be called before any
`Drv::TcpServerSocket::open` calls to ensure that the server is ready for communication.  `Drv::TcpServerSocket::close`
will only close the client connection and does not affect the server from listening for clients, however; it does free
up the server to accept a new client.

`Drv::TcpServerSocket::terminate` will shut down and close the TCP server socket, releasing the resources allocated by
`Drv::TcpServerSocket::startup` and preventing new clients. The shutdown is done first so that a thread waiting in
`Drv::TcpServerSocket::open` can exit. It does not close an accepted client connection; use `Drv::TcpServerSocket::close`.

## Example TcpServer Usage

```c++
Drv::TcpServerSocket server; // Initialize the component
server.configure("127.0.0.1", 60210, 0, 100); // Use 127.0.0.1:60210 with a 100us send timeout
...
server.startup(); // Listen on 127.0.0.1:60210
server.open(); // Blocks until a client is available

while (chatting) {
    server.send(); // Timeout of 100us
    server.recv(); // Will block
}
server.close(); // Close the client connection
server.open(); // Blocks until a client is available
...
server.close(); // Close the client connection
server.terminate(); // Shut down and close the server's listening socket
```

## Drv::UdpSocket Class

The Drv::UdpSocket class represents an IPv4 UDP sender/receiver. Drv::UdpSocket will provide unidirectional or
bidirectional communication using UDP. UDP is typically faster than TCP as it does not acknowledge transmissions. There
is no guarantee that a sent packet is received, or even that the remote side is listening.

A UDP socket must be configured for each direction that it will communicate in. This can be done using calls to
`Drv::UdpSocket::configureSend` and `Drv::UdpSocket::configureRecv`.  If either call is omitted only a single direction
of communication will function. It is erroneous to omit both configuration calls.  Calling `Drv::UdpSocket::configure`
will result in an assertion failure. Other interaction with the UDP socket is as stipulated with `Drv::IpSocket`.  Examples
of instantiation and configuration are provided below.

```c++
Drv::UdpSocket socketSend;
socketSend.configureSend(127.0.0.1, 60211, 0, 100); // Send only socket
...

Drv::UdpSocket socketRecv;
socketRecv.configureRecv(127.0.0.1, 60212); // Receive only socket
...

Drv::UdpSocket& socketBoth = Drv::UdpSocket;
socketBoth.configureSend(127.0.0.1, 60211, 0, 100);
socketBoth.configureRecv(127.0.0.1, 60212);
...
```
### Support for Ephemeral Ports

Drv::UdpSocket supports ephemeral ports through passing a 0 as the port argument for either `Drv::UdpSocket::configureSend`
or `Drv::UdpSocket::configureRecv`.

For `Drv::UdpSocket::configureSend` this means that you would like to set up the UdpSocket to be able to respond to the source
port that is indicated in the UDP datagrams you receive. Note that this configuration will set up the send port to the port
specified only in the first message received.

For `Drv::UdpSocket::configureRecv` this means that you would like to be assigned an ephemeral port. This would generally be used
for setting up a sender that would like to receive responses to messages on an ephemeral port.

## Drv::SocketComponentHelper Virtual Baseclass

The Drv::SocketComponentHelper is intended as a base class used to add in the functionality of an automatically reconnecting
receive thread to another class (typically an F´ component) as well as an interface between the component using an IP socket
and the IP socket library functions implemented in this folder. In order for the receive thread to function, the inheritor must
implement several methods to provide the necessary interaction of this thread. These functions are described in the next
section. The automatic reconnection is handled by a separate dedicated thread upon request from either the receive thread
or a send request.

In order to start the receiving thread a call to the `Drv::SocketComponentHelper::start` method is performed passing
in a name, and all arguments to `Os::Task::start` to start the receive and reconnect tasks. An optional parameter, reconnect,
will determine if this read task will request reconnects to sockets should a disconnect or error occur. Once started, the read task will continue
until a `Drv::SocketComponentHelper::stop` has been called or an error occurred when started without reconnect set to
`true`.  Once the socket stop call has been made, the user should call `Drv::SocketComponentHelper::join` in order to
wait until the full tasks have finished.  `Drv::SocketComponentHelper::stop` will call `Drv::SocketComponentHelper::shutdown`
on the provided Drv::IpSocket to ensure that any blocking reads exit, freeing the thread to completely stop. Normal usage of
a Drv::SocketComponentHelper derived class is shown below.

```c++
Os::TaskString name("ReceiveTask");
uplinkComm.start(name); // Default reconnect=true
...

uplinkComm.stop();
(void) uplinkComm.join(); // this will join the receive and reconnect tasks
```

`Drv::SocketComponentHelper::open` and `Drv::SocketComponentHelper::close` convenience methods are also provided to open and close the
provided Drv::IpSocket, although it should be noted that both are called automatically either by the receive thread or on send.


### Drv::SocketComponentHelper Inheritance

Drv::SocketComponentHelper is used via inheritance. A class that needs to provide receive thread functionality may inherit from
this class and implement the three virtual methods to integrate with the thread. These methods are described below and
each provides its prototype.

`Drv::SocketComponentHelper::getSocketHandler` returns a reference to an existing Drv::IpSocket for this task to interact with
for receiving, sending, opening, and closing. This should not be allocated within the function call as it will be used
outside the call. The instance is expected to have been configured and started (in the case of servers). The prototype
of the function is shown below.

```c++
virtual IpSocket& getSocketHandler() = 0;
```

`Drv::SocketComponentHelper::getBuffer` returns an Fw::Buffer instance that wraps data for the read call to fill. This can wrap
stack memory, or delegate the call to a buffer manager instance.  This buffer will be returned via a call to
`Drv::SocketComponentHelper::sendBuffer` once the received data has filled it.  The prototype of the function is shown below.

```c++
virtual Fw::Buffer getBuffer() = 0;
```

`Drv::SocketComponentHelper::sendBuffer` returns the buffer obtained from `Drv::SocketComponentHelper::getBuffer` after it has been
filled with data read from the socket. This will come with a status associated with the read but will always be called
such that the allocated buffer can be freed when needed.

```c++
virtual void sendBuffer(Fw::Buffer buffer, SocketIpStatus status) = 0;
```

## Further Information

Further information can be read by referencing the following components.

`Drv::TcpClientComponent`: a F´ component wrapper of the tcp client
`Drv::TcpServerComponent`: a F´ component wrapper of the tcp server
`Drv::UdpComponent`: a F´ component wrapper of the udp
