Socket API¶
Overview¶
The Socket API provides functions to establish and use TCP and UDP communication channels. The implementation has similarities to BSD Sockets but is not fully compatible.
The API includes client-side function implementations to communicate with a
Network Stack CAmkES component via the if_OS_Socket
interface.
Architecture¶
Within TRENTOS networking functionality is realized the following way:
NIC driver CAmkES components implement the driver to the ethernet hardware. These components provide the
if_OS_Nic
interface.A Network Stack CAmkES component implements the IP Stack functionality.
It uses the interface
if_OS_Nic
to communicate to a NIC driver. There is a 1:1 relationship between those components.It provides the interface
if_OS_Socket
to client CAmkES components, that require network functionality. The interface is designed so that up to 8 clients can connect to one Network Stack. The specific capabilities and client numbers depend on the Network Stack component implementation.
A client CAmkES component that requires network functionality
uses the interface
if_OS_Socket
to communicate with the Network Stack component.Instead of directly using the functions of
if_OS_Socket
, it uses the Socket API to do so.Clients can open several sockets at a time.
A client could be connected to several Network Stacks at the same time.
The Socket API is implemented in a non-blocking way. That means that
every API call will return immediately, but some calls will have a
delayed response. An example of this is OS_Socket_connect()
.
To avoid polling strategies together with the non-blocking API,
if_OS_Socket
implements a notification scheme. The Network Stack
will inform each client about new events related to sockets this client
opened. The clients can retrieve a list of pending events for their
sockets from the Network Stack and react to them accordingly.
The following diagram shows a simplified architecture of a networked application running on an i.MX6 platform.
flowchart TD subgraph trentos ["TRENTOS networked application on i.MX6"] client1[client1:ClientApp] client2[client2:ClientApp] nwStack[nwStack:NetworkStack_PicoTcp] timeserver[timeServer:Timeserver] nicDriver[nic:NIC_iMX6] client1 -->|if_OS_Socket| nwStack client2 -->|if_OS_Socket| nwStack nwStack -->|if_OS_Nic| nicDriver nwStack -->|if_OS_Timer| timeserver end
The following diagram shows a simplified diagram of a networked application running in QEMU.
flowchart TD subgraph trentos ["TRENTOS networked application on QEMU with ChanMux"] subgraph clientApps ["Clients"] Client_1[client1:ClientApp] Client_2[client2:ClientApp] end subgraph nwStacks ["Network Stacks"] NwStack_1[nwStack1:NetworkStack_PicoTcp] NwStack_2[nwStack2:NetworkStack_PicoTcp] end NIC_Driver_1[nic1:NIC_ChanMux] NIC_Driver_2[nic2:NIC_ChanMux] Timeserver[timeServer:Timeserver] subgraph chanMuxBridge ["ChanMux UART Bridge"] ChanMux[chanmux:ChanMux] end Client_1 -->|if_OS_Socket| NwStack_1 Client_2 -->|if_OS_Socket| NwStack_2 NwStack_1 -->|if_OS_Timer| Timeserver NwStack_2 -->|if_OS_Timer| Timeserver NwStack_1 -->|if_OS_Nic| NIC_Driver_1 NwStack_2 -->|if_OS_Nic| NIC_Driver_2 NIC_Driver_1 <-->|Ctrl and Data Channel\nfor TAP Device 1| ChanMux NIC_Driver_2 <-->|Ctrl and Data Channel\nfor TAP Device 2| ChanMux end subgraph linuxHost ["Linux on Host PC"] subgraph proxyApp ["Proxy Application"] tap_1[TAP Device 1] tap_2[TAP Device 2] proxy[Proxy] proxy <--> tap_1 proxy <--> tap_2 end LinuxTAP[Linux network subsystem] tap_1 <--> LinuxTAP tap_2 <--> LinuxTAP end ChanMux -.-> proxy
Limitations¶
Only IPv4 connections are supported
Usage¶
This is how the API can be used by a user component.
Declaration of API Library in CMake¶
To use the Socket API, a client needs to link the following project libraries in the CMake file.
os_core_api: Generic build target to include the TRENTOS core API
os_socket_client: Generic build target to include the Socket API client implementation
DeclareCAmkESComponent(
UserComponent
INCLUDES
...
SOURCES
...
C_FLAGS
...
LIBS
os_core_api
os_socket_client
)
Including CAmkES Connectors in the Component Definition¶
The CAmkES component that wants to use the Socket API needs to include the macro
IF_OS_SOCKET_USE
in its component definition together with a prefix. The
prefix is a unique identifier for that client connection to a specific Network
Stack instance. It needs to be used consistently also in other macros like
IF_OS_SOCKET_ASSIGN
and the macros that connect the client component to
the Network Stack component like
NetworkStack_PicoTcp_INSTANCE_CONNECT_CLIENTS
.
#include <if_OS_Socket.camkes>
component Client {
IF_OS_SOCKET_USE(<if_OS_Socket_prefix>)
}
Using the API in C¶
In the implementation, the client needs to set up the RPC lookup table with the
macro IF_OS_SOCKET_ASSIGN
and the <prefix> the client used for
IF_OS_SOCKET_USE
.
The created variable (in the example below called networkStackCtx
) can
then be used in the OS_Socket_xxx()
functions provided by the Socket API
as the ctx parameter.
#include "OS_Error.h"
#include "OS_Socket.h"
#include "interfaces/if_OS_Socket.h"
#include <camkes.h>
static const if_OS_Socket_t networkStackCtx =
IF_OS_SOCKET_ASSIGN(<if_OS_Socket_prefix>);
...
OS_Error_t err;
OS_Socket_Handle_t hsocket;
err = OS_Socket_create(
&networkStackCtx,
&hsocket,
OS_AF_INET,
OS_SOCK_STREAM);
...
const OS_Socket_Addr_t dstAddr =
{
.addr = "192.168.1.42",
.port = 12345
};
err = OS_Socket_connect(hsocket, &dstAddr);
...
OS_Socket_close(hsocket);
...
API Functions Overview¶
In the following overview, the API functions will be briefly explained.
OS_Socket_create¶
This function will create a new socket handle and connect it to a Network Stack context.
OS_Error_t
OS_Socket_create(
const if_OS_Socket_t* const ctx,
OS_Socket_Handle_t* const phandle,
const int domain,
const int type);
Parameters:
Parameter |
Description |
Supported Values |
---|---|---|
ctx |
Interface context that should be used with the handle. |
|
phandle |
Handle that will be assigned to the created socket. |
|
domain |
Domain of the socket that should be created. |
OS_AF_INET |
type |
Type of the socket that should be created. |
OS_SOCK_STREAM - for TCP sockets |
Return values:
Error Code |
Description |
---|---|
OS_SUCCESS |
Operation was successful. |
OS_ERROR_ABORTED |
If the Network Stack has experienced a fatal error. |
OS_ERROR_NOT_INITIALIZED |
If the function was called before the Network Stack was fully initialized. |
OS_ERROR_INVALID_PARAMETER |
If an invalid parameter or NULL pointer was passed. |
OS_ERROR_NETWORK_PROTO_NO_SUPPORT |
If the passed domain or type is unsupported. |
OS_ERROR_NETWORK_UNREACHABLE |
If the network is unreachable. |
OS_ERROR_INSUFFICIENT_SPACE |
If no free sockets could be found. |
other |
Each component implementing this might have additional error codes. |
Particularities:
None
OS_Socket_connect¶
This function will initiate a connection to a remote server.
OS_Error_t
OS_Socket_connect(
const OS_Socket_Handle_t handle,
const OS_Socket_Addr_t* dstAddr);
Parameters:
Parameter |
Description |
Supported Values |
---|---|---|
handle |
Handle of the socket to connect. |
|
dstAddr |
Address of the destination to connect to. |
Only IPv4 Addresses are supported. |
Return values:
Error Code |
Description |
---|---|
OS_SUCCESS |
Operation was successful. |
OS_ERROR_ABORTED |
If the Network Stack has experienced a fatal error. |
OS_ERROR_NOT_INITIALIZED |
If the function was called before the Network Stack was fully initialized. |
OS_ERROR_INVALID_HANDLE |
If an invalid handle was passed. |
OS_ERROR_INVALID_PARAMETER |
If an invalid parameter or NULL pointer was passed. |
OS_ERROR_CONNECTION_CLOSED |
If the socket connection was previously shut down. |
OS_ERROR_NETWORK_PROTO_NO_SUPPORT |
If the protocol is not supported. |
OS_ERROR_NETWORK_HOST_UNREACHABLE |
If the host is not unreachable. |
OS_ERROR_NETWORK_PROTO |
If the function is called on the wrong socket type. |
other |
Each component implementing this might have additional error codes. |
Particularities:
A return value of
OS_SUCCESS
does not mean, that the connection is established, but that the connection process was successfully initiated.The Network Stack will inform the client with a notification about the connection process result.
A successful connection process will be reported with
OS_SOCK_EV_CONN_EST
.An unsuccessful connection attempt will be reported with
OS_SOCK_EV_FIN
orOS_SOCK_EV_ERROR
.
If during the connection process
OS_Socket_read()
orOS_Socket_write()
are called, the functions will return withOS_ERROR_NETWORK_CONN_NONE
.If after an unsuccessful connection process
OS_Socket_read()
orOS_Socket_write()
are called, the functions will return withOS_ERROR_CONNECTION_CLOSED
.Following an unsuccessful connection process, the socket handle is not usable anymore and needs to be closed with
OS_Socket_close()
.If during the connection process
OS_Socket_close()
is called, the connection attempt will be canceled.
OS_Socket_bind¶
This function will bind a specified local IP address and port to a socket.
OS_Error_t
OS_Socket_bind(
const OS_Socket_Handle_t handle,
const OS_Socket_Addr_t* const localAddr);
Parameters:
Parameter |
Description |
Supported Values |
---|---|---|
handle |
Handle of the socket to bind. |
|
localAddr |
Local address to bind the socket to. |
Only IPv4 Addresses are supported. |
Return values:
Error Code |
Description |
---|---|
OS_SUCCESS |
Operation was successful. |
OS_ERROR_ABORTED |
If the Network Stack has experienced a fatal error. |
OS_ERROR_NOT_INITIALIZED |
If the function was called before the Network Stack was fully initialized. |
OS_ERROR_INVALID_HANDLE |
If an invalid handle was passed. |
OS_ERROR_INVALID_PARAMETER |
If an invalid parameter or NULL pointer was passed. |
OS_ERROR_IO |
If the specified address can not be found. |
OS_ERROR_INSUFFICIENT_SPACE |
If there is not enough space. |
OS_ERROR_CONNECTION_CLOSED |
If the connection is in a closed state. |
other |
Each component implementing this might have additional error codes. |
Particularities:
None
OS_Socket_listen¶
This function will listen for incoming connections on an opened and bound socket.
OS_Error_t
OS_Socket_listen(
const OS_Socket_Handle_t handle,
const int backlog);
Parameters:
Parameter |
Description |
Supported Values |
---|---|---|
handle |
Handle of the socket to listen on. |
|
backlog |
Sets the maximum size to which the queue of pending connections may grow. |
Return values:
Error Code |
Description |
---|---|
OS_SUCCESS |
Operation was successful. |
OS_ERROR_ABORTED |
If the Network Stack has experienced a fatal error. |
OS_ERROR_NOT_INITIALIZED |
If the function was called before the Network Stack was fully initialized. |
OS_ERROR_INVALID_HANDLE |
If an invalid handle was passed. |
OS_ERROR_INVALID_PARAMETER |
If an invalid parameter or NULL pointer was passed. |
OS_ERROR_CONNECTION_CLOSED |
If the connection is in a closed state. |
OS_ERROR_NETWORK_CONN_ALREADY_BOUND |
If the socket is already connected. |
OS_ERROR_NETWORK_PROTO |
If the function is called on the wrong socket type. |
other |
Each component implementing this might have additional error codes. |
Particularities:
None
OS_Socket_accept¶
Accept the next connection request on the queue of pending connections for the listening socket.
OS_Error_t
OS_Socket_accept(
const OS_Socket_Handle_t handle,
OS_Socket_Handle_t* const pClientHandle,
OS_Socket_Addr_t* const srcAddr);
Parameters:
Parameter |
Description |
Supported Values |
---|---|---|
handle |
Handle of the listening socket. |
|
pClientHandle |
Handle that will be used to map the accepted connection to. |
|
srcAddr |
Address of the accepted socket. |
Return values:
Error Code |
Description |
---|---|
OS_SUCCESS |
Operation was successful. |
OS_ERROR_ABORTED |
If the Network Stack has experienced a fatal error. |
OS_ERROR_NOT_INITIALIZED |
If the function was called before the Network Stack was fully initialized. |
OS_ERROR_INVALID_HANDLE |
If an invalid handle was passed. |
OS_ERROR_INVALID_PARAMETER |
If an invalid parameter or NULL pointer was passed. |
OS_ERROR_TRY_AGAIN |
If the resource is temporarily unavailable and the caller should try again. |
OS_ERROR_NETWORK_PROTO |
If the function is called on the wrong socket type. |
OS_ERROR_INSUFFICIENT_SPACE |
If no free sockets could be found. |
OS_ERROR_CONNECTION_CLOSED |
If the connection was closed by remote before it was accepted by the localhost. |
other |
Each component implementing this might have additional error codes. |
Particularities:
None
OS_Socket_read¶
This function will read data from a socket. This function checks whether or not the socket is bound and connected before it attempts to receive data.
OS_Error_t
OS_Socket_read(
const OS_Socket_Handle_t handle,
void* const buf,
size_t requestedLen,
size_t* const actualLen);
Parameters:
Parameter |
Description |
Supported Values |
---|---|---|
handle |
Handle of the socket to read from. |
|
buf |
Buffer to store the read data. |
|
requestedLen |
Length of the data that should be read. |
|
actualLen |
Actual length that was read from the socket. |
Return values:
Error Code |
Description |
---|---|
OS_SUCCESS |
Operation was successful. |
OS_ERROR_ABORTED |
If the Network Stack has experienced a fatal error. |
OS_ERROR_NOT_INITIALIZED |
If the function was called before the Network Stack was fully initialized. |
OS_ERROR_INVALID_HANDLE |
If an invalid handle was passed. |
OS_ERROR_INVALID_PARAMETER |
If an invalid parameter or NULL pointer was passed. |
OS_ERROR_IO |
If there is an input/output error. |
OS_ERROR_NETWORK_CONN_NONE |
If no connection is established when calling this function. |
OS_ERROR_NETWORK_PROTO |
If the function is called on the wrong socket type. |
OS_ERROR_TRY_AGAIN |
If the resource is temporarily unavailable or the request would block and the caller should try again. |
OS_ERROR_CONNECTION_CLOSED |
If the connection is in a closed state. |
OS_ERROR_NETWORK_CONN_SHUTDOWN |
If the connection got shut down. |
other |
Each component implementing this might have additional error codes. |
Particularities:
OS_Socket_read()
will return as many bytes as have been received from the remote host, up torequestedLen
. This may also include data received over several ethernet frames.
OS_Socket_write¶
This function will write data on a socket. This function checks if the socket is bound, connected and that it isn’t shut down locally.
OS_Error_t
OS_Socket_write(
const OS_Socket_Handle_t handle,
const void* const buf,
const size_t requestedLen,
size_t* const actualLen);
Parameters:
Parameter |
Description |
Supported Values |
---|---|---|
handle |
Handle of the socket to write on. |
|
buf |
Buffer containing data that should be sent. |
|
requestedLen |
Amount of data that should be written. |
|
actualLen |
Actual length that was written on the socket. |
Return values:
Error Code |
Description |
---|---|
OS_SUCCESS |
Operation was successful. |
OS_ERROR_ABORTED |
If the Network Stack has experienced a fatal error. |
OS_ERROR_NOT_INITIALIZED |
If the function was called before the Network Stack was fully initialized. |
OS_ERROR_INVALID_HANDLE |
If an invalid handle was passed. |
OS_ERROR_INVALID_PARAMETER |
If an invalid parameter or NULL pointer was passed. |
OS_ERROR_IO |
If there is an input/output error. |
OS_ERROR_NETWORK_PROTO |
If the function is called on the wrong socket type. |
OS_ERROR_INSUFFICIENT_SPACE |
If there is not enough space. |
OS_ERROR_TRY_AGAIN |
If the resource is temporarily unavailable or the request would block and the caller should try again. |
OS_ERROR_CONNECTION_CLOSED |
If the connection is in a closed state. |
OS_ERROR_NETWORK_CONN_NONE |
If the socket is not connected. |
OS_ERROR_NETWORK_CONN_SHUTDOWN |
If the connection got shut down. |
OS_ERROR_NETWORK_ADDR_NOT_AVAILABLE |
If the address is not available. |
OS_ERROR_NETWORK_HOST_UNREACHABLE |
If the host is not unreachable. |
other |
Each component implementing this might have additional error codes. |
Particularities:
With
OS_Socket_write()
the network stack will copy data to be sent into it’s internal buffers and schedule it for actual transmission later. If errors occur during the actual transmission, those will be reported byOS_Socket_getPendingEvents()
/OS_SOCK_EV_ERROR
.Depending on the internal buffer capacity of the network stack,
OS_Socket_write()
may accept only partially the data in buf. It will return the number of successfully accepted bytes inactualLen
.
OS_Socket_recvfrom¶
This function will receive data from a specified socket. This operation checks if the socket is bound but not if it is connected and is therefore not connection-oriented.
OS_Error_t
OS_Socket_recvfrom(
const OS_Socket_Handle_t handle,
void* const buf,
size_t requestedLen,
size_t* const actualLen,
OS_Socket_Addr_t* const srcAddr);
Parameters:
Parameter |
Description |
Supported Values |
---|---|---|
handle |
Handle to a previously created socket. |
|
buf |
Buffer to store the read data. |
|
requestedLen |
Length of the data that should be read. |
|
actualLen |
Actual length that was read from the socket. |
|
srcAddr |
Address of the source socket that data was received from. |
Return values:
Error Code |
Description |
---|---|
OS_SUCCESS |
Operation was successful. |
OS_ERROR_ABORTED |
If the Network Stack has experienced a fatal error. |
OS_ERROR_NOT_INITIALIZED |
If the function was called before the Network Stack was fully initialized. |
OS_ERROR_INVALID_HANDLE |
If an invalid handle was passed. |
OS_ERROR_INVALID_PARAMETER |
If an invalid parameter or NULL pointer was passed. |
OS_ERROR_NETWORK_PROTO |
If the function is called on the wrong socket type. |
OS_ERROR_TRY_AGAIN |
If the resource is temporarily unavailable or the request would block and the caller should try again. |
OS_ERROR_CONNECTION_CLOSED |
If no further communication is possible on the socket. |
OS_ERROR_NETWORK_CONN_SHUTDOWN |
If the connection got shut down. |
OS_ERROR_NETWORK_ADDR_NOT_AVAILABLE |
If the address is not available. |
other |
Each component implementing this might have additional error codes. |
Particularities:
OS_Socket_recvfrom()
will return the contents of one received UDP datagram.If the datagram had a payload size of 0,
OS_Socket_recvfrom()
will return withOS_SUCCESS
andactualLen
of 0.If requestedLen is smaller then the the payload size of the datagram,
OS_Socket_recvfrom()
will return the partial data as requested. Following calls toOS_Socket_recvfrom()
will return the remaining data of the partially read datagram.
OS_Socket_sendto¶
This function will send data on a destination socket without checking if the destination is connected or not and is therefore not connection-oriented.
OS_Error_t
OS_Socket_sendto(
const OS_Socket_Handle_t handle,
const void* const buf,
size_t requestedLen,
size_t* const actualLen,
const OS_Socket_Addr_t* const dstAddr);
Parameters:
Parameter |
Description |
Supported Values |
---|---|---|
handle |
Handle to a previously created socket. |
|
buf |
Buffer containing data to be written. |
|
requestedLen |
Length of the data that should be written on the destination socket. |
|
actualLen |
Actual length that was written on the socket. |
|
dstAddr |
Address of the destination socket to send data on. |
Only IPv4 Addresses are supported. |
Return values:
Error Code |
Description |
---|---|
OS_SUCCESS |
Operation was successful. |
OS_ERROR_ABORTED |
If the Network Stack has experienced a fatal error. |
OS_ERROR_NOT_INITIALIZED |
If the function was called before the Network Stack was fully initialized. |
OS_ERROR_INVALID_HANDLE |
If an invalid handle was passed. |
OS_ERROR_INVALID_PARAMETER |
If an invalid parameter or NULL pointer was passed. |
OS_ERROR_INSUFFICIENT_SPACE |
If there is not enough space. |
OS_ERROR_NETWORK_PROTO |
If the function is called on the wrong socket type. |
OS_ERROR_TRY_AGAIN |
If the resource is temporarily unavailable or the request would block and the caller should try again. |
OS_ERROR_CONNECTION_CLOSED |
If no further communication is possible on the socket. |
OS_ERROR_NETWORK_ADDR_NOT_AVAILABLE |
If the address is not available. |
OS_ERROR_NETWORK_HOST_UNREACHABLE |
If the host is not unreachable. |
other |
Each component implementing this might have additional error codes. |
Particularities:
With
OS_Socket_sendto()
the network stack will copy data to be sent into it’s internal buffers and schedule it for actual transmission later. If errors occur during the actual transmission, those will be reported byOS_Socket_getPendingEvents()
/OS_SOCK_EV_ERROR
.Depending on the internal buffer capacity of the network stack,
OS_Socket_sendto()
may accept only partially the data in buf. It will return the number of successfully accepted bytes inactualLen
.
OS_Socket_close¶
This function will close a network socket. No further socket communication is possible after closure.
OS_Error_t
OS_Socket_close(
const OS_Socket_Handle_t handle);
Parameters:
Parameter |
Description |
Supported Values |
---|---|---|
handle |
Handle of the socket that should be closed. |
Return values:
Error Code |
Description |
---|---|
OS_SUCCESS |
Operation was successful. |
OS_ERROR_ABORTED |
If the Network Stack has experienced a fatal error. |
OS_ERROR_NOT_INITIALIZED |
If the function was called before the Network Stack was fully initialized. |
OS_ERROR_INVALID_HANDLE |
If an invalid handle was passed. |
OS_ERROR_INVALID_PARAMETER |
If the handle context is invalid. |
other |
Each component implementing this might have additional error codes. |
Particularities:
None
OS_Socket_getPendingEvents¶
This function will get the pending events for the opened sockets.
OS_Error_t
OS_Socket_getPendingEvents(
const if_OS_Socket_t* const ctx,
void* const buf,
const size_t bufSize,
int* const numberOfEvents);
Parameters:
Parameter |
Description |
Supported Values |
---|---|---|
ctx |
Interface context that should be used. |
|
buf |
Buffer to store the event data. |
|
bufSize |
Size of the buffer to store the event data. |
|
numberOfEvents |
Will be overwritten with the number of events. |
Return values:
Error Code |
Description |
---|---|
OS_SUCCESS |
Operation was successful. |
OS_ERROR_ABORTED |
If the Network Stack has experienced a fatal error. |
OS_ERROR_NOT_INITIALIZED |
If the function was called before the Network Stack was fully initialized. |
OS_ERROR_INVALID_HANDLE |
If an invalid handle was passed. |
OS_ERROR_INVALID_PARAMETER |
If an invalid parameter or NULL pointer was passed. |
OS_ERROR_BUFFER_TOO_SMALL |
If the buffer is not even large enough for one event. |
other |
Each component implementing this might have additional error codes. |
The events that this function will place in the passed buffer are all of
type OS_Socket_Evt_t
.
typedef struct __attribute__((packed))
{
int socketHandle; //!< Handle ID of the socket.
int parentSocketHandle; //!< Handle ID of the parent socket.
uint8_t eventMask; //!< Event mask of the socket.
OS_Error_t currentError; //!< Current error of the socket.
}
OS_Socket_Evt_t;
The event flags that can be set for the eventMask
member of this
struct and that can be evaluated by the user are listed below.
#define OS_SOCK_EV_NONE (0)
#define OS_SOCK_EV_CONN_EST (1<<0) //!< Connection established (TCP only).
#define OS_SOCK_EV_CONN_ACPT (1<<1) //!< Connection accepted (TCP only).
#define OS_SOCK_EV_READ (1<<2) //!< Data arrived on the socket.
#define OS_SOCK_EV_WRITE (1<<3) //!< Ready to write to the socket (TCP only).
#define OS_SOCK_EV_FIN (1<<4) //!< FIN segment received (TCP only).
#define OS_SOCK_EV_CLOSE (1<<5) //!< Socket is closed (TCP only). No further communication is possible from this point on the socket.
#define OS_SOCK_EV_ERROR (1<<6) //!< An error occurred.
Particularities:
None
OS_Socket_getStatus¶
This function queries the current state of the Network Stack component.
OS_NetworkStack_State_t
OS_Socket_getStatus(
const if_OS_Socket_t* const ctx);
Parameters:
Parameter |
Description |
Supported Values |
---|---|---|
ctx |
Interface context that should be used. |
Return values:
Network Stack State |
Description |
---|---|
UNINITIALIZED |
Network Stack is uninitialized. |
INITIALIZED |
Network Stack is initialized. |
RUNNING |
Network Stack is running. |
FATAL_ERROR |
Network Stack has experienced a fatal error. |
Particularities:
None
OS_Socket_poll¶
This function checks, if a notification was received from the Network Stack before.
OS_Error_t
OS_Socket_poll(
const if_OS_Socket_t* const ctx);
Parameters:
Parameter |
Description |
Supported Values |
---|---|---|
ctx |
Interface context that should be used with the handle. |
Return values:
Error Code |
Description |
---|---|
OS_SUCCESS |
Notification event found. |
OS_ERROR_INVALID_PARAMETER |
If the handle context is invalid. |
OS_ERROR_TRY_AGAIN |
If no notification event was found. |
other |
Each component implementing this might have additional error codes. |
Particularities:
None
OS_Socket_wait¶
This function blocks until a notification is received from the Network Stack. If a notification was received before, this function will return immediately.
OS_Error_t
OS_Socket_wait(
const if_OS_Socket_t* const ctx);
Parameters:
Parameter |
Description |
Supported Values |
---|---|---|
ctx |
Interface context that should be used with the handle. |
Return values:
Error Code |
Description |
---|---|
OS_SUCCESS |
Notification event found. |
OS_ERROR_INVALID_PARAMETER |
If the handle context is invalid. |
other |
Each component implementing this might have additional error codes. |
Particularities:
None
OS_Socket_regCallback¶
This function will register a callback function, that is executed once the Network Stack sends a notification.
OS_Error_t
OS_Socket_regCallback(
const if_OS_Socket_t* const ctx,
(void (*callback)(void*),
void *arg)
);
Parameters:
Parameter |
Description |
Supported Values |
---|---|---|
ctx |
Interface context that should be used. |
|
callback |
A function pointer to the callback function. |
|
arg |
The arguments for the callback functions. |
Can also be NULL. |
Return values:
Error Code |
Description |
---|---|
OS_SUCCESS |
Callback function successfully registered. |
OS_ERROR_INVALID_PARAMETER |
If an invalid parameter or NULL pointer was passed. |
OS_ERROR_GENERIC |
If the callback could not be registered. |
other |
Each component implementing this might have additional error codes. |
Particularities:
Upon execution of the callback function, the callback is automatically unregistered.
If the callback function is to be executed again for the next notification, the callback function needs to re-register itself within its function body.
Non-Blocking Strategies¶
Socket Handling by Polling¶
In case you have a simple application with a single client connection, you can omit using the notification sent out by the Network Stack and just use the Socket API in polling mode. All functions are non-blocking, so a user needs to read the return values and react properly.
For example, OS_Socket_connect()
will return with OS_SUCCESS
in case
the connection process could be initiated successfully. Read and write calls
will be returned with OS_ERROR_NETWORK_CONN_NONE
until the connection is
established. The code example below shows how this could be realized in a
simplified way.
...
err = OS_Socket_create(
&networkStackCtx,
&socket,
OS_AF_INET,
OS_SOCK_STREAM);
...
err = OS_Socket_connect(socket, &dstAddr);
...
size_t offs = 0;
do
{
const size_t lenRemaining = len_request - offs;
size_t len_io = lenRemaining;
err = OS_Socket_write(
socket,
&request[offs],
len_io,
&len_io);
if (err != OS_SUCCESS)
{
if (err == OS_ERROR_TRY_AGAIN || err == OS_ERROR_NETWORK_CONN_NONE)
{
// Try again.
continue;
}
else
{
Debug_LOG_ERROR("OS_Socket_write() failed, code %d", err);
OS_Socket_close(socket);
return;
}
}
offs += len_io;
}
while (offs < len_request);
...
Socket Handling by Main Event Loop¶
For more complex implementations it is advised to use the notification sent out
by the Network Stack and retrieve the details about all pending events via
OS_Socket_getPendingEvents()
.
The NetworkStack_PicoTcp will cyclically process all incoming data and existing connections. When an update happens for any of the open sockets, it will send a notification to the client that the socket belongs to. Please note, that no individual notifications are sent for each socket. Instead, only one notification is sent after each processing cycle of the Network Stack.
A client can retrieve a list of all events from the Network Stack by calling
OS_Socket_getPendingEvents()
. This will return an array of events for all
updated sockets. The client can then process each event and react appropriately.
One strategy to make use of this is to include an endless loop in the run() function and wait for the notification to be triggered. After receiving the notification we retrieve the pending events, act on all events, tidy up tasks, and wait again on the notification.
...
int
run()
{
...
for (;;)
{
// Wait until we get an event for the listening socket.
OS_Socket_wait(&networkStackCtx);
err = OS_Socket_getPendingEvents(
&network_stack,
evtBuffer,
evtBufferSize,
&numberOfSocketsWithEvents);
...
// Verify that the received number of sockets with events is within expected
// bounds.
...
// Iterate through the received socket events and handle them according to the application logic.
for (int i = 0; i < numberOfSocketsWithEvents; i++)
{
OS_Socket_Evt_t event;
memcpy(&event, &evtBuffer[offset], sizeof(event));
offset += sizeof(event);
// Socket has been closed by the network stack - close socket.
if (event.eventMask & OS_SOCK_EV_FIN)
{
OS_Socket_close(socket);
continue;
}
...
}
}
...
}
...
Socket Handling by Notification Callback¶
The CAmkES infrastructure for the if_OS_Socket
interface provides the
functionality to register a callback function that is executed once the Network
Stack sends a notification (see also the API documentation above for more
details on how to register a callback function). It is therefore possible to
also implement a scheme to handle the socket events that rely on this mechanism
to update the current event state for all sockets a client is managing.
While this strategy can provide certain benefits it should also be noted that this typically results in a more complex solution, since the callback function is executed in a separate thread and can result in concurrency issues. Additional synchronization primitives will therefore be necessary on the user application side.
The example below provides a sketch of how this strategy can be implemented and what API functions would need to be involved for this approach.
...
static OS_Socket_Evt_t eventCollection[OS_NETWORK_MAXIMUM_SOCKET_NO] = {0};
...
static void
cb_nwStackNotification(
void* ctx)
{
...
OS_Error_t err = OS_Socket_getPendingEvents(
ctx,
eventBuffer,
bufferSize,
&numberOfSocketsWithEvents);
...
for (int i = 0; i < numberOfSocketsWithEvents; i++)
{
// Update local eventCollection[OS_NETWORK_MAXIMUM_SOCKET_NO] array
// with the latest events for the sockets. The update of this shared
// ressource should be mutex protected in case the run() thread is
// currently also trying to access it.
}
// Unblock any caller of the functions below waiting for new events to
// arrive.
if (numberOfSocketsWithEvents)
{
notify_about_new_events();
}
// Re-register the callback function as it should be executed again for the
// next notification.
err = OS_Socket_regCallback(
ctx,
&cb_nwStackNotification,
ctx);
...
}
...
OS_Error_t
waitForConnectionEstOnSocket(
const OS_Socket_Handle_t handle)
{
...
for(;;)
{
// Lock mutex for shared eventCollection ressource
eventMask = eventCollection[handle.handleID].eventMask;
err = eventCollection[handle.handleID].currentError;
// Unlock mutex for shared eventCollection ressource
// Check eventMask for the specific events that might be relevant
// to evaluate.
// If a relevant event is found to be set in the eventMask
// (e.g. OS_SOCK_EV_CONN_EST) return OS_SUCCESS. If and error
// is found with OS_SOCK_EV_ERROR, return err.
// Else if no relevant event is found, wait on the arrival of new events.
// The signal would be emitted by the callback function in this example.
}
}
...
int
run()
{
...
// Set up callback for new received socket events.
err = OS_Socket_regCallback(
&networkStackCtx,
&cb_nwStackNotification,
(void*) &networkStackCtx);
...
err = OS_Socket_create(
&networkStackCtx,
&socket,
OS_AF_INET,
OS_SOCK_STREAM);
...
err = OS_Socket_connect(socket, &dstAddr);
...
// Wait until the socket is successfully connected.
err = waitForConnectionEstOnSocket(handle);
...
}
...
Further Examples¶
To get a better understanding of how the Network Stack can be utilized in a system, it is recommended to take a look at the demos provided by the SDK.
In the IoT Demos, a Cloud Connector component is connected to a Network Stack component as a TCP client and uses the networking functionalities to publish MQTT messages from a Sensor component connected to it.
In the demo_tls_api a TLS connection is established, in which the main component needs to setup the TCP connection to a remote server.