diff options
author | mrfoxygmfr <mrfoxygmfr@sch9.ru> | 2025-05-20 21:50:25 +0300 |
---|---|---|
committer | mrfoxygmfr <mrfoxygmfr@sch9.ru> | 2025-05-20 21:50:58 +0300 |
commit | 6416c8feecc760807f4edf5d9c8e8be9c2df8981 (patch) | |
tree | 8a8b734e3ab97060c7ed239f6217b5985b7f1189 | |
parent | 78bd565b9c54a962e28ea4865be286b0605ca941 (diff) |
feat(lib/net): network library implemented
-rw-r--r-- | lib/common.h | 15 | ||||
-rw-r--r-- | lib/net/client.c | 31 | ||||
-rw-r--r-- | lib/net/client.h | 8 | ||||
-rw-r--r-- | lib/net/common.c | 67 | ||||
-rw-r--r-- | lib/net/common.h | 19 | ||||
-rw-r--r-- | lib/net/server.c | 88 | ||||
-rw-r--r-- | lib/net/server.h | 16 |
7 files changed, 244 insertions, 0 deletions
diff --git a/lib/common.h b/lib/common.h new file mode 100644 index 0000000..2ffbc9b --- /dev/null +++ b/lib/common.h @@ -0,0 +1,15 @@ +#ifndef COMMON_H +#define COMMON_H + +#define _GNU_SOURCE + +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#define COMMON_CONST 1 + +#endif // COMMON_H diff --git a/lib/net/client.c b/lib/net/client.c new file mode 100644 index 0000000..10d84b1 --- /dev/null +++ b/lib/net/client.c @@ -0,0 +1,31 @@ +#include "./client.h" +#include "common.h" +#include <netinet/in.h> + +conn_t* client_connect_to_server(const char* addr, const char* port) { + conn_t* conn = calloc(1, sizeof(*conn)); + + conn->conn_socket_fd = socket(AF_INET, SOCK_STREAM, 0); + if (conn->conn_socket_fd == -1) { + fprintf(stderr, "[client_connect_to_server] Unable to create socket()\n"); + exit(EXIT_FAILURE); + } + + struct sockaddr_in* conn_addr = get_addr(addr, port); + + if (connect(conn->conn_socket_fd, conn_addr, sizeof(*conn_addr)) == -1) { + if (errno == ECONNREFUSED) { + close(conn->conn_socket_fd); + free(conn); + + return NULL; + } + + fprintf(stderr, "[connect_to_master] Unable to connect() to master"); + exit(EXIT_FAILURE); + } + + free(conn_addr); + + return conn; +} diff --git a/lib/net/client.h b/lib/net/client.h new file mode 100644 index 0000000..ae2f00d --- /dev/null +++ b/lib/net/client.h @@ -0,0 +1,8 @@ +#ifndef NET__SERVER_H +#define NET__SERVER_H + +#include "./common.h" + +conn_t* client_connect_to_server(const char* addr, const char* port); + +#endif // NET__SERVER_H diff --git a/lib/net/common.c b/lib/net/common.c new file mode 100644 index 0000000..f85a564 --- /dev/null +++ b/lib/net/common.c @@ -0,0 +1,67 @@ +#include "./common.h" +#include <netdb.h> +#include <netinet/in.h> +#include <stdlib.h> +#include <string.h> + +const uint32_t TCPALIVE_IDLE_TIME = 30; +const uint32_t TCPALIVE_CHECK_INTVL = 10; +const uint32_t TCPALIVE_CHECK_CNT = 5; + +struct sockaddr_in* get_addr(const char* addr, const char* port) { + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = 0; + hints.ai_protocol = 0; + + struct addrinfo* res; + if (getaddrinfo(addr, port, &hints, &res) != 0) { + fprintf(stderr, "[client_create_socket] Unable to call getaddrinfo()\n"); + exit(EXIT_FAILURE); + } + + if (res->ai_next != NULL) { + fprintf(stderr, "[client_create_socket] Ambigous result of getaddrinfo()\n"); + exit(EXIT_FAILURE); + } + + struct sockaddr_in* result = calloc(1, sizeof(*result)); + memcpy(result, res->ai_addr, sizeof(*res->ai_addr)); + + freeaddrinfo(res); + + return result; +} + +void conn_close(conn_t* conn) { + if (close(conn->conn_socket_fd) == -1) { + fprintf(stderr, "[conn_close] Unable to close connection socket\n"); + exit(EXIT_FAILURE); + } +} + +int conn_configure_tcpalive(conn_t* conn) { + int setsockopt_arg = 1; + if (setsockopt(conn->conn_socket_fd, SOL_SOCKET, SO_KEEPALIVE, &setsockopt_arg, sizeof(setsockopt_arg)) == -1) { + return -1; + } + + setsockopt_arg = TCPALIVE_IDLE_TIME; + if (setsockopt(conn->conn_socket_fd, IPPROTO_TCP, TCP_KEEPIDLE, &setsockopt_arg, sizeof(setsockopt_arg)) == -1) { + return -1; + } + + setsockopt_arg = TCPALIVE_CHECK_INTVL; + if (setsockopt(conn->conn_socket_fd, IPPROTO_TCP, TCP_KEEPINTVL, &setsockopt_arg, sizeof(setsockopt_arg)) == -1) { + return -1; + } + + setsockopt_arg = TCPALIVE_CHECK_CNT; + if (setsockopt(conn->conn_socket_fd, IPPROTO_TCP, TCP_KEEPCNT, &setsockopt_arg, sizeof(setsockopt_arg)) == -1) { + return -1; + } + + return 1; +} diff --git a/lib/net/common.h b/lib/net/common.h new file mode 100644 index 0000000..f7c4c9a --- /dev/null +++ b/lib/net/common.h @@ -0,0 +1,19 @@ +#ifndef NET__COMMON_H +#define NET__COMMON_H + +#include "../common.h" + +#include <netdb.h> +#include <sys/socket.h> +#include <netinet/tcp.h> + +typedef struct { + int conn_socket_fd; +} conn_t; + +struct sockaddr_in* get_addr(const char* addr, const char* port); + +int conn_configure_tcpalive(conn_t* conn); +void conn_close(conn_t* conn); + +#endif // NET__COMMON_H diff --git a/lib/net/server.c b/lib/net/server.c new file mode 100644 index 0000000..a81a1d3 --- /dev/null +++ b/lib/net/server.c @@ -0,0 +1,88 @@ +#include "./server.h" +#include "common.h" + +const uint32_t CONNECTION_QUEUE_LEN = 10U; + +server_t* server_init_tcp(const char* addr, const char* port) { + server_t* srv = calloc(1, sizeof(*srv)); + + srv->listen_socket_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); + if (srv->listen_socket_fd == -1) { + fprintf(stderr, "[server_init_tcp] Unable to create socket!\n"); + exit(EXIT_FAILURE); + } + + int setsockopt_yes = 1; + if (setsockopt(srv->listen_socket_fd, SOL_SOCKET, SO_REUSEADDR, &setsockopt_yes, sizeof(setsockopt_yes)) == -1) { + fprintf(stderr, "[server_init_tcp] Unable to set SO_REUSEADDR socket option\n"); + exit(EXIT_FAILURE); + } + + struct sockaddr_in* listen_addr = get_addr(addr, port); + + if (bind(srv->listen_socket_fd, (struct sockaddr*) listen_addr, sizeof(*listen_addr)) == -1) { + fprintf(stderr, "[server_init_tcp] Unable to bind\n"); + exit(EXIT_FAILURE); + } + + free(listen_addr); + + if (listen(srv->listen_socket_fd, CONNECTION_QUEUE_LEN) == -1) { + fprintf(stderr, "[server_init_tcp] Unable to listen() on a socket\n"); + exit(EXIT_FAILURE); + } + + struct linger linger_params = {.l_onoff = 1, .l_linger = 1}; + if (setsockopt(srv->listen_socket_fd, SOL_SOCKET, SO_LINGER, &linger_params, sizeof(linger_params)) == -1) { + fprintf(stderr, "[conn_try_accept] Unable to modify SO_LINGER socket option\n"); + exit(EXIT_FAILURE); + } + + return srv; +} + +void server_shutdown(server_t* srv) { + if (close(srv->listen_socket_fd) == -1) { + fprintf(stderr, "[server_shutdown] Unable to close listen socket\n"); + exit(EXIT_FAILURE); + } +} + +conn_t* conn_try_accept(server_t* srv) { + int client_socket_fd = accept4(srv->listen_socket_fd, NULL, NULL, SOCK_NONBLOCK); + if (client_socket_fd == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return NULL; + } + + fprintf(stderr, "[conn_try_accept] Unable to accept() connection on a socket\n"); + exit(EXIT_FAILURE); + } + + conn_t* conn = calloc(1, sizeof(*conn)); + conn->conn_socket_fd = client_socket_fd; + + // Разрешаем "зависания сокета" для доотправки данных. + + // Disable Nagle's algorithm: + int setsockopt_arg = 1; + if (setsockopt(conn->conn_socket_fd, IPPROTO_TCP, TCP_NODELAY, &setsockopt_arg, sizeof(setsockopt_arg)) == -1) { + fprintf(stderr, "[conn_try_accept] Unable to enable TCP_NODELAY socket option\n"); + exit(EXIT_FAILURE); + } + + // Disable corking: + setsockopt_arg = 0; + if (setsockopt(conn->conn_socket_fd, IPPROTO_TCP, TCP_CORK, &setsockopt_arg, sizeof(setsockopt_arg)) == -1) { + fprintf(stderr, "[conn_try_accept] Unable to disable TCP_CORK socket option\n"); + exit(EXIT_FAILURE); + } + + if (conn_configure_tcpalive(conn) == -1) { + fprintf(stderr, "[conn_try_accept] Unable to enable TCPALIVE\n"); + exit(EXIT_FAILURE); + } + + printf("[conn_try_accept] new client connected\n"); + return conn; +} diff --git a/lib/net/server.h b/lib/net/server.h new file mode 100644 index 0000000..b363724 --- /dev/null +++ b/lib/net/server.h @@ -0,0 +1,16 @@ +#ifndef NET__SERVER_H +#define NET__SERVER_H + +#include "./common.h" + +typedef struct { + int listen_socket_fd; +} server_t; + +server_t* server_init_tcp(const char* addr, const char* port); +void server_shutdown(server_t* srv); + +conn_t* conn_try_accept(server_t* srv); +void conn_close(conn_t* conn); + +#endif // NET__SERVER_H |