#ifndef UDPCONNECTION_H #define UDPCONNECTION_H /*! * Any function or struct starting with "UDPC_INTERNAL" should never be used, * as they are used internally by this library. */ #include #include #include #include #include "UDPC_Defines.h" #include "UDPC_Deque.h" #include "UDPC_HashMap.h" #if UDPC_PLATFORM == UDPC_PLATFORM_WINDOWS #include #define CleanupSocket(x) closesocket(x) #elif UDPC_PLATFORM == UDPC_PLATFORM_MAC || UDPC_PLATFORM == UDPC_PLATFORM_LINUX #include #include #include #include #define CleanupSocket(x) close(x) #else #define CleanupSocket(x) ((void)0) #endif #define UDPC_ATOSTR_BUF_SIZE 16 /// (void *userData, uint32_t address) /*! * Note address is in network byte order (usually big-endian) */ typedef void (*UDPC_callback_connected)(void*, uint32_t); /// (void *userData, uint32_t address) /*! * Note address is in network byte order (usually big-endian) */ typedef void (*UDPC_callback_disconnected)(void*, uint32_t); /// (void *userData, uint32_t address, char *packetData, uint32_t packetSize) /*! * The data pointed to by the packetData argument is to data internally managed * by the UDPC_Context. It will change every time this callback is called so do * not depend on it persisting. This means you should copy the data out of it * when the callback is invoked and work with the copied data. */ typedef void (*UDPC_callback_received)(void*, uint32_t, char*, uint32_t); /// This struct should not be used outside of this library typedef struct { uint32_t addr; // in network order (big-endian) uint32_t id; /* * 0x1 - is resending * 0x2 - is check received packet * 0x4 - has been re-sent * 0x8 - initiate connection packet */ uint32_t flags; char *data; // no-header in sendPktQueue and receivedPackets, header in sentPkts uint32_t size; struct timespec sent; } UDPC_INTERNAL_PacketInfo; /// This struct should not be used outside of this library typedef struct { /* * 0x1 - trigger send * 0x2 - is good mode * 0x4 - is good rtt * 0x8 - initiating connection to server * 0x10 - is id set */ uint32_t flags; uint32_t id; uint32_t lseq; uint32_t rseq; uint32_t ack; float timer; float toggleT; float toggleTimer; float toggledTimer; uint32_t addr; // in network order (big-endian) uint16_t port; UDPC_Deque *sentPkts; UDPC_Deque *sendPktQueue; UDPC_Deque *priorityPktQueue; struct timespec received; struct timespec sent; float rtt; } UDPC_INTERNAL_ConnectionData; /// This struct should not be used externally, only passed to functions that require it typedef struct { int isThreaded; /* * 0x1 - unused * 0x2 - is client * 0x4 - log errors * 0x8 - log warnings * 0x10 - log info * 0x20 - log verbose * 0x40 - accept new connections */ uint32_t flags; /* * 0x1 - thread should stop */ uint32_t threadFlags; uint32_t protocolID; uint32_t error; int socketHandle; struct sockaddr_in socketInfo; thrd_t threadHandle; mtx_t tCVMtx; mtx_t tflagsMtx; cnd_t threadCV; UDPC_HashMap *conMap; UDPC_HashMap *idMap; struct timespec lastUpdated; char atostrBuf[UDPC_ATOSTR_BUF_SIZE]; char recvBuf[UDPC_PACKET_MAX_SIZE]; UDPC_Deque *connectedEvents; UDPC_Deque *disconnectedEvents; UDPC_Deque *receivedPackets; UDPC_callback_connected callbackConnected; void *callbackConnectedUserData; UDPC_callback_disconnected callbackDisconnected; void *callbackDisconnectedUserData; UDPC_callback_received callbackReceived; void *callbackReceivedUserData; } UDPC_Context; /// This struct should not be used outside of this library typedef struct { struct timespec tsNow; float dt; UDPC_Deque *removedQueue; UDPC_Context *ctx; } UDPC_INTERNAL_update_struct; /// Creates a new UDPC_Context, for establishing a connection via UDP /*! * Callbacks must be set to use UDPC effectively, using the following functions: * - UDPC_set_callback_connected() * - UDPC_set_callback_disconnected() * - UDPC_set_callback_received() * * Clients should also use UDPC_client_initiate_connection() to let UDPC know * what server to connect to. * * UDPC_update() must be called periodically (ideally at 30 updates per second * or faster), to send and receive UDP packets, and establish new connections * when enabled. An init variant "UDPC_init_threaded_update()" is available * which will call update periodically on a separate thread. * * UDPC_check_events() must also be called to call the set callbacks for * connected, disconnected, and received events. * * When finished using UDPC, UDPC_destroy must be called on the context to free * resources. */ UDPC_Context* UDPC_init(uint16_t listenPort, uint32_t listenAddr, int isClient); /// Creates a new UDPC_Context, where a thread is created to update UDPC on its own /*! * If using a UDPC_Context created by this init variant, UDPC_update() must not * be manually called, as it is called automatically by a separate thread. * * See the documentation for UDPC_init() for more details. */ UDPC_Context* UDPC_init_threaded_update(uint16_t listenPort, uint32_t listenAddr, int isClient); /// This fn must be called on a UDPC_Context to free resources void UDPC_destroy(UDPC_Context *ctx); void UDPC_INTERNAL_destroy_conMap(void *unused, uint32_t addr, char *data); /// Sets the callback for connected events void UDPC_set_callback_connected( UDPC_Context *ctx, UDPC_callback_connected fptr, void *userData); /// Sets the callback for disconnected events void UDPC_set_callback_disconnected( UDPC_Context *ctx, UDPC_callback_disconnected fptr, void *userData); /// Sets the callback for received packet events void UDPC_set_callback_received( UDPC_Context *ctx, UDPC_callback_received fptr, void *userData); /// Invokes callbacks based on events that have ocurred during UDPC_update() void UDPC_check_events(UDPC_Context *ctx); /// Tells UDPC to initiate a connection to a server void UDPC_client_initiate_connection(UDPC_Context *ctx, uint32_t addr, uint16_t port); /*! * \brief Queues a packet to send to a connected peer * Note addr is expected to be in network-byte-order (big-endian). * If isChecked is non-zero, UDPC will attempt to resend the packet if peer has * not received it within UDPC_PACKET_TIMEOUT_SEC seconds. * \return non-zero on success */ int UDPC_queue_send( UDPC_Context *ctx, uint32_t addr, uint32_t isChecked, void *data, uint32_t size); /*! * \brief get the number of packets that can be queued to the addr * \return number of queueable packets or 0 if connection has not been established */ int UDPC_get_queue_send_available(UDPC_Context *ctx, uint32_t addr); /// Returns non-zero if UDPC is accepting new connections int UDPC_get_accept_new_connections(UDPC_Context *ctx); /// Set isAccepting to non-zero to let UDPC accept new connections /*! * Set isAccepting to zero to prevent UDPC from accepting new connections. */ void UDPC_set_accept_new_connections(UDPC_Context *ctx, int isAccepting); /// Gets the currently set protocol id uint32_t UDPC_get_protocol_id(UDPC_Context *ctx); /// Sets the protocol id /*! * Note that UDPC can only connect to other UDPC instances that use the same * protocol id. */ void UDPC_set_protocol_id(UDPC_Context *ctx, uint32_t id); /*! * \brief Get the currently set error code, and clear it internally * Error codes and their meanings are defined in UDPC_Defines.h . * Use UDPC_get_error_str() to get a string describing the error. */ uint32_t UDPC_get_error(UDPC_Context *ctx); /// Returns a string describing the error code for UDPC const char* UDPC_get_error_str(uint32_t error); /*! * 0 - log nothing * 1 - log only errors * 2 - log only errors and warnings * 3 - log errors, warnings, and info * 4+ - log everything * * By default, erros and warnings are logged. */ void UDPC_set_logging_type(UDPC_Context *ctx, uint32_t logType); /// If threaded, this function is called automatically void UDPC_update(UDPC_Context *ctx); void UDPC_INTERNAL_update_to_rtt_si(void *userData, uint32_t addr, char *data); void UDPC_INTERNAL_update_send(void *userData, uint32_t addr, char *data); void UDPC_INTERNAL_update_rtt( UDPC_Context *ctx, UDPC_INTERNAL_ConnectionData *cd, uint32_t rseq, struct timespec *tsNow); void UDPC_INTERNAL_check_pkt_timeout( UDPC_Context *ctx, UDPC_INTERNAL_ConnectionData *cd, uint32_t rseq, uint32_t ack, struct timespec *tsNow); float UDPC_INTERNAL_ts_diff(struct timespec *ts0, struct timespec *ts1); int UDPC_INTERNAL_threadfn(void *context); /* * 0x1 - is ping * 0x2 - is resending * 0x4 - is checked received packet * 0x8 - is init connection packet */ void UDPC_INTERNAL_prepare_pkt( void *data, uint32_t protocolID, uint32_t conID, uint32_t rseq, uint32_t ack, uint32_t *seqID, int flags); /*! * 0 - error * 1 - warning * 2 - info * 3 - verbose */ void UDPC_INTERNAL_log(UDPC_Context *ctx, uint32_t level, const char *msg, ...); char* UDPC_INTERNAL_atostr(UDPC_Context *ctx, uint32_t addr); uint32_t UDPC_INTERNAL_generate_id(UDPC_Context *ctx); /*1 * \brief Converts a IPV4 string to a 32-bit unsigned integer address in big-endian * \return 0 if string is invalid, address in big-endian format otherwise */ uint32_t UDPC_strtoa(const char *addrStr); #endif