TcpClient/Reactor 框架
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
#include <c_TcpClient.h>
|
||||
#include <c_AddrInfo.h>
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user