From d94b44e4de3d91b3108a806d7f42f7220b72af58 Mon Sep 17 00:00:00 2001 From: Stephen Seo Date: Fri, 6 Dec 2019 20:49:30 +0900 Subject: [PATCH] More documentation, fix constants to be #define'd --- src/UDPC_Defines.hpp | 4 + src/UDPConnection.cpp | 20 ++-- src/UDPConnection.h | 240 +++++++++++++++++++++++++++++++++++++++--- 3 files changed, 241 insertions(+), 23 deletions(-) diff --git a/src/UDPC_Defines.hpp b/src/UDPC_Defines.hpp index 5eb2564..0854ae7 100644 --- a/src/UDPC_Defines.hpp +++ b/src/UDPC_Defines.hpp @@ -44,6 +44,10 @@ #define UDPC_LSFULL_HEADER_SIZE (UDPC_MIN_HEADER_SIZE+1+crypto_sign_BYTES) #define UDPC_NSFULL_HEADER_SIZE (UDPC_MIN_HEADER_SIZE+1) +#define UDPC_UPDATE_MS_MIN 4 +#define UDPC_UPDATE_MS_MAX 333 +#define UDPC_UPDATE_MS_DEFAULT 8 + namespace UDPC { static const auto ONE_SECOND = std::chrono::seconds(1); diff --git a/src/UDPConnection.cpp b/src/UDPConnection.cpp index f82901e..a9d50fc 100644 --- a/src/UDPConnection.cpp +++ b/src/UDPConnection.cpp @@ -1777,7 +1777,7 @@ UDPC_HContext UDPC_init_threaded_update(UDPC_ConnectionId listenId, } ctx->flags.set(0); - ctx->threadedSleepTime = std::chrono::milliseconds(8); + ctx->threadedSleepTime = std::chrono::milliseconds(UDPC_UPDATE_MS_DEFAULT); ctx->thread = std::thread(UDPC::threadedUpdate, ctx); UDPC_CHECK_LOG(ctx, UDPC_LoggingType::UDPC_INFO, "Initialized threaded UDPC"); @@ -1795,10 +1795,10 @@ UDPC_HContext UDPC_init_threaded_update_ms( } ctx->flags.set(0); - if(updateMS < 4) { - ctx->threadedSleepTime = std::chrono::milliseconds(4); - } else if(updateMS > 333) { - ctx->threadedSleepTime = std::chrono::milliseconds(333); + if(updateMS < UDPC_UPDATE_MS_MIN) { + ctx->threadedSleepTime = std::chrono::milliseconds(UDPC_UPDATE_MS_MIN); + } else if(updateMS > UDPC_UPDATE_MS_MAX) { + ctx->threadedSleepTime = std::chrono::milliseconds(UDPC_UPDATE_MS_MAX); } else { ctx->threadedSleepTime = std::chrono::milliseconds(updateMS); } @@ -1816,7 +1816,7 @@ int UDPC_enable_threaded_update(UDPC_HContext ctx) { } c->flags.set(0); - c->threadedSleepTime = std::chrono::milliseconds(8); + c->threadedSleepTime = std::chrono::milliseconds(UDPC_UPDATE_MS_DEFAULT); c->threadRunning.store(true); c->thread = std::thread(UDPC::threadedUpdate, c); @@ -1831,10 +1831,10 @@ int UDPC_enable_threaded_update_ms(UDPC_HContext ctx, int updateMS) { } c->flags.set(0); - if(updateMS < 4) { - c->threadedSleepTime = std::chrono::milliseconds(4); - } else if(updateMS > 333) { - c->threadedSleepTime = std::chrono::milliseconds(333); + if(updateMS < UDPC_UPDATE_MS_MIN) { + c->threadedSleepTime = std::chrono::milliseconds(UDPC_UPDATE_MS_MIN); + } else if(updateMS > UDPC_UPDATE_MS_MAX) { + c->threadedSleepTime = std::chrono::milliseconds(UDPC_UPDATE_MS_MAX); } else { c->threadedSleepTime = std::chrono::milliseconds(updateMS); } diff --git a/src/UDPConnection.h b/src/UDPConnection.h index 8b62470..f64df96 100644 --- a/src/UDPConnection.h +++ b/src/UDPConnection.h @@ -76,16 +76,24 @@ # endif // DOXYGEN_SHOULD_SKIP_THIS #endif +// other defines +#define UDPC_PACKET_MAX_SIZE 8192 +#define UDPC_DEFAULT_PROTOCOL_ID 1357924680 // 0x50f04948 + #ifndef DOXYGEN_SHOULD_SKIP_THIS -// other defines -# define UDPC_PACKET_MAX_SIZE 8192 -# define UDPC_DEFAULT_PROTOCOL_ID 1357924680 // 0x50f04948 +// other defines continued # ifndef UDPC_LIBSODIUM_ENABLED -# define crypto_sign_PUBLICKEYBYTES 1 -# define crypto_sign_SECRETKEYBYTES 1 -# define crypto_sign_BYTES 1 +# ifndef crypto_sign_PUBLICKEYBYTES +# define crypto_sign_PUBLICKEYBYTES 1 +# endif +# ifndef crypto_sign_SECRETKEYBYTES +# define crypto_sign_SECRETKEYBYTES 1 +# endif +# ifndef crypto_sign_BYTES +# define crypto_sign_BYTES 1 +# endif # endif #endif // DOXYGEN_SHOULD_SKIP_THIS @@ -103,27 +111,70 @@ typedef struct UDPC_Context *UDPC_HContext; typedef enum { UDPC_SILENT, UDPC_ERROR, UDPC_WARNING, UDPC_INFO, UDPC_VERBOSE, UDPC_DEBUG } UDPC_LoggingType; +/*! + * \brief Data identifying a peer via addr, port, and scope_id + * + * This struct needn't be used directly; use UDPC_create_id(), + * UDPC_create_id_full(), UDPC_create_id_anyaddr(), or UDPC_create_id_easy() to + * create one. This struct does not hold dynamic data, so there is no need to + * free it. + */ typedef struct { UDPC_IPV6_ADDR_TYPE addr; uint32_t scope_id; uint16_t port; } UDPC_ConnectionId; +/*! + * \brief Data representing a received/sent packet + */ typedef struct { + /*! + * A char array of size \p UDPC_PACKET_MAX_SIZE. Note that the received data + * will probably use up less data than the full size of the array. The + * actual size of the received data is \p dataSize. + */ // id is stored at offset 8, size 4 (uint32_t) even for "empty" PktInfos char data[UDPC_PACKET_MAX_SIZE]; - /* - * 0x1 - connect - * 0x2 - ping - * 0x4 - no_rec_chk - * 0x8 - resending + /*! + * \brief Flags indication some additional information about the received + * packet. + * + * The following list indicates what each used bit in \p flags refers to. + * - 0x1: Is an initiate-connection packet + * - 0x2: Is a ping packet + * - 0x4: Is a packet that will not be re-sent if not received + * - 0x8: Is a packet that was re-sent */ uint32_t flags; - uint16_t dataSize; /// zero if invalid + /*! + * \brief The size in bytes of the received packet's data inside the \p data + * array member variable. + * + * If this variable is zero, then this packet is invalid, or an empty packet + * was received. + */ + uint16_t dataSize; + /// The \p UDPC_ConnectionId of the sender UDPC_ConnectionId sender; + /// The \p UDPC_ConnectionId of the receiver UDPC_ConnectionId receiver; } UDPC_PacketInfo; +/*! + * \brief An enum describing the type of event. + * + * Note that only the following values will be presented when using + * UDPC_get_event() + * - UDPC_ET_NONE: No events have ocurred + * - UDPC_ET_CONNECTED: A peer has initiated a connection + * - UDPC_ET_DISCONNECTED: A peer has disconnected + * - UDPC_ET_GOOD_MODE: The connection has switched to "good mode" + * - UDPC_ET_BAD_MODE: The connection has switched to "bad mode" + * + * The other unmentioned enum values are used internally, and should never be + * returned in a call to UDPC_get_event(). + */ typedef enum { UDPC_ET_NONE, UDPC_ET_REQUEST_CONNECT, @@ -135,6 +186,16 @@ typedef enum { UDPC_ET_REQUEST_CONNECT_PK } UDPC_EventType; +/*! + * \brief A struct containing information related to the type of event + * + * Note that instances of this struct received from a call to UDPC_get_event() + * will not store any useful data in its union member variable \p v (it will + * only be used internally). + * Thus, all events received through a call to UDPC_get_event() will contain a + * valid UDPC_ConnectionId \p conId that identifies the peer that the event is + * referring to. + */ typedef struct { UDPC_EventType type; UDPC_ConnectionId conId; @@ -190,42 +251,195 @@ UDPC_ConnectionId UDPC_create_id_easy(const char *addrString, uint16_t port); * \param isUsingLibsodium Set to non-zero if libsodium verification of packets * should be enabled (fails if libsodium support was not compiled) * - * The received UDPC_HContext must be freed with a call to UDPC_destroy(). + * UDPC_is_valid_context() may be used to check if the context was successfully + * created. + * + * \warning The received UDPC_HContext must be freed with a call to UDPC_destroy(). */ UDPC_HContext UDPC_init(UDPC_ConnectionId listenId, int isClient, int isUsingLibsodium); +/*! + * \brief Creates an UDPC_HContext that holds state for connections that + * auto-updates via a thread. + * + * By default, the update interval is set to 8 milliseconds. + * + * \param listenId The addr and port to listen on (contained in a + * UDPC_ConnectionId) + * \param isClient Whether or not this instance is a client or a server + * \param isUsingLibsodium Set to non-zero if libsodium verification of packets + * should be enabled (fails if libsodium support was not compiled) + * + * UDPC_is_valid_context() may be used to check if the context was successfully + * created. + * + * \warning The received UDPC_HContext must be freed with a call to UDPC_destroy(). + */ UDPC_HContext UDPC_init_threaded_update( UDPC_ConnectionId listenId, int isClient, int isUsingLibsodium); +/*! + * \brief Creates an UDPC_HContext that holds state for connections that + * auto-updates via a thread at a specified interval. + * + * \param listenId The addr and port to listen on (contained in a + * UDPC_ConnectionId) + * \param isClient Whether or not this instance is a client or a server + * \param updateMS The interval to update at in milliseconds (clamped at a + * minimum of 4 ms and a maximum of 333 ms) + * \param isUsingLibsodium Set to non-zero if libsodium verification of packets + * should be enabled (fails if libsodium support was not compiled) + * + * UDPC_is_valid_context() may be used to check if the context was successfully + * created. + * + * \warning The received UDPC_HContext must be freed with a call to UDPC_destroy(). + */ UDPC_HContext UDPC_init_threaded_update_ms( UDPC_ConnectionId listenId, int isClient, int updateMS, int isUsingLibsodium); +/*! + * \brief Enables auto updating on a separate thread for the given UDPC_HContext + * + * By default, the update interval is set to 8 milliseconds. + * + * \param ctx The context to enable auto updating for + * \return non-zero if auto updating is enabled. If the context already had auto + * updating enabled, this function will return zero. + */ int UDPC_enable_threaded_update(UDPC_HContext ctx); +/*! + * \brief Enables auto updating on a separate thread for the given UDPC_HContext + * with the specified update interval + * + * \param ctx The context to enable auto updating for + * \param updateMS The interval to update at in milliseconds (clamped at a + * minimum of 4 ms and a maximum of 333 ms) + * \return non-zero if auto updating is enabled. If the context already had auto + * updating enabled, this function will return zero. + */ int UDPC_enable_threaded_update_ms(UDPC_HContext ctx, int updateMS); +/*! + * \brief Disables auto updating on a separate thread for the given + * UDPC_HContext + * + * \param ctx The context to disable auto updating for + * \return non-zero if auto updating is disabled. If the context already had + * auto updating disabled, this function will return zero. + */ int UDPC_disable_threaded_update(UDPC_HContext ctx); +/*! + * \brief Checks if the given UDPC_HContext is valid (successfully initialized) + * + * \return non-zero if the given context is valid + */ int UDPC_is_valid_context(UDPC_HContext ctx); +/*! + * \brief Cleans up the UDPC_HContext + * + * If auto updating was enabled for the given context, it will gracefully stop + * the thread before cleaning up the context. + * + * \warning This function must be called after a UDPC_HContext is no longer used + * to avoid memory leaks. + */ void UDPC_destroy(UDPC_HContext ctx); +/*! + * \brief Updates the context + * + * Updating consists of: + * - Checking if peers have timed out + * - Handling requests to connect to server peers as a client + * - Sending packets to connected peers + * - Receiving packets from connected peers + * - Calculating round-trip-time (RTT) to peers + * - Checking if a peer has not received a packet and queuing that packet to be + * resent (this is done by using an ack) + * + * If auto updating was enabled for the context, then there is no need to call + * this function. + * + * Note that the context can only receive at most one packet per call to update + * (due to the fact that UDPC created its UDP socket to not block on receive + * checks). This is why it is expected to either call this function several + * times a second (such as in a game's update loop), or have auto-updating + * enabled via UDPC_init_threaded_update(), UDPC_init_threaded_update_ms(), + * UDPC_enable_threaded_update(), or UDPC_enable_threaded_update_ms(). + */ void UDPC_update(UDPC_HContext ctx); +/*! + * \brief Initiate a connection to a server peer + * + * Note that this function does nothing on a server context. + * + * \param ctx The context to initiate a connection from + * \param connectionId The server peer to initiate a connection to + * \param enableLibSodium If packet headers should be verified with the server + * peer (Fails if UDPC was not compiled with libsodium support) + */ void UDPC_client_initiate_connection( UDPC_HContext ctx, UDPC_ConnectionId connectionId, int enableLibSodium); +/*! + * \brief Initiate a connection to a server peer with an expected public key + * + * Note that this function does nothing on a server context. + * + * \param ctx The context to initiate a connection from + * \param connectionId The server peer to initiate a connection to + * \param serverPK A pointer to the public key that the server is expected to + * use (if the server does not use this public key, then the connection will + * fail; it must point to a buffer of size \p crypto_sign_PUBLICKEYBYTES) + * + * This function assumes that support for libsodium was enabled when UDPC was + * compiled. If it has not, then this function will fail. + */ void UDPC_client_initiate_connection_pk( UDPC_HContext ctx, UDPC_ConnectionId connectionId, unsigned char *serverPK); +/*! + * \brief Queues a packet to be sent to the specified peer + * + * Note that there must already be an established connection with the peer. A + * client can establish a connection to a server peer via a call to + * UDPC_client_initiate_connection() or UDPC_client_initiate_connection_pk(). A + * server must receive an initiate-connection-packet from a client to establish + * a connection (sent by previously mentioned UDPC_client_initiate_* functions). + * + * \param ctx The context to send a packet on + * \param destinationId The peer to send a packet to + * \param isChecked Set to non-zero if the packet should be re-sent if the peer + * doesn't receive it + * \param data A pointer to data to be sent in a packet + * \param size The size in bytes of the data to be sent + */ void UDPC_queue_send(UDPC_HContext ctx, UDPC_ConnectionId destinationId, int isChecked, void *data, uint32_t size); +/*! + * \brief Gets the size of the data structure holding queued packets + * + * Note that a UDPC context holds a different data structure per established + * connection that holds a limited amount of packets to send. If a connection's + * queue is full, it will not be removed from the main queue that this function + * (and UDPC_queue_send()) uses. The queue that this function refers to does not + * have an imposed limit as it is implemented as a thread-safe linked list (data + * is dynamically stored on the heap). Also note that this queue holds packets + * for all connections this context maintains. Thus if one connection has free + * space, then it may partially remove packets only destined for that connection + * from the queue this function refers to. + */ unsigned long UDPC_get_queue_send_current_size(UDPC_HContext ctx); int UDPC_set_accept_new_connections(UDPC_HContext ctx, int isAccepting);