#define AIRHOOK_DEBUG 1 /* or 0; optional */ #include <airhook.h>
The AIRHOOK_DEBUG macro controls whether assertions are used in the Airhook implementation to catch programming errors on the part of the user and bugs in the library. If it is not set, the assertions are on by default unless the NDEBUG macro is defined.
Nothing in this header file or the Airhook library relies on anything not defined by ANSI C. The Airhook library performs no I/O; it is up to the client to provide the physical network transport. The Airhook library does not allocate memory; the function call interface is set up so all structures are allocated and managed by the client.
struct airhook_time { unsigned long second; unsigned long nanosecond; };
Airhook uses time values expressed in seconds since some fixed "epoch". This is often the standard Unix epoch (1 January 1970), but it doesn't have to be; Airhook only cares about relative time and never sends absolute time values over the network. Seconds since system boot, or any other consistent origin, would work just as well.
struct airhook_data { const unsigned char *begin; const unsigned char *end; };
A sequence of bytes is represented by a pointer to the first byte (begin) and a pointer to one byte past the last (end). Please note that this structure merely points to data, and does not make any statements about its "ownership"; the data's lifetime must be managed independently.
enum airhook_state { ah_pending, ah_sent, ah_confirmed, ah_discarded };
These four values are used to represent the status of some network transaction:
struct airhook_socket; void airhook_init(struct airhook_socket *,unsigned long session);
Each struct airhook_socket represents one endpoint of an Airhook session and should be associated with a single network connection. (Normally each airhook_socket corresponds to a connected UDP/IP socket, but Airhook can be used with any network transport.)
The airhook_socket structure is opaque but not incomplete. (Clients should never access its fields directly, but its size is known.) The structure is allocated by the client and initialized by the library. Once initialized, it should never be copied or moved, and must remain allocated as long as it is in use.
A session number must be specified when an airhook_socket is initialized. This may be any number which is nonzero and which is different from the number of any previous or subsequent session which might share the same physical link. (That is, if someone restarts your program, the session number should be different.) Common practice is to use a value derived from the current time and/or process ID.
Airhook sessions aren't closed explicitly but rather abandoned. Clients may decide to stop using an airhook_socket, cease related network activity and deallocate all related structures at any point. (Clients usually have an application-level "I'm done now" message.) Since the Airhook library never allocates memory or creates any I/O handles itself, there is no need to notify the library when you stop using it.
struct airhook_status { struct airhook_settings settings; unsigned long session,remote_session; enum airhook_state state,remote_state; signed long wanted; struct airhook_time last_transmit; struct airhook_time next_transmit; struct airhook_time last_response; }; struct airhook_status airhook_status(const struct airhook_socket *);
Clients may request the status of an Airhook socket any time after it has been initialized. The fields of the returned structure are as follows:
struct airhook_settings { struct airhook_time retransmit; unsigned long window_size; }; void airhook_settings(struct airhook_socket *,struct airhook_settings);
Tunable parameters may be adjusted at any point after the Airhook socket has been initialized. The fields of this structure are as follows:
To work properly with future versions of the Airhook library (which may include additional parameters), clients should not build airhook_settings structures from scratch. Instead, clients should use the settings member of the airhook_status structure to get the current settings, modify them as needed, and then use airhook_settings to update the parameters.
struct airhook_outgoing; void airhook_init_outgoing( struct airhook_outgoing *outgoing, struct airhook_socket *socket, struct airhook_data data,void *user);
Messages are sent by initializing an airhook_outgoing structure. (Like airhook_socket, airhook_outgoing is opaque but not incomplete, and must be allocated by the client and initialized by the library.) The parameters are as follows:
struct airhook_outgoing_status { struct airhook_data data; void *user; enum airhook_state state; unsigned long transmit_count; struct airhook_time last_change; }; struct airhook_outgoing_status airhook_outgoing_status(const struct airhook_outgoing *);
Clients may request the status of a message any time after its airhook_outgoing structure is initialized. This is particularly crucial because it indicates when the airhook_outgoing structure and message data may be deallocated. (See airhook_next_changed below to find out how to learn when the status of a message changes.) The fields of the returned structure are as follows:
void airhook_discard_outgoing(struct airhook_outgoing *);
Clients may discard a message any time after its airhook_outgoing structure is initialized. Discarding the message cancels (re)transmission and allows its resources (the airhook_outgoing structure and the message data itself) to be reclaimed by the client.
Messages are typically discarded once their receipt is confirmed, but clients may discard messages sooner if they are deemed to be no longer worth sending.
It's safe (but pointless) to discard a message multiple times (but not after the structure is deallocated, of course).
int airhook_next_changed(struct airhook_socket *socket,struct airhook_outgoing **out);
Returns the airhook_outgoing structure corresponding to the next message whose state has changed since it was initialized or last returned by airhook_next_changed. If any messages meet this criteria, a pointer will be stored in *out and a nonzero value returned. If none have changed, zero will be returned and *out unmodified.
Typically, clients enumerate the changed messages after any network transaction (see airhook_receive and airhook_transmit below); if airhook_outgoing_status reports a state of ah_confirmed, the client calls airhook_discard_outgoing and deallocates the airhook_outgoing structure, the message data itself, and any associated resources.
size_t airhook_transmit( struct airhook_socket *socket, struct airhook_time now, size_t length,unsigned char *data);
When the time specified by next_transmit (in the structure returned by airhook_status) arrives, the client should call airhook_transmit to generate an actual packet to send. The client should then send that packet. The parameters are as follows:
If the Airhook doesn't want to transmit anything, it will return zero (this hardly ever happens). If the value returned is greater than length, then the smallest possible packet is too big to fit in the buffer allocated; the client should allocate more space and try again. Otherwise, a packet has been stored in the buffer, and the value returned is its length; the client should send the data immediately.
Clients may call airhook_transmit before the time specified; the Airhook library will obligingly generate a packet and adapt. This can be used if the client wants to ensure a maximum delay between packets for "keepalive"; if there is no data to be sent, the library will generate a probe packet.
int airhook_receive( struct airhook_socket *socket, struct airhook_time now, struct airhook_data data);
Clients should call airhook_receive whenever a packet arrives on the wire. Clients are responsible for preserving the integrity of packet contents, but not guaranteeing delivery or ordering (UDP's native guarantees are fine). The parameters are as follows:
After calling airhook_receive, clients can (usually in this order) check to see if the socket status has changed in any exciting ways (with airhook_status), check to see if any messages have been confirmed (with airhook_next_changed), and check to see if any new messages have arrived (with airhook_next_incoming, below).
int airhook_next_incoming(struct airhook_socket *socket,struct airhook_data *data);
If the last packet processed with airhook_receive contained any incoming messages, airhook_next_incoming will return nonzero, store a reference to the message data in *data, and remove the message from the internal queue. The data is returned as a reference to the data last passed to airhook_receive, and will remain valid as long as that data does.
Typically, clients call airhook_next_incoming in a loop until it returns zero.
When airhook_receive is called again, the incoming queue is flushed, so clients should make sure to fully drain the queue after every packet is received.