summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormrfoxygmfr <mrfoxygmfr@sch9.ru>2025-05-20 21:50:25 +0300
committermrfoxygmfr <mrfoxygmfr@sch9.ru>2025-05-20 21:50:58 +0300
commit6416c8feecc760807f4edf5d9c8e8be9c2df8981 (patch)
tree8a8b734e3ab97060c7ed239f6217b5985b7f1189
parent78bd565b9c54a962e28ea4865be286b0605ca941 (diff)
feat(lib/net): network library implemented
-rw-r--r--lib/common.h15
-rw-r--r--lib/net/client.c31
-rw-r--r--lib/net/client.h8
-rw-r--r--lib/net/common.c67
-rw-r--r--lib/net/common.h19
-rw-r--r--lib/net/server.c88
-rw-r--r--lib/net/server.h16
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