Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
ex_network.c
Go to the documentation of this file.
1/*
2 * Nilorea Library
3 * Copyright (C) 2005-2026 Castagnier Mickael
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14 * implied. See the License for the specific language governing
15 * permissions and limitations under the License.
16 *
17 * SPDX-License-Identifier: Apache-2.0
18 */
19
28#include <stdio.h>
29#include <errno.h>
30
31#include "ex_network.h"
32
33#define SERVER 0
34#define CLIENT 1
35
37
39 *netw = NULL;
42int use_udp = 0;
43
44static pthread_t netw_thr;
45
46void usage(void) {
47 fprintf(stderr,
48 " -a 'serveur address name/ip to bind (server mode) (optionnal)\n"
49 " -s 'serveur address name/ip to connect (client mode)\n"
50 " -p 'port': port to use, server or client\n"
51 " -u: use UDP transport instead of TCP\n"
52 " -i 'ipmode': optional, ip version to use. Default support both ipv4 and ipv6. Values: ipv4, ipv6\n"
53 " -n 'nb': optional, number of attempts (default: 3)\n"
54 " -h: show that help file\n"
55 " -v: show the program version\n"
56 " -V 'log level': optional, set the log level. Default: LOG_ERR, values: LOG_INFO, LOG_NOTICE, LOG_ERR, LOG_DEBUG\n");
57}
58
59void process_args(int argc, char** argv, char** address, char** server, char** port, int* nb, int* ip_version, int* udp_mode) {
60 int getoptret = 0,
61 log_level = LOG_ERR; /* default log level */
62
63 /* Arguments optionnels */
64 /* -v version
65 * -V log level
66 * -h help
67 * -a address name/ip to bind to (server mode, NULL if not specified)
68 * -s serveur address name/ip to connect (client mode)
69 * -p port
70 * -u udp mode
71 * -i v4 ip version (default support both ipv4 and ipv6 )
72 * -i v6 ip version ( " " " " " " )
73 */
74 if (argc == 1) {
75 fprintf(stderr, "No arguments given, help:\n");
76 usage();
77 exit(1);
78 }
79 while ((getoptret = getopt(argc, argv, "hvus:V:p:n:i:a:")) != EOF) {
80 switch (getoptret) {
81 case 'i':
82 if (!strcmp("v4", optarg)) {
83 (*ip_version) = NETWORK_IPV4;
84 n_log(LOG_NOTICE, "IPV4 selected");
85 } else if (!strcmp("v6", optarg)) {
86 (*ip_version) = NETWORK_IPV6;
87 n_log(LOG_NOTICE, "IPV6 selected");
88 } else {
89 n_log(LOG_NOTICE, "IPV4/6 selected");
90 }
91 break;
92 case 'v':
93 fprintf(stderr, "Date de compilation : %s a %s.\n", __DATE__, __TIME__);
94 exit(1);
95 case 'V':
96 if (!strncmp("LOG_NULL", optarg, 8)) {
98 } else {
99 if (!strncmp("LOG_NOTICE", optarg, 10)) {
101 } else {
102 if (!strncmp("LOG_INFO", optarg, 8)) {
104 } else {
105 if (!strncmp("LOG_ERR", optarg, 7)) {
107 } else {
108 if (!strncmp("LOG_DEBUG", optarg, 9)) {
110 } else {
111 fprintf(stderr, "%s n'est pas un niveau de log valide.\n", optarg);
112 exit(-1);
113 }
114 }
115 }
116 }
117 }
118 break;
119 case 'u':
120 (*udp_mode) = 1;
121 break;
122 case 's':
123 (*server) = strdup(optarg);
124 break;
125 case 'a':
126 (*address) = strdup(optarg);
127 break;
128 case 'n':
129 (*nb) = atoi(optarg);
130 break;
131 case 'p':
132 (*port) = strdup(optarg);
133 break;
134 default:
135 case '?': {
136 usage();
137 exit(1);
138 }
139 case 'h': {
140 usage();
141 exit(1);
142 }
143 }
144 }
146} /* void process_args( ... ) */
147
148int main(int argc, char** argv) {
149 char* addr = NULL;
150 char* srv = NULL;
151 char* port = NULL;
152
154
155 /* processing args and set log_level */
156 process_args(argc, argv, &addr, &srv, &port, &NB_ATTEMPTS, &ip_mode, &use_udp);
157
158 if (!port) {
159 n_log(LOG_ERR, "No port given. Exiting.");
160 exit(-1);
161 }
162
163 if (srv && addr) {
164 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)");
165 }
166
167 if (srv) {
168 n_log(LOG_INFO, "Client mode, connecting to %s:%s", srv, port);
169 mode = CLIENT;
170 } else {
171 n_log(LOG_INFO, "Server mode , waiting client on port %s", port);
172 mode = SERVER;
173 }
174
175#ifdef __linux__
176 if (sigchld_handler_installer() == FALSE) {
177 exit(-1);
178 }
179#endif
180
181 /* UDP mode */
182 if (use_udp) {
183 if (mode == SERVER) {
184 n_log(LOG_INFO, "Creating UDP server on %s:%s", _str(addr), _str(port));
185 if (netw_bind_udp(&netw_server, addr, port, ip_mode) == FALSE) {
186 n_log(LOG_ERR, "Fatal error binding UDP socket");
187 netw_unload();
188 exit(-1);
189 }
190 n_log(LOG_NOTICE, "UDP server listening on %s:%s", _str(netw_server->link.ip), port);
191
192 char buf[4096] = "";
193 struct sockaddr_storage client_addr;
194 socklen_t client_len = sizeof(client_addr);
195
196 for (int it = 0; it < NB_ATTEMPTS; it++) {
197 n_log(LOG_INFO, "UDP server: waiting for datagram %d/%d...", it + 1, NB_ATTEMPTS);
198 memset(buf, 0, sizeof(buf));
199 client_len = sizeof(client_addr);
200 ssize_t received = netw_udp_recvfrom(netw_server, buf, sizeof(buf) - 1, (struct sockaddr*)&client_addr, &client_len);
201 if (received > 0) {
202 buf[received] = '\0';
203
204 char client_ip[64] = "";
205 char client_port[16] = "";
206 getnameinfo((struct sockaddr*)&client_addr, client_len, client_ip, sizeof(client_ip), client_port, sizeof(client_port), NI_NUMERICHOST | NI_NUMERICSERV);
207 n_log(LOG_NOTICE, "UDP RECV from %s:%s: %s (%zd bytes)", client_ip, client_port, buf, received);
208
209 /* echo back */
210 ssize_t sent = netw_udp_sendto(netw_server, buf, (uint32_t)received, (struct sockaddr*)&client_addr, client_len);
211 if (sent > 0) {
212 n_log(LOG_NOTICE, "UDP SENT echo to %s: %zd bytes", client_ip, sent);
213 } else {
214 n_log(LOG_ERR, "UDP send error");
215 }
216 } else {
217 n_log(LOG_ERR, "UDP recv error");
218 }
219 }
221 } else if (mode == CLIENT) {
222 for (int it = 1; it <= NB_ATTEMPTS; it++) {
223 n_log(LOG_INFO, "UDP client: connecting to %s:%s (attempt %d/%d)", srv, port, it, NB_ATTEMPTS);
224 if (netw_connect_udp(&netw, srv, port, ip_mode) != TRUE) {
225 n_log(LOG_ERR, "Unable to connect UDP to %s:%s", srv, port);
226 netw_unload();
227 exit(1);
228 }
229 n_log(LOG_NOTICE, "UDP Attempt %d: Connected to %s:%s", it, srv, port);
230
231 char send_buf[256] = "";
232 snprintf(send_buf, sizeof(send_buf), "UDP Hello from client (attempt %d)", it);
233
234 ssize_t sent = send_udp_data(netw, send_buf, (uint32_t)strlen(send_buf));
235 if (sent > 0) {
236 n_log(LOG_NOTICE, "UDP SENT: %s (%zd bytes)", send_buf, sent);
237 } else {
238 n_log(LOG_ERR, "UDP send error");
240 continue;
241 }
242
243 char recv_buf[4096] = "";
244 ssize_t received = recv_udp_data(netw, recv_buf, sizeof(recv_buf) - 1);
245 if (received > 0) {
246 recv_buf[received] = '\0';
247 n_log(LOG_NOTICE, "UDP RECV: %s (%zd bytes)", recv_buf, received);
248 } else {
249 n_log(LOG_ERR, "UDP recv error or timeout");
250 }
252 }
253 }
254 }
255 /* TCP mode */
256 else if (mode == SERVER) {
257 n_log(LOG_INFO, "Creating listening network for %s:%s %d", _str(addr), _str(port), ip_mode);
258 /* create listening network */
259 if (netw_make_listening(&netw_server, addr, port, 10, ip_mode) == FALSE) {
260 n_log(LOG_ERR, "Fatal error with network initialization");
261 netw_unload();
262 exit(-1);
263 }
264
265 int it = 0;
266 for (it = 0; it < NB_ATTEMPTS / 2; it++) {
267 n_log(LOG_INFO, "Blocking on accept...");
268 /* get any accepted client on a network */
270 n_log(LOG_ERR, "Error on accept");
271 } else {
272 /* someone is connected. starting some dialog */
273 int error = 0;
274 int pthread_error = 0;
275 errno = 0;
276 pthread_error = pthread_create(&netw_thr, NULL, &manage_client, (void*)netw);
277 error = errno;
278 if (pthread_error != 0) {
279 n_log(LOG_ERR, "Error creating client management pthread:%d , error: %s", pthread_error, strerror(error));
280 netw_unload();
281 exit(-1);
282 }
283 pthread_join(netw_thr, NULL);
284 }
285 }
286 /* testing with thread pool && non blocking */
287 int error = 0;
289 while (it < NB_ATTEMPTS) {
290 /* get any accepted client on a network */
291 if ((netw = netw_accept_from_ex(netw_server, 0, 0, 0, &error))) {
292 /* someone is connected. starting some dialog */
294 n_log(LOG_ERR, "Error adding client management to thread pool");
295 }
296 it++;
297 } else {
298 n_log(LOG_DEBUG, "Waiting connections...");
299 u_sleep(250000);
300 }
302 }
303 n_log(LOG_NOTICE, "Waiting thread_pool...");
305 n_log(LOG_NOTICE, "Destroying thread_pool...");
307 n_log(LOG_NOTICE, "Setting a 2s wait close timeout to wait for last datas to be received by peer (optional)");
310 } else if (mode == CLIENT) {
311 for (int it = 1; it <= NB_ATTEMPTS; it++) {
312 if (netw_connect(&netw, srv, port, ip_mode) != TRUE) {
313 /* there were some error when trying to connect */
314 n_log(LOG_ERR, "Unable to connect to %s:%s", srv, port);
315 netw_unload();
316 exit(1);
317 }
318 n_log(LOG_NOTICE, "Attempt %d: Connected to %s:%s", it, srv, port);
319
320 /* backgrounding network send / recv */
322
323 N_STR *sended_data = NULL, *recved_data = NULL, *hostname = NULL, *tmpstr = NULL;
324
325 sended_data = char_to_nstr("SENDING DATAS...");
326 send_net_datas(netw, sended_data);
327
328 free_nstr(&sended_data);
329
330 /* let's check for an answer: test each 250000 usec, with
331 * a limit of 1000000 usec */
332 n_log(LOG_INFO, "waiting for datas back from server...");
333 tmpstr = netw_wait_msg(netw, 25000, 10000000);
334
335 if (tmpstr) {
336 get_net_datas(tmpstr, &hostname, &recved_data);
337 n_log(LOG_NOTICE, "RECEIVED DATAS: %s - %s", recved_data ? _nstr(recved_data) : "NULL", hostname ? _nstr(hostname) : "NULL");
338 free_nstr(&tmpstr);
339 free_nstr(&recved_data);
340 free_nstr(&hostname);
341 } else {
342 n_log(LOG_ERR, "Error getting back answer from server");
343 }
344 n_log(LOG_NOTICE, "Closing client connection...");
345 sleep(1);
347 }
348 }
349
350 FreeNoLog(srv);
353
354 netw_unload();
355
356 n_log(LOG_INFO, "Exiting network example");
357
358 exit(0);
359} /* END_OF_MAIN() */
static void usage(void)
int main(void)
void process_args(int argc, char **argv)
Definition ex_common.c:48
THREAD_POOL * thread_pool
Definition ex_fluid.c:76
int getoptret
Definition ex_fluid.c:59
int log_level
Definition ex_fluid.c:60
static NETWORK * netw_server
static int mode
#define SERVER
Definition ex_network.c:33
static pthread_t netw_thr
Definition ex_network.c:44
int NB_ATTEMPTS
Definition ex_network.c:36
int use_udp
Definition ex_network.c:42
NETWORK * netw
Network for server mode, accepting incomming.
Definition ex_network.c:39
int ip_mode
Definition ex_network.c:41
#define CLIENT
Definition ex_network.c:34
void * manage_client(void *ptr)
recv/send datas if any for the client
Definition ex_network.h:139
int get_net_datas(N_STR *str, N_STR **hostname, N_STR **data)
decode data we got from network
Definition ex_network.h:104
int send_net_datas(NETWORK *netw, N_STR *data)
send data to specified network
Definition ex_network.h:51
NETWORK * server
int ip_version
char * addr
char * port
#define FreeNoLog(__ptr)
Free Handler without log.
Definition n_common.h:272
#define _str(__PTR)
define true
Definition n_common.h:193
int sigchld_handler_installer()
install signal SIGCHLD handler to reap zombie processes
Definition n_common.c:267
#define _nstr(__PTR)
N_STR or "NULL" string for logging purposes.
Definition n_common.h:199
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition n_log.h:89
#define LOG_DEBUG
debug-level messages
Definition n_log.h:84
#define LOG_ERR
error conditions
Definition n_log.h:76
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL )
Definition n_log.c:121
#define LOG_NOTICE
normal but significant condition
Definition n_log.h:80
#define LOG_NULL
no log output
Definition n_log.h:46
#define LOG_INFO
informational
Definition n_log.h:82
#define free_nstr(__ptr)
free a N_STR structure and set the pointer to NULL
Definition n_str.h:203
N_STR * char_to_nstr(const char *src)
Convert a char into a N_STR, short version.
Definition n_str.c:255
A box including a string and his lenght.
Definition n_str.h:61
void u_sleep(unsigned int usec)
wrapper around usleep for API consistency
Definition n_time.c:54
char * ip
ip of the connected socket
Definition n_network.h:295
N_SOCKET link
networking socket
Definition n_network.h:388
int netw_bind_udp(NETWORK **netw, char *addr, char *port, int ip_version)
Create a UDP bound socket for receiving datagrams.
Definition n_network.c:3005
#define NETWORK_IPV6
Flag to force IPV6
Definition n_network.h:52
int netw_make_listening(NETWORK **netw, char *addr, char *port, int nbpending, int ip_version)
Make a NETWORK be a Listening network.
Definition n_network.c:2885
ssize_t send_udp_data(void *netw, char *buf, uint32_t n)
send data via UDP on a connected socket
Definition n_network.c:3218
int netw_start_thr_engine(NETWORK *netw)
Start the NETWORK netw Threaded Engine.
Definition n_network.c:3740
#define NETWORK_IPV4
Flag to force IPV4
Definition n_network.h:50
ssize_t netw_udp_sendto(NETWORK *netw, char *buf, uint32_t n, struct sockaddr *dest_addr, socklen_t dest_len)
send data via UDP to a specific destination address
Definition n_network.c:3295
NETWORK * netw_accept_from_ex(NETWORK *from, size_t send_list_limit, size_t recv_list_limit, int blocking, int *retval)
make a normal 'accept' .
Definition n_network.c:3358
#define NETWORK_IPALL
Flag for auto detection by OS of ip version to use.
Definition n_network.h:48
#define NETWORK_WAIT_CLOSE_TIMEOUT
Flag to set network closing wait timeout.
Definition n_network.h:64
int netw_setsockopt(NETWORK *netw, int optname, int value)
Modify common socket options on the given netw.
Definition n_network.c:967
ssize_t recv_udp_data(void *netw, char *buf, uint32_t n)
recv data via UDP from a connected socket
Definition n_network.c:3259
NETWORK * netw_accept_from(NETWORK *from)
make a normal blocking 'accept' .
Definition n_network.c:3544
int netw_close(NETWORK **netw)
Closing a specified Network, destroy queues, free the structure.
Definition n_network.c:2662
N_STR * netw_wait_msg(NETWORK *netw, unsigned int refresh, size_t timeout)
Wait a message from aimed NETWORK.
Definition n_network.c:3688
int netw_connect(NETWORK **netw, char *host, char *port, int ip_version)
Use this to connect a NETWORK to any listening one, unrestricted send/recv lists.
Definition n_network.c:2359
int netw_connect_udp(NETWORK **netw, char *host, char *port, int ip_version)
Connect a UDP socket to a remote host.
Definition n_network.c:3113
ssize_t netw_udp_recvfrom(NETWORK *netw, char *buf, uint32_t n, struct sockaddr *src_addr, socklen_t *src_len)
recv data via UDP and capture the source address
Definition n_network.c:3327
Structure of a NETWORK.
Definition n_network.h:309
THREAD_POOL * new_thread_pool(size_t nbmaxthr, size_t nb_max_waiting)
Create a new pool of nbmaxthr threads.
int add_threaded_process(THREAD_POOL *thread_pool, void *(*func_ptr)(void *param), void *param, int mode)
add a function and params to a thread pool
int refresh_thread_pool(THREAD_POOL *thread_pool)
try to add some waiting DIRECT_PROCs on some free thread slots, else do nothing
int wait_for_threaded_pool(THREAD_POOL *thread_pool)
Wait for the thread pool to become idle (no active threads, empty waiting list), blocking without pol...
int destroy_threaded_pool(THREAD_POOL **pool, unsigned int delay)
delete a thread_pool, exit the threads and free the structs
#define DIRECT_PROC
processing mode for added func, direct start, not queued
Structure of a thread pool.