#include #include /* ------------------------------------------------------------------------------------------------------------------ */ /* */ c_err_t c_TcpClient_Connect(c_TcpClient_t* self, int family, const char* server, const char* port, void* args, c_Allocator_t* allocator) { if (!self || (!server && !port)) return C_ERR_PARAM; self->allocator = allocator ? *allocator : c_DefaultAllocator; self->sock = C_INVALID_SOCKET; self->is_linked = C_FALSE; /* 1. Resolve network address dynamically using your custom c_AddrInfo_t wrapper */ c_AddrInfo_t addr_resolver; c_err_t err = c_AddrInfo_Init(&addr_resolver, family, SOCK_STREAM, 0, server, port); if (err != C_ERR_OK) { c_Socket_Destroy(); return C_ERR_FAIL; } c_AddrInfo_t curr_node = addr_resolver; c_bool_t connected = C_FALSE; /* 2. Traverse the resolved address link chain until a connection succeeds */ while (curr_node.addrinfo_p != NULL) { /* Open the system socket using descriptors from addrinfo */ self->sock = socket( c_AddrInfo_GetFamily(&curr_node), c_AddrInfo_GetSockType(&curr_node), c_AddrInfo_GetProtocol(&curr_node) ); if (c_Socket_IsValid(self->sock)) { /* Execute synchronous network connect transaction handshake */ int rc = connect(self->sock, curr_node.addrinfo_p->ai_addr, (int)curr_node.addrinfo_p->ai_addrlen); if (rc != C_SOCKET_ERROR) { connected = C_TRUE; break; /* Route successfully established, break chain safely */ } /* Connection failed on this node: close socket using custom macro helper */ c_Socket_Close(self->sock); } /* Shift forward to parse alternative network address frames */ c_AddrInfo_GetNext(&curr_node, &curr_node); } c_AddrInfo_Destroy(&addr_resolver); if (!connected) { if (self->fnOnError) { self->fnOnError(self, C_TCPCLIENT_ERR_ON_CONNECT, args); } return C_ERR_FAIL; /* Host unreachable or timed out */ } self->is_linked = C_TRUE; if (self->fnOnConnect) { if (!self->fnOnConnect(self, args)) { self->is_linked = C_FALSE; } } return C_SUCCESS; } 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) { if (!self || !self->is_linked || !c_Socket_IsValid(self->sock) || !buffer) return C_ERR_PARAM; if (bytes_to_send == 0) { if (out_bytes_sent) *out_bytes_sent = 0; return C_SUCCESS; } c_size_t total_sent = 0; const char* ptr = (const char*)buffer; /* Loop until the complete raw buffer payload block is flushed out to the wire */ while (total_sent < bytes_to_send) { c_size_t remaining = bytes_to_send - total_sent; #if defined(_WIN32) || defined(_WIN64) int n = send(self->sock, ptr + total_sent, (int)remaining, 0); #else #ifdef MSG_NOSIGNAL ssize_t n = send(self->sock, ptr + total_sent, (size_t)remaining, MSG_NOSIGNAL); /* Guard against SIGPIPE */ #else ssize_t n = send(self->sock, ptr + total_sent, (size_t)remaining, 0); #endif #endif if (n == C_SOCKET_ERROR) { if (out_bytes_sent) *out_bytes_sent = total_sent; return C_ERR_FAIL; /* Network pipeline transmission error */ } total_sent += (c_size_t)n; } if (out_bytes_sent) *out_bytes_sent = total_sent; return C_SUCCESS; } 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) { if (!self || !self->is_linked || !c_Socket_IsValid(self->sock) || !buffer || max_bytes_to_read == 0) return C_ERR_PARAM; #if defined(_WIN32) || defined(_WIN64) int n = recv(self->sock, (char*)buffer, (int)max_bytes_to_read, 0); #else ssize_t n = recv(self->sock, buffer, (size_t)max_bytes_to_read, 0); #endif if (n == 0) { if (out_bytes_read) *out_bytes_read = 0; return C_ERR_EOF; /* Connection clean cut closed by peer node */ } if (n == C_SOCKET_ERROR) { if (out_bytes_read) *out_bytes_read = 0; return C_ERR_FAIL; /* Network pipeline failure */ } if (out_bytes_read) *out_bytes_read = (c_size_t)n; return C_SUCCESS; } void c_TcpClient_Destroy(c_TcpClient_t* self) { if (!self) return; self->is_linked = C_FALSE; if (c_Socket_IsValid(self->sock)) { c_Socket_Close(self->sock); } if (self->reactor) c_TcpClientReactor_Destroy(self->reactor); } c_err_t c_TcpClient_PollEvents(c_TcpClient_t* self, long timeout_ms, void* args) { if (!self || !self->reactor || self->is_linked==C_FALSE) return C_ERR_PARAM; return c_TcpClientReactor_Poll(self->reactor, timeout_ms, self, args); }