Source code for tcp_server_core.c

/*
    =========================================================================
    =========================================================================
*/

/******************************** INCLUDE FILES *******************************/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <assert.h>

#include <sys/socket.h> /* socket */
#include <arpa/inet.h> /* endiaanes ntoh, hton utils */

#include <linux/if_packet.h>

#include "tcp_server_classes.h"

#include "generic_t.h"

#include "worker_pool.h"
#include "utils.h"
#include "log.h"
/******************************** LOCAL DEFINES *******************************/ #define MODULE_NAME "tcp_server_server_core"
#define TCP_SERVER_BACKLOG 10
/*********************************** TYPEDEFS *********************************/ // Structure of our class struct _tcp_server_t {
int sock_fd;
int workers_n;
worker_pool_t *wp;
/* select mechanism */
fd_set masterset;
int maxfdp;
int nready;
struct timeval select_tv;
};
/************************* LOCAL FUNCTIONS DEFINITIONS ************************/
/********************************* LOCAL DATA *********************************/ /* State (volatile since we are stopping this from keyboard isr)*/ static volatile bool SERVER_STOP = true;
/******************************* INTERFACE DATA *******************************/
/******************************* LOCAL FUNCTIONS ******************************/ static void tcp_server_sig_hndlr(int signum) { SERVER_STOP = true; LOG_MSG(INFO, "Server shuting down..."); }
/******************************* PRIVATE METHODS ******************************/ // -------------------------------------------------------------------------- // Constructor
[docs]/** * * Create new tcp_server object. * * Returns: * On success new tcp_server object, or NULL if the new tcp server could not be created. */ tcp_server_t * tcp_server_new (void) { tcp_server_t *self = (tcp_server_t *) smalloc (sizeof (tcp_server_t)); assert (self); LOG_MSG(TRACE, "TCP Server created [%p]", self); return self; }
// -------------------------------------------------------------------------- // Destructor
[docs]/** * * Destroy tcp_server object. You must use this for any tcp server created via the * tcp_server_new method. * * Parameters: * self_p (tcp_server_t **): pointer to tcp_server_t object reference, * so the destructor can nullify it * * Returns: * None (void) */ void tcp_server_destroy (tcp_server_t **self_p) { assert (self_p); if (*self_p) { tcp_server_t *self = *self_p; /* * Free class properties here */ if (self->sock_fd) { close(self->sock_fd); } worker_pool_destroy(&self->wp); LOG_MSG(TRACE, "Destroying TCP Server [%p]", self); // Free object itself sfree (self); *self_p = NULL; } }
int tcp_server_sock_set_rcv_timeout(tcp_server_t *self_p, int timeout) { assert(self_p); }
int tcp_server_sock_set_send_timeout(tcp_server_t *self_p, int timeout) { assert(self_p); }
[docs]/** * * Initialize properties of a tcp_server object. * * Parameters: * self_p (tcp_server_t *): reference to a tcp_server_t object * server_iface (const char *): name of the network interface to which the server * will bind. * server_port (int): listening port of the server * callback (voidVoid_ptr_t): connection callback * workers_n (int): number of workers to dispatch * * Returns: * On 0 on success, -1 on failiure. */ int tcp_server_init(tcp_server_t *self_p, const char *server_iface, int server_port, voidVoid_ptr_t callback, int workers_n) { assert(self_p); assert(server_iface); int i = 0; int ret = 0; struct sockaddr_in saddr; ip_parser_t sipv4; ret = net_get_ipv4_from_iface(server_iface, &sipv4); if (ret == -1) { LOG_MSG(ERR, "Failed to obtain IP address for %s", server_iface); return -1; } self_p->sock_fd = socket(AF_INET, SOCK_STREAM, 0); if (self_p->sock_fd == -1) { LOG_MSG(ERR, "Failed to create TCP server socket! (%s)", strerror(errno)); return -1; } /* reuse sock */ int optval = 1; ret = setsockopt(self_p->sock_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); if (ret == -1) { LOG_MSG(ERR, "Failed to set sock option SO_REUSEADDR! (%s)", strerror(errno)); close(self_p->sock_fd); return -1; } /* Bind the socket */ saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = inet_addr(sipv4.ip_str); saddr.sin_port = htons(server_port); ret = bind(self_p->sock_fd, (struct sockaddr *)&saddr, sizeof(saddr)); if (ret == -1) { LOG_MSG(ERR, "bind failed! (%s)", strerror(errno)); close(self_p->sock_fd); return -1; } ret = listen(self_p->sock_fd, TCP_SERVER_BACKLOG); if (ret == -1) { LOG_MSG(ERR, "listen failed! (%s)", strerror(errno)); close(self_p->sock_fd); return -1; } /* Clear the descriptor set */ FD_ZERO(&self_p->masterset); FD_SET(self_p->sock_fd, &self_p->masterset); self_p->maxfdp = self_p->sock_fd + 1; /* Create worker pool */ self_p->workers_n = workers_n; self_p->wp = worker_pool_new(self_p->workers_n); /* Prefork all the workers */ for (i = 0; i < self_p->workers_n; i++) { /* parent returns */ worker_pool_dispatch_worker(self_p->wp, i, self_p->sock_fd, callback); worker_pool_worker_fd_set(self_p->wp, i, &self_p->masterset); self_p->maxfdp = max(self_p->maxfdp, worker_pool_worker_fd_get(self_p->wp, i)); } /* Register the sig handler */ signal(SIGINT, tcp_server_sig_hndlr); return 0; }
[docs]/** * * Server main processing loop. * * Parameters: * self_p (tcp_server_t *): reference to a tcp_server_t object * * Returns: * None (void) */ void tcp_server_run(tcp_server_t *self_p) { assert(self_p); assert(self_p->wp); int i = 0; int nsel = 0; int conn = -1; fd_set rset; struct sockaddr_in src_addr; socklen_t src_addr_len = sizeof(src_addr); /* Start the server loop */ SERVER_STOP = false; for (;;) { rset = self_p->masterset; if (worker_pool_workers_avail_get(self_p->wp) <= 0) { /* turn off if no available children */ FD_CLR(self_p->sock_fd, &rset); } self_p->select_tv.tv_sec = 2; self_p->select_tv.tv_usec = 0; nsel = select(self_p->maxfdp, &rset, NULL, NULL, NULL); if (SERVER_STOP) { FD_ZERO(&rset); break; } /* Check for new connections */ if (FD_ISSET(self_p->sock_fd, &rset)) { conn = accept(self_p->sock_fd, (struct sockaddr *)&src_addr, &src_addr_len); LOG_MSG(INFO, "New connection from: %s:%d", inet_ntoa(src_addr.sin_addr), ntohs(src_addr.sin_port)); worker_pool_workers_submit_conn(self_p->wp, conn); close(conn); if (--nsel == 0) { continue; /* all done with select() results */ } } /* Find any newly-available children */ worker_pool_workers_find_free(self_p->wp, nsel, &rset); } worker_pool_workers_terminate(self_p->wp); }
// --------------------------------------------------------------------------
static void server_connection_cb(void *args) { int conn = -1; int rx_bytes = 0; char rx_buffer[1500]; conn = *(int *)args; rx_bytes = read(conn, rx_buffer, 1500); hexdump("RX Buffer", rx_buffer, rx_bytes); /* Just loopback */ write(conn, rx_buffer, rx_bytes); }
void tcp_server_test (bool verbose) { LOG_MSG(TRACE, "Running %s", __func__); const char *iface = "lo"; int port = 9080; int workers = 2; /* Create new instance of a server */ tcp_server_t *self = tcp_server_new(); tcp_server_init(self, iface, port, server_connection_cb, workers); tcp_server_run(self); /* Destroy server */ tcp_server_destroy(&self); }