6 ex__network_8c
gullradriel edited this page 2026-10-09 00:55:18 +02:00

examples/ex_network.c

Functions

Name
void usage(void )
void process_args(int argc, char ** argv, char ** address, char ** server, char ** port, int * nb, int * ip_version, int * udp_mode)
int main(int argc, char ** argv)

Attributes

Name
int use_udp
pthread_t netw_thr
NETWORK * netw_server
NETWORK * netw
Network for server mode, accepting incomming.
int mode
Network for managing conenctions.
int ip_mode
int NB_ATTEMPTS

Defines

Name
SERVER
CLIENT

Functions Documentation

function usage

void usage(
    void 
)

function process_args

void process_args(
    int argc,
    char ** argv,
    char ** address,
    char ** server,
    char ** port,
    int * nb,
    int * ip_version,
    int * udp_mode
)

function main

int main(
    int argc,
    char ** argv
)

Attributes Documentation

variable use_udp

int use_udp = 0;

variable netw_thr

static pthread_t netw_thr;

variable netw_server

NETWORK * netw_server = NULL;

variable netw

NETWORK * netw = NULL;

Network for server mode, accepting incomming.

variable mode

int mode = -1;

Network for managing conenctions.

variable ip_mode

int ip_mode = 0;

variable NB_ATTEMPTS

int NB_ATTEMPTS = 3;

Macros Documentation

define SERVER

#define SERVER 0

define CLIENT

#define CLIENT 1

Source code

/*
 * Nilorea Library
 * Copyright (C) 2005-2026 Castagnier Mickael
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * permissions and limitations under the License.
 *
 * SPDX-License-Identifier: Apache-2.0
 */


#include <stdio.h>
#include <errno.h>

#include "ex_network.h"

#define SERVER 0
#define CLIENT 1

int NB_ATTEMPTS = 3;

NETWORK *netw_server = NULL, 
    *netw = NULL;            

int mode = -1, ip_mode = NETWORK_IPALL;
int use_udp = 0;

static pthread_t netw_thr;

void usage(void) {
    fprintf(stderr,
            "     -a 'serveur address name/ip to bind (server mode) (optionnal)\n"
            "     -s 'serveur address name/ip to connect (client mode)\n"
            "     -p 'port': port to use, server or client\n"
            "     -u: use UDP transport instead of TCP\n"
            "     -i 'ipmode': optional, ip version to use. Default support both ipv4 and ipv6. Values: ipv4, ipv6\n"
            "     -n 'nb': optional, number of attempts (default: 3)\n"
            "     -h: show that help file\n"
            "     -v: show the program version\n"
            "     -V 'log level': optional, set the log level. Default: LOG_ERR, values: LOG_INFO, LOG_NOTICE, LOG_ERR, LOG_DEBUG\n");
}

void process_args(int argc, char** argv, char** address, char** server, char** port, int* nb, int* ip_version, int* udp_mode) {
    int getoptret = 0,
        log_level = LOG_ERR; /* default log level */

    /* Arguments optionnels */
    /* -v version
     * -V log level
     * -h help
     * -a address name/ip to bind to (server mode, NULL if not specified)
     * -s serveur address name/ip to connect (client mode)
     * -p port
     * -u udp mode
     * -i v4 ip version (default support both ipv4 and ipv6 )
     * -i v6 ip version (   "       "     "    "    "   "   )
     */
    if (argc == 1) {
        fprintf(stderr, "No arguments given, help:\n");
        usage();
        exit(1);
    }
    while ((getoptret = getopt(argc, argv, "hvus:V:p:n:i:a:")) != EOF) {
        switch (getoptret) {
            case 'i':
                if (!strcmp("v4", optarg)) {
                    (*ip_version) = NETWORK_IPV4;
                    n_log(LOG_NOTICE, "IPV4 selected");
                } else if (!strcmp("v6", optarg)) {
                    (*ip_version) = NETWORK_IPV6;
                    n_log(LOG_NOTICE, "IPV6 selected");
                } else {
                    n_log(LOG_NOTICE, "IPV4/6 selected");
                }
                break;
            case 'v':
                fprintf(stderr, "Date de compilation : %s a %s.\n", __DATE__, __TIME__);
                exit(1);
            case 'V':
                if (!strncmp("LOG_NULL", optarg, 8)) {
                    log_level = LOG_NULL;
                } else {
                    if (!strncmp("LOG_NOTICE", optarg, 10)) {
                        log_level = LOG_NOTICE;
                    } else {
                        if (!strncmp("LOG_INFO", optarg, 8)) {
                            log_level = LOG_INFO;
                        } else {
                            if (!strncmp("LOG_ERR", optarg, 7)) {
                                log_level = LOG_ERR;
                            } else {
                                if (!strncmp("LOG_DEBUG", optarg, 9)) {
                                    log_level = LOG_DEBUG;
                                } else {
                                    fprintf(stderr, "%s n'est pas un niveau de log valide.\n", optarg);
                                    exit(-1);
                                }
                            }
                        }
                    }
                }
                break;
            case 'u':
                (*udp_mode) = 1;
                break;
            case 's':
                (*server) = strdup(optarg);
                break;
            case 'a':
                (*address) = strdup(optarg);
                break;
            case 'n':
                (*nb) = atoi(optarg);
                break;
            case 'p':
                (*port) = strdup(optarg);
                break;
            default:
            case '?': {
                usage();
                exit(1);
            }
            case 'h': {
                usage();
                exit(1);
            }
        }
    }
    set_log_level(log_level);
} /* void process_args( ... ) */

