93 lines
3.0 KiB
C
93 lines
3.0 KiB
C
#include <c_TcpServer.h>
|
|||
|
|
|
||
|
|
#include "c_SocketStream.h"
|
||
|
|
|
||
|
|
c_err_t c_TcpServer_Init(c_TcpServer_t* self, int family, uint16_t port, int backlog, c_Allocator_t* allocator) {
|
||
|
|
if (!self) return C_ERR_PARAM;
|
||
|
|
|
||
|
|
self->allocator = allocator ? *allocator : c_DefaultAllocator;
|
||
|
|
self->listen_sock = C_INVALID_SOCKET;
|
||
|
|
self->reactor = NULL;
|
||
|
|
self->is_running = C_FALSE;
|
||
|
|
|
||
|
|
/* 2. Instantiate the listening socket descriptor using the explicit parameter map */
|
||
|
|
self->listen_sock = socket(family, SOCK_STREAM, IPPROTO_IP);
|
||
|
|
if (self->listen_sock == C_INVALID_SOCKET) return C_ERR_FAIL;
|
||
|
|
|
||
|
|
/* 3. Enable SO_REUSEADDR to avoid socket bind crashes on rapid restarts */
|
||
|
|
c_Socket_SetReuseAddr(self->listen_sock, true);
|
||
|
|
|
||
|
|
/* 4. Configure dual-stack sockaddr union variants based on the address family */
|
||
|
|
c_SockAddr_t bind_addr;
|
||
|
|
if (c_SockAddr_Init(&bind_addr, family, 0, port)!=C_ERR_OK) {
|
||
|
|
c_TcpServer_Destroy(self);
|
||
|
|
return C_ERR_FAIL;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (family == AF_INET6) {
|
||
|
|
/* Optional optimization: allow dual-stack IPv4 mapping on the IPv6 socket if supported */
|
||
|
|
c_Socket_SetIPV6Only(self->listen_sock, false);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 6. Bind the structural endpoint socket down to the network stack */
|
||
|
|
if (c_SocketUtil_BindWithSockAddr(self->listen_sock, &bind_addr) != C_ERR_OK) {
|
||
|
|
c_TcpServer_Destroy(self);
|
||
|
|
return C_ERR_FAIL;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 7. Switch the socket handle to listener mode with a generous backlog size */
|
||
|
|
if (listen(self->listen_sock, backlog) == C_SOCKET_ERROR) {
|
||
|
|
c_TcpServer_Destroy(self);
|
||
|
|
return C_ERR_FAIL;
|
||
|
|
}
|
||
|
|
|
||
|
|
self->is_running = C_TRUE;
|
||
|
|
return C_SUCCESS;
|
||
|
|
}
|
||
|
|
|
||
|
|
c_err_t c_TcpServer_SetReactor(c_TcpServer_t* self, c_TcpReactor_t* reactor) {
|
||
|
|
if (!self || !reactor) return C_ERR_PARAM;
|
||
|
|
self->reactor = reactor;
|
||
|
|
/* Automatically add our master listener socket to watch for read/inbound events */
|
||
|
|
return c_TcpReactor_Add(self->reactor, self->listen_sock, 1);
|
||
|
|
}
|
||
|
|
|
||
|
|
c_err_t c_TcpServer_Run(c_TcpServer_t* self, long timeout_ms, void* args) {
|
||
|
|
if (!self || !self->reactor || !self->is_running) return C_ERR_PARAM;
|
||
|
|
return c_TcpReactor_Poll(self->reactor, timeout_ms, self, args);
|
||
|
|
}
|
||
|
|
|
||
|
|
void c_TcpServer_Destroy(c_TcpServer_t* self) {
|
||
|
|
if (!self) return;
|
||
|
|
self->is_running = C_FALSE;
|
||
|
|
if (self->reactor) {
|
||
|
|
c_TcpReactor_Destroy(self->reactor);
|
||
|
|
self->reactor = NULL;
|
||
|
|
}
|
||
|
|
if (self->listen_sock != C_INVALID_SOCKET) {
|
||
|
|
c_Socket_Close(self->listen_sock);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
const char* c_TcpServer_GetErrorString(int code) {
|
||
|
|
switch (code) {
|
||
|
|
case C_ERR_OK: {
|
||
|
|
return "OK";
|
||
|
|
}
|
||
|
|
case C_TCPSERVER_ERR_ON_ACCEPT: {
|
||
|
|
return "ACCEPT";
|
||
|
|
}
|
||
|
|
case C_TCPSERVER_ERR_ON_POLL: {
|
||
|
|
return "POLL";
|
||
|
|
}
|
||
|
|
case C_TCPSERVER_ERR_ON_POLLERR: {
|
||
|
|
return "POLLERR";
|
||
|
|
}
|
||
|
|
case C_TCPSERVER_ERR_ON_RECV: {
|
||
|
|
return "RECV";
|
||
|
|
}
|
||
|
|
default: {
|
||
|
|
return "UNKNOWN";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|