21#include <netinet/tcp.h>
40static ssize_t network_platform_send(
socket_t sockfd,
const void *data,
size_t len) {
46 int raw_sent = send(sockfd, (
const char *)data, (
int)len, 0);
50 if (raw_sent == SOCKET_ERROR) {
55 sent = (ssize_t)raw_sent;
57 if (raw_sent > (
int)len) {
63#elif defined(MSG_NOSIGNAL)
64 return send(sockfd, data, len, MSG_NOSIGNAL);
67 return send(sockfd, data, len, 0);
78static ssize_t network_platform_recv(
socket_t sockfd,
void *buf,
size_t len) {
80 int raw_received = recv(sockfd, (
char *)buf, (
int)len, 0);
82 if (raw_received == SOCKET_ERROR) {
85 return (ssize_t)raw_received;
87 return recv(sockfd, buf, len, 0);
96static const char *network_get_error_string(
int error) {
112static int network_handle_send_error(
int error) {
116 if (error ==
EPIPE) {
129static int network_handle_recv_error(
int error) {
131 if (error == WSAEWOULDBLOCK) {
134 if (error == WSAEINTR) {
136 log_debug(
"recv interrupted by signal, retrying");
143 if (error ==
EINTR) {
145 log_debug(
"recv interrupted by signal, retrying");
148 if (error ==
EBADF) {
163static int network_handle_select_error(
int result) {
174 int error = WSAGetLastError();
175 if (error == WSAEINTR) {
177 log_debug(
"select interrupted by signal, retrying");
184 log_debug(
"select interrupted by signal, retrying");
206 size_t total_sent = 0;
207 const char *data_ptr = (
const char *)data;
209 while (total_sent < len) {
211 size_t bytes_to_send = len - total_sent;
212 const size_t MAX_CHUNK_SIZE = 65536;
213 if (bytes_to_send > MAX_CHUNK_SIZE) {
214 bytes_to_send = MAX_CHUNK_SIZE;
219 struct timeval timeout;
227 int result =
socket_select(sockfd, NULL, &write_fds, NULL, &timeout);
233 if (network_handle_select_error(result)) {
246 ssize_t sent = network_platform_send(sockfd, data_ptr + total_sent, bytes_to_send);
250 if (network_handle_send_error(error)) {
257 total_sent += (size_t)sent;
261 return (ssize_t)total_sent;
274 log_error(
"NETWORK_DEBUG: recv_with_timeout called with INVALID_SOCKET_VALUE");
280 struct timeval timeout;
281 ssize_t total_received = 0;
282 char *data = (
char *)buf;
284 while (total_received < (ssize_t)len) {
292 int result =
socket_select(sockfd, &read_fds, NULL, NULL, &timeout);
298 if (network_handle_select_error(result)) {
311 size_t bytes_to_recv = len - (size_t)total_received;
312 ssize_t received = network_platform_recv(sockfd, data + total_received, bytes_to_recv);
316 log_error(
"NETWORK_DEBUG: network_platform_recv failed with error %d (errno=%d), sockfd=%d, buf=%p, len=%zu",
317 received, error, sockfd, data + total_received, bytes_to_recv);
318 if (network_handle_recv_error(error)) {
319 log_debug(
"NETWORK_DEBUG: retrying after error %d", error);
322 log_error(
"NETWORK_DEBUG: fatal error %d, giving up", error);
328 log_debug(
"Connection closed by peer during recv");
329 return total_received;
332 total_received += received;
335 return total_received;
348 struct timeval timeout;
353 timeout.tv_sec = timeout_seconds;
356 int result =
socket_select(listenfd, &read_fds, NULL, NULL, &timeout);
368 if (network_handle_select_error(result)) {
427 return (
int)accept_result;
441 struct timeval timeout;
442 timeout.tv_sec = timeout_seconds;
445 if (
socket_setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout,
sizeof(timeout)) < 0) {
449 if (
socket_setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout,
sizeof(timeout)) < 0) {
498 int failed_options = 0;
501 int send_buffer_size = 1024 * 1024;
502 if (
socket_setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &send_buffer_size,
sizeof(send_buffer_size)) < 0) {
508 int recv_buffer_size = 1024 * 1024;
509 if (
socket_setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &recv_buffer_size,
sizeof(recv_buffer_size)) < 0) {
517 if (
socket_setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &tcp_nodelay,
sizeof(tcp_nodelay)) < 0) {
523 if (failed_options >= 3) {
559 int result = connect(sockfd, addr, addrlen);
567 int error = WSAGetLastError();
568 if (error != WSAEWOULDBLOCK && error != WSAEINPROGRESS) {
579 struct timeval timeout;
584 timeout.tv_sec = timeout_seconds;
587 result =
socket_select(sockfd, NULL, &write_fds, NULL, &timeout);
611 log_warn(
"Failed to restore socket to blocking mode after connect");
asciichat_error_t error_code
⚠️‼️ Error and/or exit() when things go bad.
#define SAFE_STRERROR(errnum)
#define SET_ERRNO_SYS(code, context_msg,...)
Set error code with custom message and system error context.
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it.
__thread asciichat_error_t asciichat_errno
Thread-local current error code.
void asciichat_set_errno_with_wsa_error(asciichat_error_t code, const char *file, int line, const char *function, int wsa_error)
Set error code with Windows socket error (WSA error)
__thread asciichat_error_context_t asciichat_errno_context
Thread-local error context storage.
asciichat_error_t
Error and exit codes - unified status values (0-255)
#define log_warn(...)
Log a WARN message.
#define log_error(...)
Log an ERROR message.
#define log_debug(...)
Log a DEBUG message.
asciichat_error_t set_socket_timeout(socket_t sockfd, int timeout_seconds)
Set socket timeout.
asciichat_error_t set_socket_keepalive(socket_t sockfd)
Set socket keepalive.
#define KEEPALIVE_COUNT
Keepalive probe count (8 probes)
#define KEEPALIVE_INTERVAL
Keepalive interval in seconds (10 seconds)
asciichat_error_t set_socket_nonblocking(socket_t sockfd)
Set socket non-blocking.
ssize_t send_with_timeout(socket_t sockfd, const void *data, size_t len, int timeout_seconds)
Send data with timeout using chunked transmission.
#define network_is_test_environment()
Check if we're in a test environment.
bool connect_with_timeout(socket_t sockfd, const struct sockaddr *addr, socklen_t addrlen, int timeout_seconds)
Connect with timeout.
int accept_with_timeout(socket_t listenfd, struct sockaddr *addr, socklen_t *addrlen, int timeout_seconds)
Accept connection with timeout.
const char * network_error_string()
Get human-readable error string for network errors.
asciichat_error_t socket_configure_buffers(socket_t sockfd)
Configure socket buffers and TCP_NODELAY for optimal performance.
#define KEEPALIVE_IDLE
Keepalive idle time in seconds (60 seconds)
ssize_t recv_with_timeout(socket_t sockfd, void *buf, size_t len, int timeout_seconds)
Receive data with timeout.
🌐 Core network I/O operations with timeout support
Cross-platform socket interface for ascii-chat.
int system_errno
System errno value (if applicable, 0 otherwise)
bool has_system_error
True if system_errno is valid.
asciichat_error_t code
Error code (asciichat_error_t enum value)