├── .gitignore ├── .github └── FUNDING.yml ├── src ├── modules │ ├── main.h │ ├── termflag.h │ ├── mainsocket.h │ ├── inputcommand.h │ ├── inputpayload.h │ ├── nextperiodic.h │ ├── sockettime.h │ ├── socketlist.h │ ├── readlist.h │ ├── log.h │ ├── network.h │ ├── periodic.h │ ├── termflag.c │ ├── command.h │ ├── connectfunction.h │ ├── disconnectfunction.h │ ├── mainsocket.c │ ├── nextperiodic.c │ ├── inputcommand.c │ ├── inputpayload.c │ ├── periodic.c │ ├── connectfunction.c │ ├── socketlist.c │ ├── disconnectfunction.c │ ├── component.h │ ├── sockettime.c │ ├── command.c │ ├── readlist.c │ ├── component.c │ ├── log.c │ ├── main.c │ └── network.c ├── client.c ├── server.c ├── example │ ├── server.c │ └── client.c └── config.h ├── LICENSE ├── Makefile └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | bin/* 2 | obj/* 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: bartobri 2 | -------------------------------------------------------------------------------- /src/modules/main.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Brian Barto 2 | // 3 | // This program is free software; you can redistribute it and/or modify it 4 | // under the terms of the MIT License. See LICENSE for more details. 5 | 6 | #ifndef MAIN_H 7 | #define MAIN_H 1 8 | 9 | // Function prototypes defined in server.c and client.c 10 | void server_init(void); 11 | void client_init(void); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /src/modules/termflag.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Brian Barto 2 | // 3 | // This program is free software; you can redistribute it and/or modify it 4 | // under the terms of the MIT License. See LICENSE for more details. 5 | 6 | #ifndef TERMFLAG_H 7 | #define TERMFLAG_H 1 8 | 9 | /* 10 | * Function Declarations 11 | */ 12 | void termflag_init(void); 13 | void termflag_set(void); 14 | int termflag_isset(void); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/modules/mainsocket.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Brian Barto 2 | // 3 | // This program is free software; you can redistribute it and/or modify it 4 | // under the terms of the MIT License. See LICENSE for more details. 5 | 6 | #ifndef MAINSOCKET_H 7 | #define MAINSOCKET_H 1 8 | 9 | /* 10 | * Function Declarations 11 | */ 12 | void mainsocket_init(void); 13 | void mainsocket_set(int); 14 | int mainsocket_get(void); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/modules/inputcommand.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Brian Barto 2 | // 3 | // This program is free software; you can redistribute it and/or modify it 4 | // under the terms of the MIT License. See LICENSE for more details. 5 | 6 | #ifndef INPUTCOMMAND_H 7 | #define INPUTCOMMAND_H 1 8 | 9 | /* 10 | * Function Declarations 11 | */ 12 | void inputcommand_init(void); 13 | char *inputcommand_get(void); 14 | void inputcommand_parse(char *); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/modules/inputpayload.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Brian Barto 2 | // 3 | // This program is free software; you can redistribute it and/or modify it 4 | // under the terms of the MIT License. See LICENSE for more details. 5 | 6 | #ifndef INPUTPAYLOAD_H 7 | #define INPUTPAYLOAD_H 1 8 | 9 | /* 10 | * Function Declarations 11 | */ 12 | void inputpayload_init(void); 13 | char *inputpayload_get(void); 14 | void inputpayload_parse(char *); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/modules/nextperiodic.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Brian Barto 2 | // 3 | // This program is free software; you can redistribute it and/or modify it 4 | // under the terms of the MIT License. See LICENSE for more details. 5 | 6 | #ifndef NEXTPERIODIC_H 7 | #define NEXTPERIODIC_H 1 8 | 9 | /* 10 | * Function Declarations 11 | */ 12 | void nextperiodic_init(void); 13 | void nextperiodic_reset(void); 14 | int nextperiodic_elapsed(unsigned int); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/modules/sockettime.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Brian Barto 2 | // 3 | // This program is free software; you can redistribute it and/or modify it 4 | // under the terms of the MIT License. See LICENSE for more details. 5 | 6 | #ifndef SOCKETTIME_H 7 | #define SOCKETTIME_H 1 8 | 9 | /* 10 | * Function Declarations 11 | */ 12 | void sockettime_init(void); 13 | void sockettime_set(int); 14 | int sockettime_get(int); 15 | int sockettime_elapsed(int); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /src/modules/socketlist.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Brian Barto 2 | // 3 | // This program is free software; you can redistribute it and/or modify it 4 | // under the terms of the MIT License. See LICENSE for more details. 5 | 6 | #ifndef SOCKETLIST_H 7 | #define SOCKETLIST_H 1 8 | 9 | /* 10 | * Function Declarations 11 | */ 12 | void socketlist_init(void); 13 | void socketlist_add(int); 14 | void socketlist_remove(int); 15 | int socketlist_get_next(void); 16 | void socketlist_reset_next(void); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /src/modules/readlist.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Brian Barto 2 | // 3 | // This program is free software; you can redistribute it and/or modify it 4 | // under the terms of the MIT License. See LICENSE for more details. 5 | 6 | #ifndef READLIST_H 7 | #define READLIST_H 1 8 | 9 | /* 10 | * Function Declarations 11 | */ 12 | void readlist_init(void); 13 | void readlist_add(int); 14 | int readlist_wait(unsigned int); 15 | void readlist_remove(int); 16 | int readlist_check(int); 17 | int readlist_get_next(void); 18 | void readlist_reset_next(void); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /src/modules/log.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Brian Barto 2 | // 3 | // This program is free software; you can redistribute it and/or modify it 4 | // under the terms of the MIT License. See LICENSE for more details. 5 | 6 | #ifndef LOG_H 7 | #define LOG_H 1 8 | 9 | /* 10 | * Function Declarations 11 | */ 12 | 13 | void log_init(void); 14 | int log_open(int); 15 | int log_open_server(void); 16 | int log_open_client(void); 17 | void log_write(char *, ...); 18 | void log_print(char *, ...); 19 | void log_close(void); 20 | char *log_get_errmsg(void); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /src/modules/network.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Brian Barto 2 | // 3 | // This program is free software; you can redistribute it and/or modify it 4 | // under the terms of the MIT License. See LICENSE for more details. 5 | 6 | #ifndef NETWORK_H 7 | #define NETWORK_H 1 8 | 9 | /* 10 | * Function Declarations 11 | */ 12 | void network_init(void); 13 | int network_start_server(char *, char *); 14 | int network_start_client(char *, char *); 15 | int network_accept(int); 16 | int network_read(int); 17 | char *network_get_readdata(int); 18 | int network_write(int, char *); 19 | void network_close(int); 20 | char *network_get_errmsg(void); 21 | char *network_get_ipaddress(void); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/modules/periodic.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Brian Barto 2 | // 3 | // This program is free software; you can redistribute it and/or modify it 4 | // under the terms of the MIT License. See LICENSE for more details. 5 | 6 | #ifndef PERIODIC_H 7 | #define PERIODIC_H 1 8 | 9 | /* 10 | * These symbolic constants define the parameters and the return value for 11 | * functions executed at a timed interval. 12 | */ 13 | #define PERIODIC_PARAMS void 14 | #define PERIODIC_RETURN void 15 | 16 | /* 17 | * Define a data type for a function pointer whos paramaters are 18 | * PERIODIC_PARAMS and return value is PERIODIC_RETURN. 19 | */ 20 | typedef PERIODIC_RETURN (*prdFunctionType)(PERIODIC_PARAMS); 21 | 22 | /* 23 | * Function Declarations 24 | */ 25 | void periodic_init(void); 26 | void periodic_add(prdFunctionType); 27 | void periodic_exec(void); 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /src/modules/termflag.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Brian Barto 2 | // 3 | // This program is free software; you can redistribute it and/or modify it 4 | // under the terms of the MIT License. See LICENSE for more details. 5 | 6 | /* 7 | * MODULE DESCRIPTION 8 | * 9 | * The termflag module stores and provides access to a simple boolean 10 | * flag. This flag can be set from inside a custom function to tell the 11 | * main program loop to terminate the program. 12 | */ 13 | 14 | /* 15 | * Static Variables 16 | */ 17 | static int termflag; 18 | 19 | /* 20 | * Initialize the flag to a false value. 21 | */ 22 | void termflag_init(void) { 23 | termflag = 0; 24 | } 25 | 26 | /* 27 | * Set the flag to a true value. 28 | */ 29 | void termflag_set(void) { 30 | termflag = 1; 31 | } 32 | 33 | /* 34 | * Return the flag value. 35 | */ 36 | int termflag_isset(void) { 37 | return termflag; 38 | } 39 | -------------------------------------------------------------------------------- /src/modules/command.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Brian Barto 2 | // 3 | // This program is free software; you can redistribute it and/or modify it 4 | // under the terms of the MIT License. See LICENSE for more details. 5 | 6 | #ifndef COMMAND_H 7 | #define COMMAND_H 1 8 | 9 | /* 10 | * These symbolic constants define the parameters and the return value for 11 | * functions executed in response to a given command. 12 | */ 13 | #define COMMAND_PARAMS int socket, char *payload 14 | #define COMMAND_RETURN void 15 | 16 | /* 17 | * Define a data type for a function pointer whos paramaters are 18 | * COMMAND_PARAMS and return value is COMMAND_RETURN. 19 | */ 20 | typedef COMMAND_RETURN (*comFunctionType)(COMMAND_PARAMS); 21 | 22 | /* 23 | * Function Declarations 24 | */ 25 | void command_init(void); 26 | void command_add(char *, comFunctionType); 27 | int command_exists(char *command); 28 | void command_exec(char *command, char *payload, int socket); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/modules/connectfunction.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Brian Barto 2 | // 3 | // This program is free software; you can redistribute it and/or modify it 4 | // under the terms of the MIT License. See LICENSE for more details. 5 | 6 | #ifndef CONNECTFUNCTION_H 7 | #define CONNECTFUNCTION_H 1 8 | 9 | /* 10 | * These symbolic constants define the parameters and the return value for 11 | * the functions executed in response to a new connection. 12 | */ 13 | #define CONNECTFUNCTION_PARAMS int socket 14 | #define CONNECTFUNCTION_RETURN void 15 | 16 | /* 17 | * Define a data type for a function pointer whos paramaters are 18 | * CONNECTFUNCTION_PARAMS and return value is CONNECTFUNCTION_RETURN. 19 | */ 20 | typedef CONNECTFUNCTION_RETURN (*connectFunctionType)(CONNECTFUNCTION_PARAMS); 21 | 22 | /* 23 | * Function Declarations. 24 | */ 25 | void connectfunction_init(void); 26 | void connectfunction_set(connectFunctionType); 27 | int connectfunction_exists(void); 28 | void connectfunction_exec(int); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/modules/disconnectfunction.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Brian Barto 2 | // 3 | // This program is free software; you can redistribute it and/or modify it 4 | // under the terms of the MIT License. See LICENSE for more details. 5 | 6 | #ifndef DISCONNECTFUNCTION_H 7 | #define DISCONNECTFUNCTION_H 1 8 | 9 | /* 10 | * These symbolic constants define the parameters and the return value for 11 | * the functions executed in response to a disconnection. 12 | */ 13 | #define DISCONNECTFUNCTION_PARAMS int socket 14 | #define DISCONNECTFUNCTION_RETURN void 15 | 16 | /* 17 | * Define a data type for a function pointer whos paramaters are 18 | * DISCONNECTFUNCTION_PARAMS and return value is DISCONNECTFUNCTION_RETURN. 19 | */ 20 | typedef DISCONNECTFUNCTION_RETURN (*disconnectFunctionType)(DISCONNECTFUNCTION_PARAMS); 21 | 22 | /* 23 | * Function declarations. 24 | */ 25 | void disconnectfunction_init(void); 26 | void disconnectfunction_set(disconnectFunctionType); 27 | int disconnectfunction_exists(void); 28 | void disconnectfunction_exec(int); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/modules/mainsocket.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Brian Barto 2 | // 3 | // This program is free software; you can redistribute it and/or modify it 4 | // under the terms of the MIT License. See LICENSE for more details. 5 | 6 | /* 7 | * MODULE DESCRIPTION 8 | * 9 | * The mainsocket module stores and provides access to the main socket 10 | * file descriptor, as an integer value. For the client, this is the 11 | * socket used to communicate with the server. For the server, this is 12 | * the socket used to listen for new connections. 13 | */ 14 | 15 | /* 16 | * Static Variables 17 | */ 18 | static int mainsocket; 19 | 20 | /* 21 | * Initialize the mainsocket static variable to NULL (zero). 22 | */ 23 | void mainsocket_init(void) { 24 | mainsocket = 0; 25 | } 26 | 27 | /* 28 | * Store the integer value for the main socket file descriptor. 29 | */ 30 | void mainsocket_set(int n) { 31 | mainsocket = n; 32 | } 33 | 34 | /* 35 | * Return the integer value for the main socket file descriptor. 36 | */ 37 | int mainsocket_get(void) { 38 | return mainsocket; 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Brian Barto 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/modules/nextperiodic.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Brian Barto 2 | // 3 | // This program is free software; you can redistribute it and/or modify it 4 | // under the terms of the MIT License. See LICENSE for more details. 5 | 6 | #include 7 | #include "config.h" 8 | 9 | /* 10 | * MODULE DESCRIPTION 11 | * 12 | * The periodictime module manages a timestamp of the last time the 13 | * periodic functions were run. 14 | */ 15 | 16 | /* 17 | * Static Variables 18 | */ 19 | static int periodictime; 20 | 21 | /* 22 | * Initialize periodictime to the current time. 23 | */ 24 | void nextperiodic_init(void) { 25 | periodictime = (int)time(NULL); 26 | } 27 | 28 | /* 29 | * Set periodictime to the current time. 30 | */ 31 | void nextperiodic_reset(void) { 32 | periodictime = (int)time(NULL); 33 | } 34 | 35 | /* 36 | * Check if the number of seconds configured for PERIODIC_SECONDS has 37 | * elapsed since the last time periodictime was set. Return a true value 38 | * if it has. Return a false value if it has not. 39 | */ 40 | int nextperiodic_elapsed(unsigned int t) { 41 | if (periodictime <= time(NULL) - t) 42 | return 1; 43 | 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /src/modules/inputcommand.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Brian Barto 2 | // 3 | // This program is free software; you can redistribute it and/or modify it 4 | // under the terms of the MIT License. See LICENSE for more details. 5 | 6 | #include 7 | #include "config.h" 8 | 9 | /* 10 | * MODULE DESCRIPTION 11 | * 12 | * The inputcommand module manages a character array that contains the 13 | * command string from the last time network_read() was executed. 14 | */ 15 | 16 | /* 17 | * Static Variables 18 | */ 19 | static char command[MAX_COMMAND_SIZE + 1]; 20 | 21 | /* 22 | * Set the command character array to all null characters 23 | */ 24 | void inputcommand_init(void) { 25 | memset(command, 0, sizeof(command)); 26 | } 27 | 28 | /* 29 | * Return the command character array. 30 | */ 31 | char *inputcommand_get(void) { 32 | return command; 33 | } 34 | 35 | /* 36 | * Parse out the first number of characters equal to the command length 37 | * from the given data string and store them in the command character 38 | * array. 39 | */ 40 | void inputcommand_parse(char *data) { 41 | int len = 0; 42 | 43 | memset(command, 0, sizeof(command)); 44 | 45 | while (*data >= '0' && *data <= '9') 46 | len = (len * 10) + *data++ - '0'; 47 | 48 | ++data; 49 | 50 | if (len > MAX_COMMAND_SIZE) 51 | len = MAX_COMMAND_SIZE; 52 | 53 | strncpy(command, data, len); 54 | } 55 | -------------------------------------------------------------------------------- /src/modules/inputpayload.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Brian Barto 2 | // 3 | // This program is free software; you can redistribute it and/or modify it 4 | // under the terms of the MIT License. See LICENSE for more details. 5 | 6 | #include 7 | #include "config.h" 8 | 9 | /* 10 | * MODULE DESCRIPTION 11 | * 12 | * The inputpayload module manages a character array that contains the 13 | * payload string from the last time network_read() was executed. 14 | */ 15 | 16 | /* 17 | * Static Variables 18 | */ 19 | static char payload[MAX_PAYLOAD_SIZE + 1]; 20 | 21 | /* 22 | * Set the payload character array to all null characters 23 | */ 24 | void inputpayload_init(void) { 25 | memset(payload, 0, sizeof(payload)); 26 | } 27 | 28 | /* 29 | * Return the payload character array. 30 | */ 31 | char *inputpayload_get(void) { 32 | return payload; 33 | } 34 | 35 | /* 36 | * Advance the given char pointer (data) by the number of characters equal 37 | * to the length of the command. Then parse out the next number of 38 | * characters equal to MAX_PAYLOAD_SIZE and store them in the payload 39 | * character array. 40 | */ 41 | void inputpayload_parse(char *data) { 42 | unsigned int cmdlen = 0; 43 | 44 | memset(payload, 0, sizeof(payload)); 45 | 46 | while (*data >= '0' && *data <= '9') 47 | cmdlen = (cmdlen * 10) + *data++ - '0'; 48 | 49 | ++data; 50 | 51 | if (strlen(data) > cmdlen) 52 | strncpy(payload, data + cmdlen, MAX_PAYLOAD_SIZE); 53 | } 54 | -------------------------------------------------------------------------------- /src/modules/periodic.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Brian Barto 2 | // 3 | // This program is free software; you can redistribute it and/or modify it 4 | // under the terms of the MIT License. See LICENSE for more details. 5 | 6 | #include 7 | #include "config.h" 8 | #include "modules/periodic.h" 9 | 10 | /* 11 | * MODULE DESCRIPTION 12 | * 13 | * The periodic module manages an array of function pointers referred to 14 | * as "periodic functions". It is responsible for initializing the array, 15 | * adding new functions to the array, and executing all functions 16 | * contained in the array. 17 | */ 18 | 19 | /* 20 | * Static Variables 21 | */ 22 | prdFunctionType functions[PERIODIC_LIMIT]; 23 | 24 | /* 25 | * Initialize the functions array with all NULL values. 26 | */ 27 | void periodic_init(void) { 28 | int i; 29 | 30 | // prdfunction array init 31 | for (i = 0; i < PERIODIC_LIMIT; ++i) 32 | functions[i] = NULL; 33 | } 34 | 35 | /* 36 | * Add a function pointer to the next available slot in the functions 37 | * array. 38 | */ 39 | void periodic_add(prdFunctionType functionPtr) { 40 | int i; 41 | 42 | for (i = 0; i < PERIODIC_LIMIT; ++i) { 43 | if (functions[i] == NULL) { 44 | functions[i] = functionPtr; 45 | break; 46 | } 47 | } 48 | } 49 | 50 | /* 51 | * Execute all function pointers contained in the functions array. 52 | */ 53 | void periodic_exec(void) { 54 | int i; 55 | 56 | for (i = 0; i < PERIODIC_LIMIT; ++i) 57 | if (functions[i] != NULL) 58 | functions[i](); 59 | } 60 | -------------------------------------------------------------------------------- /src/modules/connectfunction.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Brian Barto 2 | // 3 | // This program is free software; you can redistribute it and/or modify it 4 | // under the terms of the MIT License. See LICENSE for more details. 5 | 6 | #include 7 | #include "modules/connectfunction.h" 8 | 9 | /* 10 | * MODULE DESCRIPTION 11 | * 12 | * The connectfunction module manages a pointer to a function referred 13 | * to as the "connect function". This function is executed by the main 14 | * module every time a new connection established. 15 | */ 16 | 17 | /* 18 | * Static Variables 19 | */ 20 | static connectFunctionType connectFunction; 21 | 22 | /* 23 | * Set the connect function pointer to NULL. 24 | */ 25 | void connectfunction_init(void) { 26 | connectFunction = NULL; 27 | } 28 | 29 | /* 30 | * Set the connect function to the given function pointer (functionPtr) 31 | */ 32 | void connectfunction_set(connectFunctionType functionPtr) { 33 | connectFunction = functionPtr; 34 | } 35 | 36 | /* 37 | * Check if the connect function has been set. Return a true value if it 38 | * has. Return a false value if it has not. 39 | */ 40 | int connectfunction_exists(void) { 41 | if (connectFunction != NULL) 42 | return 1; 43 | 44 | return 0; 45 | } 46 | 47 | /* 48 | * Execute the connect function if the poiter has been set. Pass as a 49 | * parameter the connecting entity's socket assignment. 50 | */ 51 | void connectfunction_exec(int socket) { 52 | if (connectFunction != NULL) 53 | connectFunction(socket); 54 | } 55 | -------------------------------------------------------------------------------- /src/client.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Brian Barto 2 | // 3 | // This program is free software; you can redistribute it and/or modify it 4 | // under the terms of the MIT License. See LICENSE for more details. 5 | 6 | // Required header for custom functions. Do not remove. 7 | #include "modules/component.h" 8 | 9 | // Copy and paste the function templates below to start creating your 10 | // custom functions. 11 | 12 | /*********************************************************************** 13 | 14 | CONNECT_FUNCTION(function_name) { 15 | (void)socket; 16 | 17 | // Code here 18 | } 19 | 20 | DISCONNECT_FUNCTION(function_name) { 21 | (void)socket; 22 | 23 | // Code here 24 | } 25 | 26 | PERIODIC_FUNCTION(function_name) { 27 | // Code here 28 | } 29 | 30 | COMMAND_FUNCTION(function_name) { 31 | (void)socket; 32 | (void)payload; 33 | 34 | // Code here 35 | } 36 | 37 | ***********************************************************************/ 38 | 39 | /* 40 | * The client_init() function is executed when the client starts. This 41 | * function should be used to load all your custom functions. Further, 42 | * any other initialization tasks needed for your custom application can 43 | * be added to this function e.g. connecting to a database. 44 | */ 45 | void client_init(void) { 46 | 47 | // Load your custom functions here. 48 | // 49 | // e.g. 50 | // set_connect_function(&function_name); 51 | // set_disconnect_function(&function_name); 52 | // add_periodic_function(&function_name); 53 | // add_command_function("cmnd", &function_name); 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/server.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Brian Barto 2 | // 3 | // This program is free software; you can redistribute it and/or modify it 4 | // under the terms of the MIT License. See LICENSE for more details. 5 | 6 | // Required header for custom functions. Do not remove. 7 | #include "modules/component.h" 8 | 9 | // Copy and paste the function templates below to start creating your 10 | // custom functions. 11 | 12 | /*********************************************************************** 13 | 14 | CONNECT_FUNCTION(function_name) { 15 | (void)socket; 16 | 17 | // Code here 18 | } 19 | 20 | DISCONNECT_FUNCTION(function_name) { 21 | (void)socket; 22 | 23 | // Code here 24 | } 25 | 26 | PERIODIC_FUNCTION(function_name) { 27 | // Code here 28 | } 29 | 30 | COMMAND_FUNCTION(function_name) { 31 | (void)socket; 32 | (void)payload; 33 | 34 | // Code here 35 | } 36 | 37 | ***********************************************************************/ 38 | 39 | /* 40 | * The server_init() function is executed when the server starts. This 41 | * function should be used to load all your custom functions. Further, 42 | * any other initialization tasks needed for your custom application can 43 | * be added to this function e.g. connecting to a database. 44 | */ 45 | void server_init(void) { 46 | 47 | // Load your custom functions here. 48 | // 49 | // e.g. 50 | // set_connect_function(&function_name); 51 | // set_disconnect_function(&function_name); 52 | // add_periodic_function(&function_name); 53 | // add_command_function("cmnd", &function_name); 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/modules/socketlist.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Brian Barto 2 | // 3 | // This program is free software; you can redistribute it and/or modify it 4 | // under the terms of the MIT License. See LICENSE for more details. 5 | 6 | #include 7 | #include "modules/socketlist.h" 8 | 9 | /* 10 | * MODULE DESCRIPTION 11 | * 12 | * The socketlist module maintains and provides access to a list of all 13 | * active socket file descriptors. 14 | */ 15 | 16 | /* 17 | * Static Variables 18 | */ 19 | static fd_set active_fd_set; 20 | static int list_position; 21 | 22 | /* 23 | * Set all static variable values to zero. 24 | */ 25 | void socketlist_init(void) { 26 | FD_ZERO(&active_fd_set); 27 | list_position = 0; 28 | } 29 | 30 | /* 31 | * Add the given socket to the socketlist. 32 | */ 33 | void socketlist_add(int socket) { 34 | FD_SET(socket, &active_fd_set); 35 | } 36 | 37 | /* 38 | * Remove the given socket from the socketlist. 39 | */ 40 | void socketlist_remove(int socket) { 41 | FD_CLR(socket, &active_fd_set); 42 | } 43 | 44 | /* 45 | * Return the next socket value from the socketlist. Return a negative 46 | * value when the end of the list is reached. 47 | */ 48 | int socketlist_get_next(void) { 49 | 50 | while (++list_position < FD_SETSIZE) 51 | if (FD_ISSET (list_position, &active_fd_set)) 52 | return list_position; 53 | 54 | socketlist_reset_next(); 55 | 56 | return -1; 57 | } 58 | 59 | /* 60 | * Reset the list position used for traversing the socketlist. 61 | */ 62 | void socketlist_reset_next(void) { 63 | list_position = 0; 64 | } 65 | -------------------------------------------------------------------------------- /src/modules/disconnectfunction.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Brian Barto 2 | // 3 | // This program is free software; you can redistribute it and/or modify it 4 | // under the terms of the MIT License. See LICENSE for more details. 5 | 6 | #include 7 | #include "modules/disconnectfunction.h" 8 | 9 | /* 10 | * MODULE DESCRIPTION 11 | * 12 | * The disconnectfunction module manages a pointer to a function referred 13 | * to as the "disconnect function". This function is executed by the main 14 | * module every time it detects a terminated connection, or terminates 15 | * one itself. 16 | */ 17 | 18 | /* 19 | * Static Variables 20 | */ 21 | static disconnectFunctionType disconnectFunction; 22 | 23 | /* 24 | * Set the disconnect function pointer to NULL. 25 | */ 26 | void disconnectfunction_init(void) { 27 | disconnectFunction = NULL; 28 | } 29 | 30 | /* 31 | * Set the disconnect function to the given function pointer (functionPtr) 32 | */ 33 | void disconnectfunction_set(disconnectFunctionType functionPtr) { 34 | disconnectFunction = functionPtr; 35 | } 36 | 37 | /* 38 | * Check if the disconnect function has been set. Return a true value if 39 | * it has. Return a false value if it has not. 40 | */ 41 | int disconnectfunction_exists(void) { 42 | if (disconnectFunction != NULL) 43 | return 1; 44 | 45 | return 0; 46 | } 47 | 48 | /* 49 | * Execute the connect function if the poiter has been set. Pass as a 50 | * parameter the connecting entity's socket assignment. 51 | */ 52 | void disconnectfunction_exec(int socket) { 53 | if (disconnectFunction != NULL) 54 | disconnectFunction(socket); 55 | } 56 | -------------------------------------------------------------------------------- /src/modules/component.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Brian Barto 2 | // 3 | // This program is free software; you can redistribute it and/or modify it 4 | // under the terms of the MIT License. See LICENSE for more details. 5 | 6 | #ifndef COMPONENT_H 7 | #define COMPONENT_H 1 8 | 9 | /* 10 | * Module headers that have declarations that this header needs. 11 | */ 12 | #include "modules/command.h" 13 | #include "modules/connectfunction.h" 14 | #include "modules/disconnectfunction.h" 15 | #include "modules/periodic.h" 16 | #include "modules/log.h" 17 | 18 | /* 19 | * Macros to simpify the custom function definitions. 20 | */ 21 | #define CONNECT_FUNCTION(n) CONNECTFUNCTION_RETURN n(CONNECTFUNCTION_PARAMS) 22 | #define DISCONNECT_FUNCTION(n) DISCONNECTFUNCTION_RETURN n(DISCONNECTFUNCTION_PARAMS) 23 | #define PERIODIC_FUNCTION(n) PERIODIC_RETURN n(PERIODIC_PARAMS) 24 | #define COMMAND_FUNCTION(n) COMMAND_RETURN n(COMMAND_PARAMS) 25 | 26 | /* 27 | * Using a macro to wrap log_write() and log_print() since they are 28 | * variadic functions, which makes creating a regular function wrapper 29 | * slightly more difficult. 30 | */ 31 | #define write_log(...) log_write(__VA_ARGS__) 32 | #define print_log(...) log_print(__VA_ARGS__) 33 | 34 | /* 35 | * Function Declarations. 36 | */ 37 | int write_socket(int, char *, char *); 38 | void close_socket(int); 39 | int main_socket(void); 40 | int next_socket(void); 41 | void reset_next_socket(void); 42 | void set_connect_function(connectFunctionType); 43 | void set_disconnect_function(disconnectFunctionType); 44 | void add_periodic_function(prdFunctionType); 45 | void add_command_function(char *, comFunctionType); 46 | void terminate(void); 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /src/example/server.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Brian Barto 2 | // 3 | // This program is free software; you can redistribute it and/or modify it 4 | // under the terms of the MIT License. See LICENSE for more details. 5 | 6 | // Required header for custom functions. Do not remove. 7 | #include "modules/component.h" 8 | 9 | // Copy and paste the function templates below to start creating your 10 | // custom functions. 11 | 12 | /*********************************************************************** 13 | 14 | CONNECT_FUNCTION(function_name) { 15 | (void)socket; 16 | 17 | // Code here 18 | } 19 | 20 | DISCONNECT_FUNCTION(function_name) { 21 | (void)socket; 22 | 23 | // Code here 24 | } 25 | 26 | PERIODIC_FUNCTION(function_name) { 27 | // Code here 28 | } 29 | 30 | COMMAND_FUNCTION(function_name) { 31 | (void)socket; 32 | (void)payload; 33 | 34 | // Code here 35 | } 36 | 37 | ***********************************************************************/ 38 | 39 | CONNECT_FUNCTION(say_hello) { 40 | (void)socket; 41 | 42 | write_socket(socket, "hello", ""); 43 | } 44 | 45 | DISCONNECT_FUNCTION(print_message) { 46 | (void)socket; 47 | 48 | print_log("Socket %i disconnected.", socket); 49 | } 50 | 51 | /* 52 | * The server_init() function is executed when the server starts. This 53 | * function should be used to load all your custom functions. Further, 54 | * any other initialization tasks needed for your custom application can 55 | * be added to this function e.g. connecting to a database. 56 | */ 57 | void server_init(void) { 58 | 59 | // Load your custom functions here. 60 | // 61 | // e.g. 62 | // set_connect_function(&function_name); 63 | // set_disconnect_function(&function_name); 64 | // add_periodic_function(&function_name); 65 | // add_command_function("cmnd", &function_name); 66 | 67 | set_connect_function(&say_hello); 68 | set_disconnect_function(&print_message); 69 | } 70 | -------------------------------------------------------------------------------- /src/example/client.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Brian Barto 2 | // 3 | // This program is free software; you can redistribute it and/or modify it 4 | // under the terms of the MIT License. See LICENSE for more details. 5 | 6 | #include 7 | 8 | // Required header for custom functions. Do not remove. 9 | #include "modules/component.h" 10 | 11 | // Copy and paste the function templates below to start creating your 12 | // custom functions. 13 | 14 | /*********************************************************************** 15 | 16 | CONNECT_FUNCTION(function_name) { 17 | (void)socket; 18 | 19 | // Code here 20 | } 21 | 22 | DISCONNECT_FUNCTION(function_name) { 23 | (void)socket; 24 | 25 | // Code here 26 | } 27 | 28 | PERIODIC_FUNCTION(function_name) { 29 | // Code here 30 | } 31 | 32 | COMMAND_FUNCTION(function_name) { 33 | (void)socket; 34 | (void)payload; 35 | 36 | // Code here 37 | } 38 | 39 | ***********************************************************************/ 40 | 41 | COMMAND_FUNCTION(receive_hello) { 42 | (void)socket; 43 | (void)payload; 44 | 45 | printf("Received hello from server\n"); 46 | } 47 | 48 | PERIODIC_FUNCTION(send_heartbeat) { 49 | static int c = 0; 50 | 51 | if (c++ < 5) 52 | write_socket(main_socket(), "beat", ""); 53 | } 54 | 55 | /* 56 | * The client_init() function is executed when the client starts. This 57 | * function should be used to load all your custom functions. Further, 58 | * any other initialization tasks needed for your custom application can 59 | * be added to this function e.g. connecting to a database. 60 | */ 61 | void client_init(void) { 62 | 63 | // Load your custom functions here. 64 | // 65 | // e.g. 66 | // set_connect_function(&function_name); 67 | // set_disconnect_function(&function_name); 68 | // add_periodic_function(&function_name); 69 | // add_command_function("cmnd", &function_name); 70 | 71 | add_command_function("hello", &receive_hello); 72 | add_periodic_function(&send_heartbeat); 73 | } 74 | -------------------------------------------------------------------------------- /src/modules/sockettime.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Brian Barto 2 | // 3 | // This program is free software; you can redistribute it and/or modify it 4 | // under the terms of the MIT License. See LICENSE for more details. 5 | 6 | #include 7 | #include 8 | #include "config.h" 9 | 10 | /* 11 | * MODULE DESCRIPTION 12 | * 13 | * The sockettime module stores and provides access to a list of sockets 14 | * file descriptors and the timestamp of the last time data was received. 15 | */ 16 | 17 | /* 18 | * Static Variables 19 | */ 20 | static struct { 21 | int socket; 22 | int timestamp; 23 | } times[FD_SETSIZE]; 24 | 25 | /* 26 | * Initialize all static variable values to zero. 27 | */ 28 | void sockettime_init(void) { 29 | int i; 30 | 31 | // Init timestamp table with all zeros 32 | for (i = 0; i < FD_SETSIZE; ++i) { 33 | times[i].socket = 0; 34 | times[i].timestamp = 0; 35 | } 36 | } 37 | 38 | /* 39 | * Set the timestamp for the given socket number to the current time. If 40 | * the socket does not exists on the list, it is added. 41 | */ 42 | void sockettime_set(int socket) { 43 | int i; 44 | 45 | for (i = 0; i < FD_SETSIZE; ++i) { 46 | if (times[i].socket == socket) { 47 | times[i].timestamp = (int)time(NULL); 48 | break; 49 | } 50 | if (times[i].socket == 0) { 51 | times[i].socket = socket; 52 | times[i].timestamp = (int)time(NULL); 53 | break; 54 | } 55 | } 56 | } 57 | 58 | /* 59 | * Return the timestamp for the given socket number. If the socket is 60 | * not found, zero is returned. 61 | */ 62 | int sockettime_get(int socket) { 63 | int i; 64 | 65 | for (i = 0; i < FD_SETSIZE; ++i) { 66 | if (times[i].socket == socket) 67 | return times[i].timestamp; 68 | 69 | if (times[i].socket == 0) 70 | return 0; 71 | } 72 | 73 | return 0; 74 | } 75 | 76 | /* 77 | * Return a true value if the number of seconds configured for IDLE_SECONDS 78 | * has passed since the last time the given socket timestamp was updated. 79 | * Return a false value if not. 80 | */ 81 | int sockettime_elapsed(int socket) { 82 | int i; 83 | 84 | for (i = 0; i < FD_SETSIZE; ++i) { 85 | if (times[i].socket == socket) { 86 | if (times[i].timestamp < time(NULL) - IDLE_SECONDS) 87 | return 1; 88 | else 89 | return 0; 90 | } 91 | } 92 | 93 | return 0; 94 | } 95 | -------------------------------------------------------------------------------- /src/modules/command.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Brian Barto 2 | // 3 | // This program is free software; you can redistribute it and/or modify it 4 | // under the terms of the MIT License. See LICENSE for more details. 5 | 6 | #include 7 | #include 8 | #include "config.h" 9 | #include "modules/command.h" 10 | 11 | /* 12 | * MODULE DESCRIPTION 13 | * 14 | * The command module manages a lookup table containing command-to-function 15 | * pairings. It is responsible for initializing the table, adding new 16 | * command/function pairs, and executing a function for a given command 17 | * string. 18 | */ 19 | 20 | /* 21 | * Static Variables 22 | */ 23 | static struct { 24 | char *command; 25 | comFunctionType functionPtr; 26 | } commands[COMMAND_LIMIT]; 27 | 28 | /* 29 | * Initializes all member values in the commands structure array to NULL. 30 | */ 31 | void command_init(void) { 32 | int i; 33 | 34 | for (i = 0; i < COMMAND_LIMIT; ++i) { 35 | commands[i].command = NULL; 36 | commands[i].functionPtr = NULL; 37 | } 38 | } 39 | 40 | /* 41 | * Add a given char and function pointer to the commands structure array 42 | * in the next available slot. 43 | */ 44 | void command_add(char *command, comFunctionType functionPtr) { 45 | int i; 46 | 47 | for (i = 0; i < COMMAND_LIMIT; ++i) { 48 | if (commands[i].command == NULL) { 49 | commands[i].command = command; 50 | commands[i].functionPtr = functionPtr; 51 | break; 52 | } 53 | } 54 | } 55 | 56 | /* 57 | * Traverse the commands structure array looking to match a given command 58 | * string. A true value is returned if a match is found. A false value is 59 | * returned if no match is found. 60 | */ 61 | int command_exists(char *command) { 62 | int i; 63 | 64 | for (i = 0; i < COMMAND_LIMIT; ++i) { 65 | if (commands[i].command == NULL) 66 | return 0; 67 | 68 | if (strcmp(commands[i].command, command) == 0) 69 | return 1; 70 | } 71 | 72 | return 0; 73 | } 74 | 75 | /* 76 | * Traverse the commands structure array looking to match a given command 77 | * string. If a match is found, the associated function is executed with 78 | * the socket and payload as parameters. 79 | */ 80 | void command_exec(char *command, char *payload, int socket) { 81 | int i; 82 | 83 | for (i = 0; i < COMMAND_LIMIT; ++i) { 84 | if (commands[i].command == NULL) 85 | break; 86 | 87 | if (strcmp(commands[i].command, command) == 0) { 88 | commands[i].functionPtr(socket, payload); 89 | break; 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/modules/readlist.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Brian Barto 2 | // 3 | // This program is free software; you can redistribute it and/or modify it 4 | // under the terms of the MIT License. See LICENSE for more details. 5 | 6 | #include 7 | #include 8 | #include "modules/readlist.h" 9 | 10 | /* 11 | * MODULE DESCRIPTION 12 | * 13 | * The readlist module maintains a list of socket file descriptors for 14 | * the specific purpose of determining which have data to be read. 15 | * Initially the main loop adds all active sockets to this list. Then 16 | * after readlist_wait() is called, the list is reduced to only those 17 | * with data available. Functions are provided to access access/modify 18 | * this list pre and post readlist_wait(). 19 | */ 20 | 21 | /* 22 | * Static Variables 23 | */ 24 | static fd_set read_fd_set; 25 | static int list_position; 26 | 27 | /* 28 | * Set all static variable values to zero. 29 | */ 30 | void readlist_init(void) { 31 | FD_ZERO(&read_fd_set); 32 | list_position = 0; 33 | } 34 | 35 | /* 36 | * Add the given socket to the readlist. 37 | */ 38 | void readlist_add(int socket) { 39 | FD_SET(socket, &read_fd_set); 40 | } 41 | 42 | /* 43 | * Wait for data to arrive at one of the sockets in the readlist. A 44 | * timeout value is used that is equal to the number of seconds configured 45 | * for PERIODIC_SECONDS. On success, the total number of file descriptors 46 | * that have data available for reading is returned, and the realist is 47 | * reduced to contain only those sockets. On timeout, zero is returned. 48 | * On failure, a negative integer is returned. 49 | */ 50 | int readlist_wait(unsigned int t) { 51 | int r; 52 | struct timeval timeout; 53 | 54 | // Set select() timeout value. 55 | // This needs to be inside the loop so it is reset for each loop interation. 56 | timeout.tv_sec = t; 57 | timeout.tv_usec = 0; 58 | 59 | // Block until input arrives on one or more active sockets 60 | r = select(FD_SETSIZE, &read_fd_set, NULL, NULL, &timeout); 61 | 62 | return r; 63 | } 64 | 65 | /* 66 | * Remove the given socket from the readlist. 67 | */ 68 | void readlist_remove(int socket) { 69 | FD_CLR(socket, &read_fd_set); 70 | } 71 | 72 | /* 73 | * Check if the given socket exists in the readlist. 74 | */ 75 | int readlist_check(int socket) { 76 | if (FD_ISSET(socket, &read_fd_set)) 77 | return 1; 78 | 79 | return 0; 80 | } 81 | 82 | /* 83 | * Return the next socket value from the readlist. Return a negative 84 | * value when the end of the list is reached. 85 | */ 86 | int readlist_get_next(void) { 87 | 88 | while (++list_position < FD_SETSIZE) 89 | if (FD_ISSET (list_position, &read_fd_set)) 90 | return list_position; 91 | 92 | readlist_reset_next(); 93 | 94 | return -1; 95 | } 96 | 97 | /* 98 | * Reset the list position used for traversing the readlist. 99 | */ 100 | void readlist_reset_next(void) { 101 | list_position = 0; 102 | } 103 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Installation directories following GNU conventions 2 | prefix ?= /usr/local 3 | exec_prefix = $(prefix) 4 | bindir = $(exec_prefix)/bin 5 | sbindir = $(exec_prefix)/sbin 6 | datarootdir = $(prefix)/share 7 | datadir = $(datarootdir) 8 | includedir = $(prefix)/include 9 | mandir = $(datarootdir)/man 10 | 11 | BIN=bin 12 | OBJ=obj 13 | SRC=src 14 | 15 | OBJ_MODS=obj/modules 16 | SRC_MODS=src/modules 17 | 18 | OBJ_EX=obj/example 19 | SRC_EX=src/example 20 | 21 | CC ?= gcc 22 | CFLAGS ?= -Wextra -Wall -iquote$(SRC) 23 | 24 | SERVERFLAGS = -DIS_SERVER=1 -DIS_CLIENT=0 25 | CLIENTFLAGS = -DIS_SERVER=0 -DIS_CLIENT=1 26 | 27 | .PHONY: all install uninstall clean 28 | 29 | EXES = server client 30 | EXES_EX = example_server example_client 31 | 32 | all: $(EXES) 33 | 34 | example: $(EXES_EX) 35 | 36 | server: $(OBJ_MODS)/termflag.o $(OBJ_MODS)/mainsocket.o $(OBJ_MODS)/inputpayload.o $(OBJ_MODS)/inputcommand.o $(OBJ_MODS)/nextperiodic.o $(OBJ_MODS)/sockettime.o $(OBJ_MODS)/readlist.o $(OBJ_MODS)/socketlist.o $(OBJ_MODS)/disconnectfunction.o $(OBJ_MODS)/connectfunction.o $(OBJ_MODS)/command.o $(OBJ_MODS)/periodic.o $(OBJ_MODS)/network.o $(OBJ_MODS)/log.o $(OBJ_MODS)/component.o $(OBJ_MODS)/main_server.o $(OBJ)/server.o | $(BIN) 37 | $(CC) $(CFLAGS) -o $(BIN)/$@ $^ 38 | 39 | client: $(OBJ_MODS)/termflag.o $(OBJ_MODS)/mainsocket.o $(OBJ_MODS)/inputpayload.o $(OBJ_MODS)/inputcommand.o $(OBJ_MODS)/nextperiodic.o $(OBJ_MODS)/sockettime.o $(OBJ_MODS)/readlist.o $(OBJ_MODS)/socketlist.o $(OBJ_MODS)/disconnectfunction.o $(OBJ_MODS)/connectfunction.o $(OBJ_MODS)/command.o $(OBJ_MODS)/periodic.o $(OBJ_MODS)/network.o $(OBJ_MODS)/log.o $(OBJ_MODS)/component.o $(OBJ_MODS)/main_client.o $(OBJ)/client.o | $(BIN) 40 | $(CC) $(CFLAGS) -o $(BIN)/$@ $^ 41 | 42 | example_server: $(OBJ_MODS)/termflag.o $(OBJ_MODS)/mainsocket.o $(OBJ_MODS)/inputpayload.o $(OBJ_MODS)/inputcommand.o $(OBJ_MODS)/nextperiodic.o $(OBJ_MODS)/sockettime.o $(OBJ_MODS)/readlist.o $(OBJ_MODS)/socketlist.o $(OBJ_MODS)/disconnectfunction.o $(OBJ_MODS)/connectfunction.o $(OBJ_MODS)/command.o $(OBJ_MODS)/periodic.o $(OBJ_MODS)/network.o $(OBJ_MODS)/log.o $(OBJ_MODS)/component.o $(OBJ_MODS)/main_server.o $(OBJ_EX)/server.o | $(BIN) 43 | $(CC) $(CFLAGS) -o $(BIN)/$@ $^ 44 | 45 | example_client: $(OBJ_MODS)/termflag.o $(OBJ_MODS)/mainsocket.o $(OBJ_MODS)/inputpayload.o $(OBJ_MODS)/inputcommand.o $(OBJ_MODS)/nextperiodic.o $(OBJ_MODS)/sockettime.o $(OBJ_MODS)/readlist.o $(OBJ_MODS)/socketlist.o $(OBJ_MODS)/disconnectfunction.o $(OBJ_MODS)/connectfunction.o $(OBJ_MODS)/command.o $(OBJ_MODS)/periodic.o $(OBJ_MODS)/network.o $(OBJ_MODS)/log.o $(OBJ_MODS)/component.o $(OBJ_MODS)/main_client.o $(OBJ_EX)/client.o | $(BIN) 46 | $(CC) $(CFLAGS) -o $(BIN)/$@ $^ 47 | 48 | $(OBJ)/%.o: $(SRC)/%.c | $(OBJ) 49 | $(CC) $(CFLAGS) -o $@ -c $< 50 | 51 | $(OBJ_MODS)/main_server.o: $(SRC_MODS)/main.c | $(OBJ_MODS) 52 | $(CC) $(CFLAGS) $(SERVERFLAGS) -o $@ -c $< 53 | 54 | $(OBJ_MODS)/main_client.o: $(SRC_MODS)/main.c | $(OBJ_MODS) 55 | $(CC) $(CFLAGS) $(CLIENTFLAGS) -o $@ -c $< 56 | 57 | $(OBJ_MODS)/%.o: $(SRC_MODS)/%.c | $(OBJ_MODS) 58 | $(CC) $(CFLAGS) -o $@ -c $< 59 | 60 | $(OBJ_EX)/%.o: $(SRC_EX)/%.c | $(OBJ_EX) 61 | $(CC) $(CFLAGS) -o $@ -c $< 62 | 63 | $(OBJ_MODS): $(OBJ) 64 | mkdir -p $(OBJ_MODS) 65 | 66 | $(OBJ_EX): $(OBJ) 67 | mkdir -p $(OBJ_EX) 68 | 69 | $(BIN): 70 | mkdir -p $(BIN) 71 | 72 | $(OBJ): 73 | mkdir -p $(OBJ) 74 | 75 | clean: 76 | rm -rf $(BIN) 77 | rm -rf $(OBJ) 78 | -------------------------------------------------------------------------------- /src/modules/component.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Brian Barto 2 | // 3 | // This program is free software; you can redistribute it and/or modify it 4 | // under the terms of the MIT License. See LICENSE for more details. 5 | 6 | #include 7 | #include 8 | #include 9 | #include "modules/command.h" 10 | #include "modules/connectfunction.h" 11 | #include "modules/disconnectfunction.h" 12 | #include "modules/log.h" 13 | #include "modules/mainsocket.h" 14 | #include "modules/network.h" 15 | #include "modules/periodic.h" 16 | #include "modules/socketlist.h" 17 | #include "modules/termflag.h" 18 | 19 | /* 20 | * MODULE DESCRIPTION 21 | * 22 | * This module is meant to act as an abstraction layer that sits above 23 | * other modules contained in this project and provides user-friendly 24 | * access to lower level functionality that greatly simplifies the 25 | * customization process for the client and server modules. 26 | */ 27 | 28 | /* 29 | * The write_socket() function writes the command (char *command) and 30 | * payload (char *payload) data to the socket (int s). If a write failure 31 | * occurs, it returns a negative integer. Otherwise, it returns the 32 | * number of bytes written. 33 | */ 34 | int write_socket(int s, char *command, char *payload) { 35 | int r, l; 36 | char *lstr; 37 | char *str; 38 | 39 | l = strlen(command); 40 | lstr = malloc(20); 41 | sprintf(lstr, "%i", l); 42 | 43 | str = malloc(strlen(lstr) + 1 + strlen(command) + strlen(payload) + 1); 44 | sprintf(str, "%s:%s%s", lstr, command, payload); 45 | r = network_write(s, str); 46 | free(str); 47 | free(lstr); 48 | 49 | return r; 50 | } 51 | 52 | /* 53 | * The close_socket() function closes the network socket (int s) and 54 | * ensures that necessary cleanup tasks are executed. 55 | */ 56 | 57 | void close_socket(int s) { 58 | network_close(s); 59 | socketlist_remove(s); 60 | disconnectfunction_exec(s); 61 | } 62 | 63 | /* 64 | * The main_socket() function returns the integer value for the main 65 | * socket file descriptor. For the client, the main socket is that which 66 | * communicates to the server. For the server, the main socket is that 67 | * which listens for new client connections. 68 | */ 69 | int main_socket(void) { 70 | return mainsocket_get(); 71 | } 72 | 73 | /* 74 | * The next_socket() function provides a iteration tool for traversing the 75 | * list of all active socket connections. Each call to next_socket() 76 | * returns the integer value for the next socket file descriptor in the 77 | * list. When the iteration reaches the end of the list, a negative 78 | * integer value is returned. 79 | */ 80 | int next_socket(void) { 81 | return socketlist_get_next(); 82 | } 83 | 84 | /* 85 | * The reset_next_socket() function sets an internally maintained list 86 | * position pointer to the start of the socket list. This function 87 | * should be called whenever you want the next_socket() function to 88 | * return the first socket in the socket list, such as the start of a 89 | * full socket list traversal. 90 | */ 91 | void reset_next_socket(void) { 92 | socketlist_reset_next(); 93 | } 94 | 95 | /* 96 | * The set_connect_function() function sets the given function pointer 97 | * (connectFunctionType functionPtr) as the "connect function". This 98 | * function is called for every new connection that is established. 99 | */ 100 | void set_connect_function(connectFunctionType functionPtr) { 101 | connectfunction_set(functionPtr); 102 | } 103 | 104 | /* 105 | * The set_disconnect_function() function sets the given function pointer 106 | * (disconnectFunctionType functionPtr) as the "disconnect function". This 107 | * function is called for every socket disconnection. 108 | */ 109 | void set_disconnect_function(disconnectFunctionType functionPtr) { 110 | disconnectfunction_set(functionPtr); 111 | } 112 | 113 | /* 114 | * The add_periodic_function() function adds the given function pointer 115 | * (prdFunctionType functionPtr) to a list of function pointers that are 116 | * executed at an interval defined by the symbolic constant 117 | * PERIODIC_SECONDS. 118 | */ 119 | void add_periodic_function(prdFunctionType functionPtr) { 120 | periodic_add(functionPtr); 121 | } 122 | 123 | /* 124 | * The add_command_function() function tells the client or server component 125 | * to execute the function pointer (comFunctionType functionPtr) when the 126 | * command (char *command) is received from a socket connection. 127 | */ 128 | void add_command_function(char *command, comFunctionType functionPtr) { 129 | command_add(command, functionPtr); 130 | } 131 | 132 | /* 133 | * The terminate() function tells the current client or server program 134 | * to terminate execution after the current function has returned 135 | * execution to the calling function. 136 | */ 137 | void terminate(void) { 138 | termflag_set(); 139 | } 140 | 141 | 142 | -------------------------------------------------------------------------------- /src/modules/log.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Brian Barto 2 | // 3 | // This program is free software; you can redistribute it and/or modify it 4 | // under the terms of the MIT License. See LICENSE for more details. 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "config.h" 13 | #include "modules/log.h" 14 | 15 | #define SERVER 1 16 | #define CLIENT 2 17 | #define ERRMSG_SIZE 200 18 | 19 | /* 20 | * MODULE DESCRIPTION 21 | * 22 | * The log module manages opening, closing and appending to the log 23 | * file. 24 | */ 25 | 26 | /* 27 | * Static Variables 28 | */ 29 | static FILE *logFile; 30 | static char errmsg[ERRMSG_SIZE]; 31 | 32 | /* 33 | * Initialize all static variables to NULL. 34 | */ 35 | void log_init(void) { 36 | memset(errmsg, 0, ERRMSG_SIZE); 37 | logFile = NULL; 38 | } 39 | 40 | /* 41 | * Open the log file using the SERVER flag. 42 | */ 43 | int log_open_server(void) { 44 | return log_open(SERVER); 45 | } 46 | 47 | /* 48 | * Open the log file using the CLIENT flag. 49 | */ 50 | int log_open_client(void) { 51 | return log_open(CLIENT); 52 | } 53 | 54 | /* 55 | * Open the log file. Use the symbolic constants SERVER or CLIENT for 56 | * logType. Returns 0 on success or a negative integer on failure. If an 57 | * error occurres, an error message with be stored in the errmsg char 58 | * array. 59 | */ 60 | int log_open(int logType) { 61 | int i; 62 | char *homeDir; 63 | char *logFilePath = LOG_FILE_PATH; 64 | char *logFileName; 65 | char *logFilePathName; 66 | char *logFileFullPathName; 67 | struct stat sb; 68 | 69 | // Do we use server of client log name? 70 | if (logType == SERVER) { 71 | logFileName = malloc(strlen(SERVER_LOG_NAME) + 1); 72 | strcpy(logFileName, SERVER_LOG_NAME); 73 | } else { 74 | logFileName = malloc(strlen(CLIENT_LOG_NAME) + 1); 75 | strcpy(logFileName, CLIENT_LOG_NAME); 76 | } 77 | 78 | // Combine log file path and name in to one string. 79 | if (logFilePath[(int)strlen(logFilePath) - 1] == '/') { 80 | logFilePathName = malloc(strlen(logFilePath) + strlen(logFileName) + 1); 81 | sprintf(logFilePathName, "%s%s", logFilePath, logFileName); 82 | } else { 83 | logFilePathName = malloc(strlen(logFilePath) + strlen(logFileName) + 2); 84 | sprintf(logFilePathName, "%s/%s", logFilePath, logFileName); 85 | } 86 | 87 | // Do we have a full path or relative path? 88 | // If relative, use home dir as base to make full path. 89 | if (logFilePathName[0] == '/') { 90 | logFileFullPathName = logFilePathName; 91 | } else { 92 | // Die if we don't have a home directory 93 | homeDir = getenv("HOME"); 94 | if (homeDir == NULL) { 95 | sprintf(errmsg, "Unable to read HOME environmental variable."); 96 | return -1; 97 | } 98 | 99 | // Build full log file path 100 | logFileFullPathName = malloc(strlen(logFilePathName) + strlen(homeDir) + 2); 101 | sprintf(logFileFullPathName, "%s/%s", homeDir, logFilePathName); 102 | } 103 | 104 | // Open log file, auto-create or die if can't open, die if can't auto-create. 105 | if ((logFile = fopen(logFileFullPathName, "a")) == NULL) { 106 | 107 | // Can't open file. Let's check and create necessary subdirs. 108 | if (strchr(logFileFullPathName, '/') != NULL) { 109 | for (i = 0; logFileFullPathName[i] != '\0'; ++i) { 110 | if (i == 0) 111 | continue; 112 | if (logFileFullPathName[i] == '/') { 113 | logFileFullPathName[i] = '\0'; 114 | if (stat(logFileFullPathName, &sb) != 0) { 115 | if (mkdir(logFileFullPathName, 0700) == -1) { 116 | sprintf(errmsg, "Unable to create log file directory (%s).", logFileFullPathName); 117 | free(logFileName); 118 | free(logFilePathName); 119 | free(logFileFullPathName); 120 | return -1; 121 | } 122 | } 123 | logFileFullPathName[i] = '/'; 124 | } 125 | } 126 | } 127 | 128 | if ((logFile = fopen(logFileFullPathName, "a")) == NULL) { 129 | sprintf(errmsg, "Unable to open or create log file (%s). Please ensure directory and/or file are writable.", logFileFullPathName); 130 | free(logFileName); 131 | free(logFilePathName); 132 | free(logFileFullPathName); 133 | return -1; 134 | } 135 | } 136 | 137 | return 0; 138 | } 139 | 140 | /* 141 | * Write an entry to the log file. The parameters are the same as the 142 | * printf() function. Automatically prepends a timestamp to the entry. 143 | */ 144 | void log_write(char *format, ...) { 145 | if (logFile == NULL) 146 | return; 147 | 148 | va_list argList; 149 | time_t timeNow; 150 | struct tm *timePtr; 151 | char timeString[20]; 152 | 153 | timeNow = time(NULL); 154 | timePtr = localtime(&timeNow); 155 | strftime(timeString, 20, "%Y-%m-%d %H:%M:%S", timePtr); 156 | fprintf(logFile, "\n[%s] ", timeString); 157 | 158 | va_start(argList, format); 159 | vfprintf(logFile, format, argList); 160 | va_end(argList); 161 | 162 | fflush(logFile); 163 | } 164 | 165 | /* 166 | * Write an entry to the log file. The parameters are the same as the 167 | * printf() function. Automatically prepends a timestamp to the entry. 168 | * This function will also print the entry to STDOUT. 169 | */ 170 | void log_print(char *format, ...) { 171 | va_list argList; 172 | time_t timeNow; 173 | struct tm *timePtr; 174 | char timeString[20]; 175 | 176 | va_start(argList, format); 177 | vprintf(format, argList); 178 | va_end(argList); 179 | 180 | printf("\n"); 181 | 182 | if (logFile == NULL) 183 | return; 184 | 185 | timeNow = time(NULL); 186 | timePtr = localtime(&timeNow); 187 | strftime(timeString, 20, "%Y-%m-%d %H:%M:%S", timePtr); 188 | fprintf(logFile, "\n[%s] ", timeString); 189 | 190 | va_start(argList, format); 191 | vfprintf(logFile, format, argList); 192 | va_end(argList); 193 | 194 | fflush(logFile); 195 | } 196 | 197 | /* 198 | * Close log file if opened. 199 | */ 200 | void log_close(void) { 201 | if (logFile != NULL) 202 | fclose(logFile); 203 | } 204 | 205 | /* 206 | * Return the errmsg string. 207 | */ 208 | char *log_get_errmsg(void) { 209 | return errmsg; 210 | } 211 | 212 | -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Brian Barto 2 | // 3 | // This program is free software; you can redistribute it and/or modify it 4 | // under the terms of the MIT License. See LICENSE for more details. 5 | 6 | #ifndef CONFIG_H 7 | #define CONFIG_H 1 8 | 9 | /* 10 | * The DEFAULT_HOST configuration sets the default host used by both the 11 | * client and server. 12 | * 13 | * Some examples of valid options are: 14 | * 15 | * Empty String e.g. "" 16 | * IP Address e.g. "192.168.0.1" 17 | * Hostname e.g. "localhost" 18 | * 19 | * The server uses this string to determine which network interface to 20 | * bind to. An empty string causes the server to listen for 21 | * incoming connections on all network interfaces. When set as an IP 22 | * address or a hostname, the server will only allow incoming connections 23 | * on the associated interface. 24 | * 25 | * The client uses this string to determine which address to connect to 26 | * when starting. If set to an empty string, the use of the -h 27 | * command line option is required to set the hostname. 28 | * 29 | * This needs to be a string literal. If you exclude the double-quotes, 30 | * bad things will happen. 31 | * 32 | * The use of the -h command line option will override this setting. 33 | */ 34 | #define DEFAULT_HOST "" 35 | 36 | /* 37 | * The DEFAULT_PORT configuration sets the default port used by both the 38 | * client and server. The server will use this value when listening for 39 | * incoming connections. The client will use this value when attempting 40 | * to connect to the server. 41 | * 42 | * The use of the -p command line option will override this setting. 43 | */ 44 | #define DEFAULT_PORT 51717 45 | 46 | /* 47 | * The COMMAND_LIMIT configuration sets the limit for the number of commands 48 | * that you can build programmatic responses for. The default setting of 49 | * 30 should be sufficient for most cases. If you require more, increase 50 | * this value as necessary. 51 | */ 52 | #define COMMAND_LIMIT 30 53 | 54 | /* 55 | * The MAX_COMMAND_SIZE configuration tells the client and server the 56 | * maximum number of characters that a command string may contain. If you 57 | * send a command string with a length that is greater than this setting, 58 | * the software will try to handle it appropriately, but you risk 59 | * truncating a portion of the payload data as a result. It is better to 60 | * simply increase this configuration as needed. Though this software can 61 | * handle up to 99 command string characters, it is suggested you keep it 62 | * at a reasonable limit of 10 or less for optimal memory consumption. 63 | * Never exceed a 99 or you risk potential memory fault errors. 64 | */ 65 | #define MAX_COMMAND_SIZE 5 66 | 67 | /* 68 | * The MAX_PAYLOAD_SIZE configuration sets the maximum number of characters 69 | * that can be contained in the payload portion of a transmission. If you 70 | * need to send a length of data longer than this value, increase as 71 | * necessary. Physical memory limitations may apply. 72 | */ 73 | #define MAX_PAYLOAD_SIZE 2000 74 | 75 | /* 76 | * The PERIODIC_SECONDS configuration sets the interval, in seconds, at 77 | * which the periodic functions are executed. Since both the server and 78 | * client execute as a single process, delays in periodic execution can 79 | * occur if other programmed functions are executing for a period of time 80 | * greater than this setting. 81 | */ 82 | #define PERIODIC_SECONDS 5 83 | 84 | /* 85 | * The PERIODIC_LIMIT configuration sets the limit for the number of 86 | * periodic functions you can create. The default setting of 30 should be 87 | * sufficient for most cases. If you require more, increase this value as 88 | * necessary. 89 | */ 90 | #define PERIODIC_LIMIT 30 91 | 92 | /* 93 | * The IDLE_SECONDS configuration sets number of seconds a client can be 94 | * idle before the server will automatically disconnect them. This is to 95 | * protect the server from maintaining connections with clients that have 96 | * unexpectedly disconnected, such as in the event of a crash or power failure. 97 | * 98 | * In order to prevent a functioning client from being disconnected during 99 | * long periods of time when it not required to communicate with the server, 100 | * it is suggested to set up a periodic function that sends a heartbeat 101 | * to the server. In this case, be sure that this value is not set lower 102 | * than the PERIODIC_SECONDS value. 103 | */ 104 | #define IDLE_SECONDS 10 105 | 106 | /* 107 | * The SERVER_LOG_NAME configuration sets the name of the file that the 108 | * server logs information to. Change it as needed. This must be a string 109 | * literal. If you exclude the double-quotes, bad things will happen. 110 | */ 111 | #define SERVER_LOG_NAME "server_log.txt" 112 | 113 | /* 114 | * The CLIENT_LOG_NAME configuration sets the name of the file that the 115 | * client logs information to. Change it as needed. This must be a string 116 | * literal. If you exclude the double-quotes, bad things will happen. 117 | */ 118 | #define CLIENT_LOG_NAME "client_log.txt" 119 | 120 | /* 121 | * The LOG_FILE_PATH configuration sets the path to the directory that 122 | * contains the log files. This can be a relative path or an absolute path. 123 | * A relative path is relative to the executing user's home directory. 124 | * The HOME environmental variable must be set for relative paths. 125 | * 126 | * The client and server will attempt to create any directories in the path 127 | * that do not exist. 128 | * 129 | * This must be a string literal. If you exclude the double-quotes, bad 130 | * things will happen. 131 | */ 132 | #define LOG_FILE_PATH ".config/spring_server/" 133 | 134 | /* 135 | * The INPUT_QUEUE_SIZE configuration sets the input queue size. 136 | * 137 | * Sometimes it is necessary to send multiple back-to-back data transmissions 138 | * from the client or server in a single instance. In this case, the 139 | * receiving operating system may concatenate them together as one long 140 | * string of data before the client or server application has a chance to 141 | * read it from the socket file descriptor. When this happens, the client 142 | * or server will deconstruct the data into multiple command/payload pairs 143 | * and treat them separately as intended. During this deconstruction, it 144 | * places them in an "input queue". This configuration sets the maximum 145 | * number of command/payload pairs it can parse from the socket file 146 | * descriptor in a single instance. 147 | * 148 | * It is not likely that you will have to change this value. 149 | */ 150 | #define INPUT_QUEUE_SIZE 10 151 | 152 | #endif 153 | -------------------------------------------------------------------------------- /src/modules/main.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Brian Barto 2 | // 3 | // This program is free software; you can redistribute it and/or modify it 4 | // under the terms of the MIT License. See LICENSE for more details. 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "modules/network.h" 13 | #include "modules/periodic.h" 14 | #include "modules/command.h" 15 | #include "modules/connectfunction.h" 16 | #include "modules/disconnectfunction.h" 17 | #include "modules/socketlist.h" 18 | #include "modules/readlist.h" 19 | #include "modules/sockettime.h" 20 | #include "modules/nextperiodic.h" 21 | #include "modules/inputcommand.h" 22 | #include "modules/inputpayload.h" 23 | #include "modules/mainsocket.h" 24 | #include "modules/termflag.h" 25 | #include "modules/log.h" 26 | #include "modules/main.h" 27 | #include "config.h" 28 | 29 | // Version Number 30 | #define VERSION "0.1.0" 31 | 32 | // Function prototypes 33 | void main_init(void); 34 | void main_sigint(int); 35 | void main_shutdown(const char *); 36 | 37 | /* 38 | * The main function handles program initialization and network connection, 39 | * argument checking, and manages the main program loop. Inside the loop 40 | * it coordinates the flow of data between the modules, does error checking, 41 | * and logs pertinent info. 42 | */ 43 | int main(int argc, char *argv[]) { 44 | int o, r, s, i; 45 | char *hostname, *portno; 46 | 47 | // Initialization functions 48 | main_init(); 49 | 50 | // Open log file 51 | if (IS_SERVER) 52 | r = log_open_server(); 53 | else 54 | r = log_open_client(); 55 | 56 | // Ensure log was successfully opened 57 | if (r < 0) 58 | main_shutdown(log_get_errmsg()); 59 | 60 | // Log startup message 61 | log_write("Starting Up"); 62 | 63 | // Set SIGINT handler 64 | signal(SIGINT, main_sigint); 65 | 66 | // Set default port 67 | portno = malloc(6); 68 | sprintf(portno, "%i", DEFAULT_PORT); 69 | 70 | // Set default hostname 71 | hostname = malloc(65); 72 | if (strlen(DEFAULT_HOST) == 0) 73 | hostname = NULL; 74 | else 75 | hostname = DEFAULT_HOST; 76 | 77 | // Check arguments 78 | while ((o = getopt(argc, argv, "h:p:v")) != -1) { 79 | switch (o) { 80 | case 'p': 81 | portno = optarg; 82 | break; 83 | case 'h': 84 | hostname = optarg; 85 | break; 86 | case 'v': 87 | printf("spring server version " VERSION "\n"); 88 | return 0; 89 | case '?': 90 | if (isprint(optopt)) 91 | log_print("Unknown option '-%c'.", optopt); 92 | else 93 | log_print("Unknown option character '\\x%x'.", optopt); 94 | main_shutdown("Invalid command option(s)."); 95 | } 96 | } 97 | 98 | // Log host and port 99 | log_write("host=%s port=%s", hostname, portno); 100 | 101 | // Execute network startup proceedure 102 | if (IS_SERVER) 103 | r = network_start_server(hostname, portno); 104 | else 105 | r = network_start_client(hostname, portno); 106 | 107 | // Check for error at startup 108 | if (r < 0) 109 | main_shutdown(network_get_errmsg()); 110 | 111 | // Print connection message 112 | log_print("%s on port %s", IS_SERVER ? "Listening" : "Connected", portno); 113 | 114 | // Set main socket 115 | mainsocket_set(r); 116 | 117 | // Add main socket to socket list and track it's time 118 | socketlist_add(mainsocket_get()); 119 | sockettime_set(mainsocket_get()); 120 | 121 | // Execute connect function for the client 122 | if (IS_CLIENT) 123 | connectfunction_exec(mainsocket_get()); 124 | 125 | // main program loop 126 | while (1) { 127 | 128 | // Initialize (empty) readlist 129 | readlist_init(); 130 | 131 | // Copy all active socket numbers to readlist 132 | while ((s = socketlist_get_next()) > 0) 133 | readlist_add(s); 134 | 135 | // Wait for incoming data on one of the readlist sockets or 136 | // timeout after the number of seconds configured for PERIODIC_SECONDS 137 | r = readlist_wait(PERIODIC_SECONDS); 138 | 139 | // Shutdown if we get an error code 140 | if (r < 0) 141 | main_shutdown("select() error"); 142 | 143 | // If we get incoming data on the main socket for the server 144 | // then we have a new client attempting to connect. 145 | if (IS_SERVER && readlist_check(mainsocket_get())) { 146 | 147 | // Update main socket time 148 | sockettime_set(mainsocket_get()); 149 | 150 | // Accept new connection 151 | r = network_accept(mainsocket_get()); 152 | 153 | // Shut down if we get an error code 154 | if (r < 0) 155 | main_shutdown("accept() error"); 156 | 157 | // Add new socket to our socket list 158 | socketlist_add(r); 159 | sockettime_set(r); 160 | 161 | // Remove main socket from our list now that we just serviced it 162 | readlist_remove(mainsocket_get()); 163 | 164 | // Log socket assignment and IP 165 | log_write("Client connected from %s. Assigned socket %i.", network_get_ipaddress(), r); 166 | 167 | // Execute connect function for new socket/client 168 | connectfunction_exec(r); 169 | 170 | // Check if termflag was set in connect function 171 | if (termflag_isset()) 172 | main_shutdown("Terminated."); 173 | } 174 | 175 | // Loop over readlist and service each socket 176 | while ((s = readlist_get_next()) > 0) { 177 | 178 | // Read data from socket 179 | r = network_read(s); 180 | 181 | // Shut down if we get an error code 182 | if (r < 0) 183 | main_shutdown("read() error"); 184 | 185 | // Check if socket terminated the connection 186 | if (r == 0) { 187 | 188 | // Log and close socket on the server side. Shut down on the 189 | // client side. 190 | if (IS_SERVER) { 191 | log_write("Client terminated connection. Closing socket %i.", s); 192 | network_close(s); 193 | socketlist_remove(s); 194 | } else 195 | main_shutdown("Server terminated connection."); 196 | 197 | // Execute disconnect function 198 | disconnectfunction_exec(s); 199 | 200 | // Check if termflag was set in disconnect function 201 | if (termflag_isset()) 202 | main_shutdown("Terminated."); 203 | 204 | // Force next loop iteration. 205 | continue; 206 | } 207 | 208 | // Update socket time 209 | sockettime_set(s); 210 | 211 | // Loop over each command/payload pair sent from the socket 212 | for (i = 0; strlen(network_get_readdata(i)) > 0; ++i) { 213 | 214 | // Parse out the command and payload from the data 215 | inputcommand_parse(network_get_readdata(i)); 216 | inputpayload_parse(network_get_readdata(i)); 217 | 218 | // Log the command we received 219 | log_write("Received command %s from socket %i", inputcommand_get(), s); 220 | 221 | // Validate and execute command 222 | if (command_exists(inputcommand_get())) 223 | command_exec(inputcommand_get(), inputpayload_get(), s); 224 | 225 | // Check if termflag was set in command function 226 | if (termflag_isset()) 227 | main_shutdown("Terminated."); 228 | } 229 | } 230 | 231 | // Close all sockets whos idle time elapsed (server only) 232 | while (IS_SERVER && (s = socketlist_get_next()) > 0) { 233 | if (s != mainsocket_get() && sockettime_elapsed(s)) { 234 | network_close(s); 235 | socketlist_remove(s); 236 | disconnectfunction_exec(s); 237 | } 238 | } 239 | 240 | // Run periodic functions if number of seconds in PERIODIC_SECONDS 241 | // has passed since last run. 242 | if (nextperiodic_elapsed(PERIODIC_SECONDS)) { 243 | periodic_exec(); 244 | nextperiodic_reset(); 245 | } 246 | 247 | // Check if termflag was set in periodic function 248 | if (termflag_isset()) 249 | main_shutdown("Terminated."); 250 | 251 | } 252 | 253 | return 0; 254 | } 255 | 256 | /* 257 | * The main_init() function initializes all the modules. Each of the 258 | * module's init functions simply reserve and scrub memory needed for 259 | * static data storage. 260 | */ 261 | void main_init(void) { 262 | network_init(); 263 | readlist_init(); 264 | periodic_init(); 265 | command_init(); 266 | socketlist_init(); 267 | readlist_init(); 268 | sockettime_init(); 269 | nextperiodic_init(); 270 | inputcommand_init(); 271 | inputpayload_init(); 272 | log_init(); 273 | termflag_init(); 274 | 275 | if (IS_SERVER) { 276 | server_init(); 277 | } else { 278 | client_init(); 279 | } 280 | } 281 | 282 | /* 283 | * The main_sigint() function serves as the SIGINT handler. This function 284 | * is called when the user presses CTRL-C. It's job is to simply call the 285 | * main_shutdown() function to ensure a proper shutdown. 286 | */ 287 | void main_sigint(int e) { 288 | (void)e; 289 | main_shutdown("Caught sigint."); 290 | } 291 | 292 | /* 293 | * The main_shutdown() function ensures all open file descriptors are closed 294 | * cleanly, and that memory resources are freed. It also calls the disconnect 295 | * function for each socket ensuring any customized cleanup tasks are also 296 | * completed. 297 | */ 298 | void main_shutdown(const char *errmsg) { 299 | int i; 300 | 301 | // Log a shutdown message 302 | log_print("Shutting down. Reason: %s", errmsg); 303 | 304 | // Close each socket. Run the disconnect function for each socket. 305 | while ((i = socketlist_get_next()) > 0) { 306 | network_close(i); 307 | socketlist_remove(i); 308 | if (i != mainsocket_get()) 309 | disconnectfunction_exec(i); 310 | } 311 | 312 | // Close log file 313 | log_close(); 314 | 315 | // Shutdown 316 | exit(1); 317 | } 318 | 319 | -------------------------------------------------------------------------------- /src/modules/network.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Brian Barto 2 | // 3 | // This program is free software; you can redistribute it and/or modify it 4 | // under the terms of the MIT License. See LICENSE for more details. 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "modules/network.h" 15 | #include "config.h" 16 | 17 | #define IPV4_ADDRESS_LENGTH 15 18 | #define ERRMSG_SIZE 100 19 | #define MAX_READ_SIZE MAX_COMMAND_SIZE + MAX_PAYLOAD_SIZE + 3 20 | 21 | /* 22 | * MODULE DESCRIPTION 23 | * 24 | * The network module is generally responsible for all direct network 25 | * communication. Many network-related tasks are performed by this 26 | * module: binding to network interfaces, accepting new connections, 27 | * writing to sockets, reading from sockets, closing sockets, etc. 28 | * 29 | * In some cases, this module is also responsible for formatting data 30 | * that it sends over the network, and on the other end, deconstructing 31 | * the data based on that format. These tasks may be better handled by a 32 | * separate module, but for now we do it here. 33 | */ 34 | 35 | /* 36 | * Static Variables 37 | */ 38 | static char ipaddress[IPV4_ADDRESS_LENGTH + 1]; 39 | static char errmsg[ERRMSG_SIZE]; 40 | static char inputqueue[INPUT_QUEUE_SIZE][MAX_READ_SIZE + 1]; 41 | 42 | /* 43 | * Initialize the static variables. Set all values to NULL (zero). 44 | */ 45 | void network_init(void) { 46 | int i; 47 | 48 | memset(ipaddress, 0, IPV4_ADDRESS_LENGTH + 1); 49 | memset(errmsg, 0, ERRMSG_SIZE); 50 | 51 | for (i = 0; i < INPUT_QUEUE_SIZE; i++) 52 | memset(inputqueue[i], 0, MAX_READ_SIZE + 1); 53 | } 54 | 55 | /* 56 | * This is the startup procedure for the server component. It determines 57 | * which network interface to bind to, based on the given hostname value. 58 | * Then it binds to it and starts listening for incoming connections on 59 | * the given port number. On success, an integer value is returned that 60 | * represents the file descriptor for the socket that listens for new 61 | * connections. If an error occurres, a negative value is returned and 62 | * the static variable errmsg is populated with an error description. 63 | */ 64 | int network_start_server(char *hostname, char *portno) { 65 | int startsockfd = 0; 66 | struct addrinfo hints; 67 | struct addrinfo *result, *rp; 68 | 69 | // Initializing serv_addr memory footprint to all integer zeros ('\0') 70 | memset((char *) &hints, 0, sizeof(struct addrinfo)); 71 | 72 | // Get one or more arrdinfo structures that conforms with that provided by 'hints' 73 | hints.ai_family = AF_INET; // Return IPv4 choices 74 | hints.ai_socktype = SOCK_STREAM; // We want a TCP socket 75 | hints.ai_flags = AI_PASSIVE; // All interfaces 76 | if (getaddrinfo(hostname, portno, &hints, &result) != 0) { 77 | sprintf(errmsg, "Could not obtain internet address info."); 78 | return -1; 79 | } 80 | 81 | // Loop over results from getaddrinfo() and try to bind. Exit loop on first successful bind. 82 | for (rp = result; rp != NULL; rp = rp->ai_next) { 83 | startsockfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); 84 | if (startsockfd == -1) 85 | continue; 86 | 87 | if (bind(startsockfd, rp->ai_addr, rp->ai_addrlen) == 0) 88 | break; // Successful binding 89 | 90 | close(startsockfd); 91 | } 92 | 93 | // Error if we didn't bind to any sockets 94 | if (rp == NULL) { 95 | sprintf(errmsg, "Could not bind to socket %i", startsockfd); 96 | return -1; 97 | } 98 | 99 | // Free the result structure we don't need anymore 100 | freeaddrinfo(result); 101 | 102 | // Mark socket as accepting connections, up to 5 backlogged connections 103 | listen(startsockfd, 5); 104 | 105 | return startsockfd; 106 | } 107 | 108 | /* 109 | * This is the startup procedure for the client component. It attempts 110 | * to establish a connection with the given server hostname on the given 111 | * port number. On success, an integer value is returned that represents 112 | * the server socket file descriptor. If an error occurres, a negative 113 | * value is returned and the static variable errmsg is populated with an 114 | * error description. 115 | */ 116 | int network_start_client(char *hostname, char *portno) { 117 | int startsockfd = 0; 118 | struct hostent *server; 119 | struct sockaddr_in serv_addr; 120 | 121 | // Require hostname and port 122 | if (hostname == NULL) { 123 | sprintf(errmsg, "hostname can not be NULL"); 124 | return -1; 125 | } 126 | 127 | // Set up a socket in the AF_INET domain (Internet Protocol v4 addresses) 128 | startsockfd = socket(AF_INET, SOCK_STREAM, 0); 129 | if (startsockfd < 0) { 130 | sprintf(errmsg, "Could not create socket"); 131 | return -1; 132 | } 133 | 134 | // Get a pointer to 'hostent' containing info about host. 135 | server = gethostbyname(hostname); 136 | if (server == NULL) { 137 | sprintf(errmsg, "no such host: %s", hostname); 138 | return -1; 139 | } 140 | 141 | // Initializing serv_addr memory footprint to all integer zeros ('\0') 142 | memset(&serv_addr, 0, sizeof(serv_addr)); 143 | 144 | // Setting up our serv_addr structure 145 | serv_addr.sin_family = AF_INET; // Internet Protocol v4 addresses 146 | memcpy(&serv_addr.sin_addr.s_addr, server->h_addr, server->h_length); // Copy address 147 | serv_addr.sin_port = htons(atoi(portno)); // Convert port byte order to 'network byte order' 148 | 149 | // Connect to server. Error if can't connect. 150 | if (connect(startsockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0) { 151 | sprintf(errmsg, "error conecting to host %s port %s", hostname, portno); 152 | return -1; 153 | } 154 | 155 | return startsockfd; 156 | } 157 | 158 | /* 159 | * Accept a new connection on the given socket. This is only run by the 160 | * server component and generally should only use the "main socket" that 161 | * is returned from the server startup procedure. On success, a new 162 | * socket integer value is returned and the IP address of the connecting 163 | * entity is stored in the static variable ipaddress. On failure, a 164 | * negative value is returned. 165 | */ 166 | int network_accept(int socket) { 167 | int newsockfd; 168 | struct sockaddr_in cliaddr; 169 | socklen_t clilen; 170 | 171 | clilen = sizeof(cliaddr); 172 | 173 | memset(ipaddress, 0, IPV4_ADDRESS_LENGTH + 1); 174 | 175 | newsockfd = accept(socket, (struct sockaddr *)&cliaddr, &clilen); 176 | 177 | if (newsockfd) { 178 | sprintf(ipaddress, "%d.%d.%d.%d", 179 | (int)(cliaddr.sin_addr.s_addr&0xFF), 180 | (int)((cliaddr.sin_addr.s_addr&0xFF00)>>8), 181 | (int)((cliaddr.sin_addr.s_addr&0xFF0000)>>16), 182 | (int)((cliaddr.sin_addr.s_addr&0xFF000000)>>24)); 183 | } 184 | 185 | return newsockfd; 186 | } 187 | 188 | /* 189 | * Read data from the given socket. This function assumes the data was 190 | * written by network_write() and therefore conforms to the structure it 191 | * uses to define the boundaries of multiple command/payload pairings 192 | * waiting in the queue at the same time. On success, each command/payload 193 | * pairing is stored as a single string in the inputqueue static array. 194 | * On failure, a negative value is returned. 195 | */ 196 | int network_read(int socket) { 197 | unsigned int len; 198 | int i, r; 199 | int q = 0; 200 | char *buffer, *bufferstart; 201 | 202 | // Set the buffer and input queue with all integer zeros ('\0') 203 | for (i = 0; i < INPUT_QUEUE_SIZE; i++) 204 | memset(inputqueue[i], 0, MAX_READ_SIZE + 1); 205 | 206 | // Get length of data waiting to be read 207 | ioctl(socket, FIONREAD, &r); 208 | 209 | if (r > 0) { 210 | // Set input buffer to length (plus 1) of waiting data 211 | buffer = malloc(r + 1); 212 | 213 | // Keep pointer to buffer start so we can free it 214 | bufferstart = buffer; 215 | 216 | // read incoming data in to buffer 217 | r = read(socket, buffer, r); 218 | 219 | // Parse out the incoming data and store in inputqueue array 220 | if (r > 0) { 221 | while (*buffer != '\0') { 222 | len = 0; 223 | while (*buffer >= '0' && *buffer <= '9') 224 | len = (len * 10) + *buffer++ - '0'; 225 | 226 | if (len > 0 && *buffer == ':' && strlen(++buffer) >= len) 227 | if (len <= MAX_READ_SIZE) 228 | strncpy(inputqueue[q++], buffer, len); 229 | else 230 | strncpy(inputqueue[q++], buffer, MAX_READ_SIZE); 231 | else 232 | break; 233 | 234 | buffer += len; 235 | } 236 | } 237 | 238 | // Free buffer 239 | free(bufferstart); 240 | } 241 | 242 | return r; 243 | } 244 | 245 | /* 246 | * Return the character array pointer stored in the input queue at the 247 | * given index. Return a null string if the given index is outside of the 248 | * inputqueue boundaries. 249 | */ 250 | char *network_get_readdata(int r) { 251 | if (r < INPUT_QUEUE_SIZE) 252 | return inputqueue[r]; 253 | else 254 | return ""; 255 | } 256 | 257 | /* 258 | * Write the given data string to the given socket file descriptor. This 259 | * function will prepend the length of the data string to the outgoing 260 | * package so that network_read() can identify the boundaries. On success, 261 | * the number of bytes written to the socket is returned. On failure, a 262 | * negative integer is returned. 263 | */ 264 | int network_write(int socket, char *data) { 265 | int r, l; 266 | char *lstr; 267 | char *str; 268 | 269 | l = strlen(data); 270 | lstr = malloc(20 + l); 271 | sprintf(lstr, "%i", l); 272 | 273 | str = malloc(strlen(lstr) + 1 + strlen(data) + 1); 274 | sprintf(str, "%s:%s", lstr, data); 275 | 276 | r = write(socket, str, strlen(str)); 277 | 278 | free(str); 279 | free(lstr); 280 | 281 | return r; 282 | } 283 | 284 | /* 285 | * Close a socket file descriptor. 286 | */ 287 | void network_close(int socket) { 288 | close(socket); 289 | } 290 | 291 | /* 292 | * Return the static char pointer errmsg. 293 | */ 294 | char *network_get_errmsg(void) { 295 | return errmsg; 296 | } 297 | 298 | /* 299 | * Return the static char pointer ipaddress. 300 | */ 301 | char *network_get_ipaddress(void) { 302 | return ipaddress; 303 | } 304 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Version](https://img.shields.io/badge/Version-0.1.0-green.svg) 2 | 3 | Like this project? Consider sponsoring me: [https://github.com/sponsors/bartobri](https://github.com/sponsors/bartobri) 4 | 5 | Spring Server 6 | ============= 7 | 8 | **Table of Contents** 9 | 10 | 1. [About](#about) 11 | 2. [Demo Videos](#demo-videos) 12 | 3. [Download and Build](#download-and-build) 13 | 4. [Usage](#usage) 14 | 5. [Customizing](#customizing) 15 | 6. [Example](#example) 16 | 7. [License](#license) 17 | 18 | About 19 | ----- 20 | 21 | The goal of this project is to provide a simple and robust framework that 22 | enables the swift creation of client-server applications. 23 | 24 | Two executables, `client` and `server`, are provided with this project. They 25 | come pre-equipped to easily connect and exchange data over a TCP/IP 26 | network socket. 27 | 28 | The `server` component can independently manage up to 1028 concurrent client 29 | connections. It can automatically detect and disconnect inactive clients. 30 | 31 | Both the `client` and `server` components provide hooks to execute 32 | custom-written functions. They also provide tools to send, receive, or log 33 | data from within those functions. 34 | 35 | Through the development of custom-written functions, the `client` and 36 | `server` can carry out virtually any task that can be written in C. 37 | 38 | Knowledge of C is required. 39 | 40 | Demo Videos 41 | ----------- 42 | 43 | **Quick Demo (2:26)** - Creating a client-server application in 2 minutes 44 | flat. 45 | 46 | Quick Demo 47 | 48 | **Custom Function Demo (13:54)** - How to customize the client and server 49 | with custom functions. 50 | 51 | Custom Functions 52 | 53 | **Function Tools Demo (18:50)** - How to interact with sockets to exchange 54 | data between the client and server, write to the log file, and a few 55 | other helpful commands. 56 | 57 | Custom Functions 58 | 59 | Download and Build 60 | ------------------ 61 | 62 | **NOTE** - This install procedure has only been confirmed on a handful of 63 | Linux variants, but likely works for most all of them. OSX is untested, 64 | but if you are using a Mac, be sure to have the developer tools installed. 65 | 66 | In order to download and build this program, you will need to have git, 67 | gcc, and make installed. Install them from your package manager if not 68 | already installed. 69 | 70 | ``` 71 | $ which make 72 | /usr/bin/make 73 | 74 | $ which gcc 75 | /usr/bin/gcc 76 | 77 | $ which git 78 | /usr/bin/git 79 | ``` 80 | 81 | Next download and build the server and client apps: 82 | ``` 83 | git clone https://github.com/bartobri/spring-server.git 84 | cd spring-server 85 | make 86 | ``` 87 | 88 | The resulting binaries will be located in the `spring-server/bin/` directory. 89 | 90 | Usage 91 | ----- 92 | 93 | ``` 94 | $ bin/server # Run with default bindings and port 95 | $ bin/server -h localhost # Specify the hostname or IP of the interface to bind to 96 | $ bin/server -p 8880 # Specify the port number to listen on 97 | 98 | $ bin/client -h localhost # Specify hostname to connect to 99 | $ bin/client -h localhost -p 8880 # Specify host and port number to connect to 100 | ``` 101 | 102 | If you don't specify a hostname or port number, the server and client 103 | will use the DEFAULT_HOST and DEFAULT_PORT settings contained in the config.h 104 | header file. If DEFAULT_HOST is set to a null string, the server will 105 | bind to all available network interfaces, while the client will require 106 | the use of the `-h` command line argument to set the hostname or IP address 107 | that it should connect to. 108 | 109 | Note that if you run these programs without any customizations, the client 110 | and server programs can successfully connect but will not exchange data or 111 | perform tasks. Since no data is being exchanged, the server will disconnect 112 | the client after about 10-15 seconds due to inactivity (this is configurable). 113 | 114 | Customizing 115 | ----------- 116 | 117 | There are only 3 files that you ever need to modify in order to customize 118 | the client and server components to do virtually anything you want them to do. 119 | 120 | 1. **config.h** - Contains global settings used by both the client and server. 121 | 2. **server.c** - Contains custom function definitions for the server. 122 | 3. **client.c** - Contains custom function definitions for the client. 123 | 124 | 95% of customizing will be through the creation of custom functions for 125 | the client and server to execute. 126 | 127 | ##### Defining Custom Functions 128 | 129 | There are 4 classes of custom functions. Each function class is 130 | executed differently. 131 | 132 | 1. **Connect Function** - Executed when a new connection is made. 133 | 2. **Disconnect Function** - Executed when a connection is terminated. 134 | 3. **Periodic Function** - Executed at a timed interval (configured in config.h). 135 | 4. **Command Function** - Executed in response to a command sent from the client or server. 136 | 137 | Custom functions should be defined with specific parameters and return 138 | values. Each function type has it's own macro to help with this. Below 139 | is how each of the four function classes should be defined using these 140 | macros. 141 | 142 | ``` 143 | CONNECT_FUNCTION(function_name) { 144 | (void)socket; 145 | 146 | // Code here 147 | } 148 | 149 | DISCONNECT_FUNCTION(function_name) { 150 | (void)socket; 151 | 152 | // Code here 153 | } 154 | 155 | PERIODIC_FUNCTION(function_name) { 156 | // Code here 157 | } 158 | 159 | COMMAND_FUNCTION(function_name) { 160 | (void)socket; 161 | (void)payload; 162 | 163 | // Code here 164 | } 165 | ``` 166 | 167 | These macros expand into a traditional function definition with the 168 | expected parameters and return values. Be sure to replace 169 | "function_name" with a unique function name. 170 | 171 | Connect and Disconnect functions are passed the integer value for the 172 | socket that connected or disconnected, defined as `int socket`. 173 | 174 | Command functions are passed the integer value of the socket that sent 175 | the command, and a string pointer optionally containing a string of 176 | characters referred to as the payload. They are defined as `int socket` 177 | and `char *payload`. 178 | 179 | Periodic functions are not passed any parameters. 180 | 181 | All functions have a return value of `void`. 182 | 183 | ##### Executing Custom Functions 184 | 185 | Once functions are defined inside the server.c and client.c files using 186 | the templates above, you must specify when they are to be executed. This 187 | is done inside the server_init() and client_init() functions which are 188 | defined inside the client.c and server.c files respectively, which should 189 | be just below where you defined your custom functions. 190 | 191 | ``` 192 | void server_init(void) { 193 | set_connect_function(&function_name); 194 | set_disconnect_function(&function_name); 195 | add_periodic_function(&function_name); 196 | add_command_function("cmnd", &function_name); 197 | } 198 | 199 | ``` 200 | 201 | Only one connect and disconnect function can be set. 202 | 203 | Multiple periodic functions can be set. They are executed in the order of which 204 | they were added inside the server/client init function. The max allowed 205 | is configured in config.h (see PERIODIC_LIMIT). 206 | 207 | Multiple command functions can also be set, but each function must be 208 | paired with a unique command string when it is set. The command string 209 | tells the client or server to execute the function when it receives the 210 | command when reading data from a network socket. 211 | 212 | Commands are sent from the client or server using a tool provided with 213 | this framework. More on tools in the next section. 214 | 215 | ##### Function Tools 216 | 217 | Several tools are provided with this framework to make it simple to 218 | exchange data, and perform other common tasks, from within your custom 219 | functions. 220 | 221 | ``` 222 | /* 223 | * The write_socket() function writes the command (char *command) and 224 | * payload (char *payload) data to the socket (int s). If a write failure 225 | * occurs, it returns a negative integer. Otherwise, it returns the 226 | * number of bytes written. 227 | */ 228 | int write_socket(int s, char *command, char *payload); 229 | 230 | /* 231 | * The close_socket() function closes the network socket (int s) and 232 | * ensures that necessary cleanup tasks are executed. 233 | */ 234 | void close_socket(int s); 235 | 236 | /* 237 | * The main_socket() function returns the integer value for the main 238 | * socket file descriptor. For the client, the main socket is that which 239 | * communicates to the server. For the server, the main socket is that 240 | * which listens for new client connections. 241 | */ 242 | int main_socket(void); 243 | 244 | /* 245 | * The next_socket() function provides a iteration tool for traversing the 246 | * list of all active socket connections. Each call to next_socket() 247 | * returns the integer value for the next socket file descriptor in the 248 | * list. When the iteration reaches the end of the list, a negative 249 | * integer value is returned. 250 | */ 251 | int next_socket(void); 252 | 253 | /* 254 | * The reset_next_socket() function sets an internally maintained list 255 | * position pointer to the start of the socket list. This function 256 | * should be called whenever you want the next_socket() function to 257 | * return the first socket in the socket list, such as the start of a 258 | * full socket list traversal. 259 | */ 260 | void reset_next_socket(void); 261 | 262 | /* 263 | * The terminate() function tells the current client or server program 264 | * to terminate execution after the current function has returned 265 | * execution to the calling function. 266 | */ 267 | void terminate(void); 268 | 269 | /* 270 | * The write_log() function writes an entry to the log file. Uses printf 271 | * formatting and parameters. 272 | */ 273 | void write_log(char *format_string, ...); 274 | 275 | /* 276 | * The print_log() function is the same as write_log() but also prints 277 | * the formatted string to stdout. 278 | */ 279 | void print_log(char *format_string, ...); 280 | ``` 281 | 282 | Example 283 | ------- 284 | 285 | An example is provided that demonstrates the creation and triggering of one 286 | of each of the four classes of custom functions. The customized client 287 | and server examples are located in the following directory. 288 | 289 | ``` 290 | spring-server/src/example 291 | ``` 292 | 293 | You can compile and run them from the spring-server directory: 294 | 295 | ``` 296 | // Make the example client and server 297 | $ make example 298 | 299 | // Run the example server and client 300 | $ bin/example_server 301 | $ bin/example_client -h localhost 302 | ``` 303 | 304 | This is the process flow that they will execute: 305 | 306 | 1. The server component has a connect function defined and loaded, called "say_hello". When the client connects, this function is executed and sends the command "hello" to the new client. 307 | 2. The client component has a command function defined and loaded, called "receive_hello". It has associated the command "hello" with this function inside the client_init() code block. When the server sends the "hello" command to the client, the client executes the receive_hello function. Inside the function, it prints a message to stdout acknowledging the receipt of the command. 308 | 3. The client component has a periodic function defined and loaded. This is executed at the interval defined in config.h for PERIODIC_SECONDS, which is 5 seconds by default. The function sends the "beat" command to the server for the first 5 intervals, and stops sending it every interval thereafter. 309 | 4. The server component receives the "beat" command from the client, but does not have any command functions associated with it, so no function is executed as a response. But the server still acknowledges the receipt of data from the client by not auto-disconnecting it during the time it is receiving the periodic "beat" commands. 310 | 5. Once the client stops sending "beat" commands to the server, the server waits for a period of time defined in config.h for IDLE_SECONDS and then disconnects the client due to inactivity. 311 | 6. The server component has a disconnect function defined and loaded. When it disconnects the client, it executes this function. This function uses print_log() to add an entry to the logfile and print it to stdout. 312 | 313 | License 314 | ------- 315 | 316 | This program is free software; you can redistribute it and/or modify it under the terms of the the 317 | MIT License (MIT). See [LICENSE](LICENSE) for more details. 318 | 319 | --------------------------------------------------------------------------------