#ifndef INCLUDED_C_TCPCLIENT_H #define INCLUDED_C_TCPCLIENT_H #ifndef INCLUDED_C_SOCKET_H #include #endif /*INCLUDED_C_SOCKET_H*/ #ifndef INCLUDED_C_ALLOCATOR_H #include #endif /*INCLUDED_C_ALLOCATOR_H*/ /* ------------------------------------------------------------------------------------------------------------------ */ /* */ #define C_TCPCLIENT_ERR_ON_POLL 1001 #define C_TCPCLIENT_ERR_ON_ACCEPT 1002 #define C_TCPCLIENT_ERR_ON_POLLERR 1003 #define C_TCPCLIENT_ERR_ON_RECV 1004 #define C_TCPCLIENT_ERR_ON_CONNECT 1005 #define C_TCPCLIENT_INITIALIZE {0} /* ------------------------------------------------------------------------------------------------------------------ */ /* */ typedef struct c_TcpClient_t c_TcpClient_t; typedef bool (*c_TcpClient_OnConnect_t)(c_TcpClient_t* client, void* args); typedef bool (*c_TcpClient_OnData_t)(c_TcpClient_t* client, void* args); typedef void (*c_TcpClient_OnDisconnect_t)(c_TcpClient_t* client, void* args); typedef void (*c_TcpClient_OnError_t)(c_TcpClient_t* client, c_err_t error_code, void* args); typedef struct c_TcpClientReactor_t c_TcpClientReactor_t; typedef struct { c_err_t (*add)(c_TcpClientReactor_t* self, c_socket_t sock, uint32_t events); c_err_t (*remove)(c_TcpClientReactor_t* self, c_socket_t sock); c_err_t (*poll)(c_TcpClientReactor_t* self, long timeout_ms, c_TcpClient_t* client, void* args); void (*destroy)(c_TcpClientReactor_t* self); } c_TcpClientReactorVtbl_t; struct c_TcpClientReactor_t { const c_TcpClientReactorVtbl_t* vtbl; }; struct c_TcpClient_t { c_socket_t sock; c_bool_t is_linked; c_TcpClientReactor_t* reactor; c_TcpClient_OnConnect_t fnOnConnect; c_TcpClient_OnData_t fnOnData; c_TcpClient_OnDisconnect_t fnOnDisconnect; c_TcpClient_OnError_t fnOnError; c_Allocator_t allocator; }; /* ------------------------------------------------------------------------------------------------------------------ */ /* */ C_STATIC_FORCE_INLINE c_err_t c_TcpClientReactor_Add(c_TcpClientReactor_t* self, c_socket_t sock, uint32_t events) { if (!self || !self->vtbl || !self->vtbl->add || !c_Socket_IsValid(sock)) return C_ERR_PARAM; return self->vtbl->add(self, sock, events); } C_STATIC_FORCE_INLINE c_err_t c_TcpClientReactor_Remove(c_TcpClientReactor_t* self, c_socket_t sock) { if (!self || !self->vtbl || !self->vtbl->remove || !c_Socket_IsValid(sock)) return C_ERR_PARAM; return self->vtbl->remove(self, sock); } C_STATIC_FORCE_INLINE c_err_t c_TcpClientReactor_Poll(c_TcpClientReactor_t* self, long timeout_ms, c_TcpClient_t* client, void* args) { if (!self || !self->vtbl || !self->vtbl->poll || !client) return C_ERR_PARAM; return self->vtbl->poll(self, timeout_ms, client, args); } C_STATIC_FORCE_INLINE void c_TcpClientReactor_Destroy(c_TcpClientReactor_t* self) { if (!self || !self->vtbl || !self->vtbl->destroy) return; self->vtbl->destroy(self); } /* ------------------------------------------------------------------------------------------------------------------ */ /* */ /** * @brief Establishes a synchronous stream connection to a remote server host. * @param family Address family (e.g., AF_INET, AF_INET6, or AF_UNSPEC) * @param server Host destination string (e.g., "127.0.0.1" or "domain.com") * @param port Service port string identifier (e.g., "8080") * @param args User defined arguments * @param allocator Explicit allocator context used to configure socket instances */ c_err_t c_TcpClient_Connect(c_TcpClient_t* self, int family, const char* server, const char* port, void* args, c_Allocator_t* allocator); /** * @brief Sends a raw data block across the socket, looping until the full buffer is dispatched. * @param buffer Pointer to the source data block payload to transmit. * @param bytes_to_send Exact size threshold of the payload block in bytes. * @param out_bytes_sent Receives the actual number of bytes written out to the wire. */ c_err_t c_TcpClient_Send(c_TcpClient_t* self, const void* buffer, c_size_t bytes_to_send, c_size_t* out_bytes_sent); /** * @brief Reads a data block up to the requested size from the socket channel. * @param buffer Pointer to the destination buffer to hold incoming data. * @param max_bytes_to_read Upper ceiling threshold size of bytes to pull. * @param out_bytes_read Receives the actual number of bytes fetched from the wire. */ c_err_t c_TcpClient_Recv(c_TcpClient_t* self, void* buffer, c_size_t max_bytes_to_read, c_size_t* out_bytes_read); /** * @brief Safely shuts down pipelines, closes active sockets, and recycles memory. */ void c_TcpClient_Destroy(c_TcpClient_t* self); /** * @brief Executes a non-recursive, single-pass asynchronous polling event dispatch iteration for the client. */ c_err_t c_TcpClient_PollEvents(c_TcpClient_t* self, long timeout_ms, void* args); /* ------------------------------------------------------------------------------------------------------------------ */ /* */ C_STATIC_FORCE_INLINE void c_TcpClient_SetReactor(c_TcpClient_t* self, c_TcpClientReactor_t* reactor) { if (!self || !reactor) return; self->reactor = reactor; } C_STATIC_FORCE_INLINE void c_TcpClient_SetOnConnect(c_TcpClient_t* self, c_TcpClient_OnConnect_t fnOnConnect) { if (!self ) return; self->fnOnConnect = fnOnConnect; } C_STATIC_FORCE_INLINE void c_TcpClient_SetOnData(c_TcpClient_t* self, c_TcpClient_OnData_t fnOnData) { if (!self ) return; self->fnOnData = fnOnData; } C_STATIC_FORCE_INLINE void c_TcpClient_SetOnDisconnect(c_TcpClient_t* self, c_TcpClient_OnDisconnect_t fnOnDisconnect) { if (!self ) return; self->fnOnDisconnect = fnOnDisconnect; } C_STATIC_FORCE_INLINE void c_TcpClient_SetOnError(c_TcpClient_t* self, c_TcpClient_OnError_t fnOnError) { if (!self ) return; self->fnOnError = fnOnError; } #endif /*INCLUDED_C_TCPCLIENT_H*/