int main(int argc, char** argv) {
    char* addr = NULL;
    char* srv = NULL;
    char* port = NULL;

    set_log_level(LOG_DEBUG);

    /* processing args and set log_level */
    process_args(argc, argv, &addr, &srv, &port, &NB_ATTEMPTS, &ip_mode, &use_udp);

    if (!port) {
        n_log(LOG_ERR, "No port given. Exiting.");
        exit(-1);
    }

    if (srv && addr) {
        n_log(LOG_ERR, "Please specify only one of the following options: -a (server, addr to bind to) or -s (server on which to connect to)");
    }

    if (srv) {
        n_log(LOG_INFO, "Client mode, connecting to %s:%s", srv, port);
        mode = CLIENT;
    } else {
        n_log(LOG_INFO, "Server mode , waiting client on port %s", port);
        mode = SERVER;
    }

#ifdef __linux__
    if (sigchld_handler_installer() == FALSE) {
        exit(-1);
    }
#endif

    /* UDP mode */
    if (use_udp) {
        if (mode == SERVER) {
            n_log(LOG_INFO, "Creating UDP server on %s:%s", _str(addr), _str(port));
            if (netw_bind_udp(&netw_server, addr, port, ip_mode) == FALSE) {
                n_log(LOG_ERR, "Fatal error binding UDP socket");
                netw_unload();
                exit(-1);
            }
            n_log(LOG_NOTICE, "UDP server listening on %s:%s", _str(netw_server->link.ip), port);

            char buf[4096] = "";
            struct sockaddr_storage client_addr;
            socklen_t client_len = sizeof(client_addr);

            for (int it = 0; it < NB_ATTEMPTS; it++) {
                n_log(LOG_INFO, "UDP server: waiting for datagram %d/%d...", it + 1, NB_ATTEMPTS);
                memset(buf, 0, sizeof(buf));
                client_len = sizeof(client_addr);
                ssize_t received = netw_udp_recvfrom(netw_server, buf, sizeof(buf) - 1, (struct sockaddr*)&client_addr, &client_len);
                if (received > 0) {
                    buf[received] = '\0';

                    char client_ip[64] = "";
                    char client_port[16] = "";
                    getnameinfo((struct sockaddr*)&client_addr, client_len, client_ip, sizeof(client_ip), client_port, sizeof(client_port), NI_NUMERICHOST | NI_NUMERICSERV);
                    n_log(LOG_NOTICE, "UDP RECV from %s:%s: %s (%zd bytes)", client_ip, client_port, buf, received);

                    /* echo back */
                    ssize_t sent = netw_udp_sendto(netw_server, buf, (uint32_t)received, (struct sockaddr*)&client_addr, client_len);
                    if (sent > 0) {
                        n_log(LOG_NOTICE, "UDP SENT echo to %s: %zd bytes", client_ip, sent);
                    } else {
                        n_log(LOG_ERR, "UDP send error");
                    }
                } else {
                    n_log(LOG_ERR, "UDP recv error");
                }
            }
            netw_close(&netw_server);
        } else if (mode == CLIENT) {
            for (int it = 1; it <= NB_ATTEMPTS; it++) {
                n_log(LOG_INFO, "UDP client: connecting to %s:%s (attempt %d/%d)", srv, port, it, NB_ATTEMPTS);
                if (netw_connect_udp(&netw, srv, port, ip_mode) != TRUE) {
                    n_log(LOG_ERR, "Unable to connect UDP to %s:%s", srv, port);
                    netw_unload();
                    exit(1);
                }
                n_log(LOG_NOTICE, "UDP Attempt %d: Connected to %s:%s", it, srv, port);

                char send_buf[256] = "";
                snprintf(send_buf, sizeof(send_buf), "UDP Hello from client (attempt %d)", it);

                ssize_t sent = send_udp_data(netw, send_buf, (uint32_t)strlen(send_buf));
                if (sent > 0) {
                    n_log(LOG_NOTICE, "UDP SENT: %s (%zd bytes)", send_buf, sent);
                } else {
                    n_log(LOG_ERR, "UDP send error");
                    netw_close(&netw);
                    continue;
                }

                char recv_buf[4096] = "";
                ssize_t received = recv_udp_data(netw, recv_buf, sizeof(recv_buf) - 1);
                if (received > 0) {
                    recv_buf[received] = '\0';
                    n_log(LOG_NOTICE, "UDP RECV: %s (%zd bytes)", recv_buf, received);
                } else {
                    n_log(LOG_ERR, "UDP recv error or timeout");
                }
                netw_close(&netw);
            }
        }
    }
    /* TCP mode */
    else if (mode == SERVER) {
        n_log(LOG_INFO, "Creating listening network for %s:%s %d", _str(addr), _str(port), ip_mode);
        /* create listening network */
        if (netw_make_listening(&netw_server, addr, port, 10, ip_mode) == FALSE) {
            n_log(LOG_ERR, "Fatal error with network initialization");
            netw_unload();
            exit(-1);
        }

        int it = 0;
        for (it = 0; it < NB_ATTEMPTS / 2; it++) {
            n_log(LOG_INFO, "Blocking on accept...");
            /* get any accepted client on a network */
            if (!(netw = netw_accept_from(netw_server))) {
                n_log(LOG_ERR, "Error on accept");
            } else {
                /* someone is connected. starting some dialog */
                int error = 0;
                int pthread_error = 0;
                errno = 0;
                pthread_error = pthread_create(&netw_thr, NULL, &manage_client, (void*)netw);
                error = errno;
                if (pthread_error != 0) {
                    n_log(LOG_ERR, "Error creating client management pthread:%d , error: %s", pthread_error, strerror(error));
                    netw_unload();
                    exit(-1);
                }
                pthread_join(netw_thr, NULL);
            }
        }
        /* testing with thread pool  && non blocking */
        int error = 0;
        THREAD_POOL* thread_pool = new_thread_pool(2, 128);
        while (it < NB_ATTEMPTS) {
            /* get any accepted client on a network */
            if ((netw = netw_accept_from_ex(netw_server, 0, 0, 0, &error))) {
                /* someone is connected. starting some dialog */
                if (add_threaded_process(thread_pool, &manage_client, (void*)netw, DIRECT_PROC) == FALSE) {
                    n_log(LOG_ERR, "Error adding client management to thread pool");
                }
                it++;
            } else {
                n_log(LOG_DEBUG, "Waiting connections...");
                u_sleep(250000);
            }
            refresh_thread_pool(thread_pool);
        }
        n_log(LOG_NOTICE, "Waiting thread_pool...");
        wait_for_threaded_pool(thread_pool);
        n_log(LOG_NOTICE, "Destroying thread_pool...");
        destroy_threaded_pool(&thread_pool, 500000);
        n_log(LOG_NOTICE, "Setting a 2s wait close timeout to wait for last datas to be received by peer (optional)");
        netw_setsockopt(netw_server, NETWORK_WAIT_CLOSE_TIMEOUT, 2);
        netw_close(&netw_server);
    } else if (mode == CLIENT) {
        for (int it = 1; it <= NB_ATTEMPTS; it++) {
            if (netw_connect(&netw, srv, port, ip_mode) != TRUE) {
                /* there were some error when trying to connect */
                n_log(LOG_ERR, "Unable to connect to %s:%s", srv, port);
                netw_unload();
                exit(1);
            }
            n_log(LOG_NOTICE, "Attempt %d: Connected to %s:%s", it, srv, port);

            /* backgrounding network send / recv */
            netw_start_thr_engine(netw);

            N_STR *sended_data = NULL, *recved_data = NULL, *hostname = NULL, *tmpstr = NULL;

            sended_data = char_to_nstr("SENDING DATAS...");
            send_net_datas(netw, sended_data);

            free_nstr(&sended_data);

            /* let's check for an answer: test each 250000 usec, with
             * a limit of 1000000 usec */
            n_log(LOG_INFO, "waiting for datas back from server...");
            tmpstr = netw_wait_msg(netw, 25000, 10000000);

            if (tmpstr) {
                get_net_datas(tmpstr, &hostname, &recved_data);
                n_log(LOG_NOTICE, "RECEIVED DATAS: %s - %s", recved_data ? _nstr(recved_data) : "NULL", hostname ? _nstr(hostname) : "NULL");
                free_nstr(&tmpstr);
                free_nstr(&recved_data);
                free_nstr(&hostname);
            } else {
                n_log(LOG_ERR, "Error getting back answer from server");
            }
            n_log(LOG_NOTICE, "Closing client connection...");
            sleep(1);
            netw_close(&netw);
        }
    }

    FreeNoLog(srv);
    FreeNoLog(addr);
    FreeNoLog(port);

    netw_unload();

    n_log(LOG_INFO, "Exiting network example");

    exit(0);
} /* END_OF_MAIN() */