-
Notifications
You must be signed in to change notification settings - Fork 4
/
uvx.h
359 lines (297 loc) · 15.1 KB
/
uvx.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
#ifndef __LIIGO_UVX_H__
#define __LIIGO_UVX_H__
#ifdef __cplusplus
extern "C" {
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <memory.h>
#include <uv.h>
#include "loge/loge.h"
#include "utils/automem.h"
//-----------------------------------------------
// uvx: a lightweight wrapper of libuv, defines `uvx_server_t`(TCP server),
// `uvx_client_t`(TCP client) and `uvx_udp_t`(UDP), along with an `uvx_log_t`(UDP logger).
//
// - To define a TCP server (which I call it xserver), you're just required to provide
// a `uvx_server_config_t` with some params and callbacks, then call `uvx_server_start`.
// - To define a TCP client (which I call it xclient), you're just required to provide
// a `uvx_client_config_t` with some params and callbacks, then call `uvx_client_connect`.
// - To define an UDP service (which I call it xudp), you're just required to provide
// a `uvx_udp_config_t` with some params and callbacks, then call `uvx_udp_start`.
// - To define a logging service (which I call it xlog), just call `uvx_log_init`.
//
// There are a few predefined callbacks, such as on_conn_ok, on_conn_close, on_heartbeat, etc.
// All callbacks are optional. Maybe `on_recv` is the most useful one you care about.
//
// by Liigo 2014-11.
// http://github.com/liigo/uvx
//-----------------------------------------------
// uvx tcp server: `uvx_server_t`
typedef struct uvx_server_s uvx_server_t;
typedef struct uvx_server_conn_s uvx_server_conn_t;
typedef void (*UVX_S_ON_CONN_OK) (uvx_server_t* xserver, uvx_server_conn_t* conn);
typedef void (*UVX_S_ON_CONN_FAIL) (uvx_server_t* xserver, uvx_server_conn_t* conn);
typedef void (*UVX_S_ON_CONN_CLOSING) (uvx_server_t* xserver, uvx_server_conn_t* conn);
typedef void (*UVX_S_ON_CONN_CLOSE) (uvx_server_t* xserver, uvx_server_conn_t* conn);
typedef void (*UVX_S_ON_ITER_CONN) (uvx_server_t* xserver, uvx_server_conn_t* conn, void* userdata);
typedef void (*UVX_S_ON_RECV) (uvx_server_t* xserver, uvx_server_conn_t* conn, void* data, ssize_t datalen);
typedef void (*UVX_S_ON_HEARTBEAT) (uvx_server_t* xserver, unsigned int index);
typedef struct uvx_server_config_s {
char name[32]; // the xserver's name (with-ending-'\0')
int conn_count; // estimated connections count
int conn_backlog; // used by uv_listen()
int conn_extra_size; // the bytes of extra data, see `uvx_server_conn_t.extra`
float conn_timeout_seconds; // if > 0, timeout-ed connections will be closed
float heartbeat_interval_seconds; // used by heartbeat timer
// callbacks
UVX_S_ON_CONN_OK on_conn_ok;
UVX_S_ON_CONN_FAIL on_conn_fail;
UVX_S_ON_CONN_CLOSING on_conn_closing;
UVX_S_ON_CONN_CLOSE on_conn_close;
UVX_S_ON_HEARTBEAT on_heartbeat;
UVX_S_ON_RECV on_recv;
// logs
FILE* log_out;
FILE* log_err;
} uvx_server_config_t;
struct uvx_server_s {
uv_loop_t* uvloop;
uv_tcp_t uvserver;
uvx_server_config_t config;
unsigned char privates[136]; // to store uvx_server_private_t
void* data; // for public use
};
typedef struct uvx_server_s uvx_server_t;
typedef struct uvx_server_conn_s {
uvx_server_t* xserver;
uv_tcp_t uvclient;
uint64_t last_comm_time; // time of last communication (uv_now(loop))
int refcount;
uv_mutex_t refmutex;
void* extra; // pointer to extra data, if config.conn_extra_size > 0, or else is NULL
// extra data resides here
} uvx_server_conn_t;
// returns the default config for xserver, used by uvx_server_start().
uvx_server_config_t uvx_server_default_config(uvx_server_t* xserver);
// start an xserver listening on ip:port, support IPv4 and IPv6.
// please pass in uninitialized xserver and initialized config.
// returns 1 on success, or 0 if fails.
int uvx_server_start(uvx_server_t* xserver, uv_loop_t* loop, const char* ip, int port, uvx_server_config_t config);
// shutdown the xserver normally.
// returns 1 on success, or 0 if fails.
int uvx_server_shutdown(uvx_server_t* xserver);
// iterate all connections if on_iter_conn != NULL.
// returns the number of connections.
int uvx_server_iter_conns(uvx_server_t* xserver, UVX_S_ON_ITER_CONN on_iter_conn, void* userdata);
// manager conn refcount manually, +1 or -1, free conn when refcount == 0. threadsafe.
void uvx_server_conn_ref(uvx_server_conn_t* conn, int ref);
// send data to tcp client (not only xclient) of the connection.
// don't use `data` any more, it will be `free`ed later.
// please make sure that `data` was `malloc`ed before, so that it can be `free`ed correctly.
// returns 1 on success, or 0 if fails.
int uvx_server_conn_send(uvx_server_conn_t* conn, void* data, unsigned int size);
//-----------------------------------------------
// uvx tcp client: `uvx_client_t`
typedef struct uvx_client_s uvx_client_t;
//TODO: combine UVX_C_ON_CONNECT_* to UVX_C_ON_CONNECT
typedef void (*UVX_C_ON_CONN_OK) (uvx_client_t* xclient);
typedef void (*UVX_C_ON_CONN_FAIL) (uvx_client_t* xclient);
typedef void (*UVX_C_ON_CONN_CLOSING) (uvx_client_t* xclient);
typedef void (*UVX_C_ON_CONN_CLOSE) (uvx_client_t* xclient);
typedef void (*UVX_C_ON_RECV) (uvx_client_t* xclient, void* data, ssize_t datalen);
typedef void (*UVX_C_ON_HEARTBEAT) (uvx_client_t* xclient, unsigned int index);
typedef struct uvx_client_config_s {
char name[32]; // the xclient's name (with-ending-'\0')
int auto_connect; // 1: on, 0: off
float heartbeat_interval_seconds;
// callbacks
UVX_C_ON_CONN_OK on_conn_ok;
UVX_C_ON_CONN_FAIL on_conn_fail;
UVX_C_ON_CONN_CLOSING on_conn_closing;
UVX_C_ON_CONN_CLOSE on_conn_close;
UVX_C_ON_RECV on_recv;
UVX_C_ON_HEARTBEAT on_heartbeat;
// logs
FILE* log_out;
FILE* log_err;
} uvx_client_config_t;
struct uvx_client_s {
uv_loop_t* uvloop;
uv_tcp_t uvclient;
uv_tcp_t* uvserver; // &uvclient or NULL
uvx_client_config_t config;
unsigned char privates[224]; // stores value of uvx_client_private_t
void* data;
};
typedef struct uvx_client_s uvx_client_t;
// returns the default config for xclient, used by uvx_client_connect().
uvx_client_config_t uvx_client_default_config(uvx_client_t* xclient);
// connect to an tcp server (not only xserver) that listening ip:port. support IPv4 and IPv6.
// please pass in uninitialized xclient and initialized config.
// returns 1 on success, or 0 if fails.
int uvx_client_connect(uvx_client_t* xclient, uv_loop_t* loop, const char* ip, int port, uvx_client_config_t config);
// send data to the connected tcp server (not only xserver).
// don't use `data` any more, it will be `free`ed later.
// please make sure that `data` was `malloc`ed before, so that it can be `free`ed correctly.
// if no server is connected, free data immediately, to avoid memory leak.
// returns 1 on success, or 0 if fails.
int uvx_client_send(uvx_client_t* xclient, void* data, unsigned int size);
// disconnect the current connection (and it will re-connect at next heartbeat timer).
// returns 1 on success, or 0 if fails.
int uvx_client_disconnect(uvx_client_t* xclient);
// shutdown the xclient normally.
// returns 1 on success, or 0 if fails.
int uvx_client_shutdown(uvx_client_t* xclient);
//-----------------------------------------------
// uvx udp: `uvx_udp_t`
typedef struct uvx_udp_s uvx_udp_t;
typedef void (*UVX_UDP_ON_RECV) (uvx_udp_t* xudp, void* data, ssize_t datalen, const struct sockaddr* addr, unsigned int flags);
typedef struct uvx_udp_config_s {
char name[32]; // the xudp's name (with-ending-'\0')
// callbacks
UVX_UDP_ON_RECV on_recv;
// logs
FILE* log_out;
FILE* log_err;
} uvx_udp_config_t;
struct uvx_udp_s {
uv_loop_t* uvloop;
uv_udp_t uvudp;
uvx_udp_config_t config;
void* data;
};
// returns the default config for xudp, used by uvx_udp_start().
uvx_udp_config_t uvx_udp_default_config(uvx_udp_t* xudp);
// start a udp service binding on ip and port. support IPv4 and IPv6.
// if ip == NULL, bind to local random port automatically.
// please pass in uninitialized xudp and initialized config.
// returns 1 on success, or 0 if fails.
int uvx_udp_start(uvx_udp_t* xudp, uv_loop_t* loop, const char* ip, int port, uvx_udp_config_t config);
// send data through udp. support IPv4 and IPv6.
// `data` is not limited to be `malloc`ed, unlike `uvx_server_send`/`uvx_client_send`.
// `data` is copy to internal buffer, so you can free it if needed after this call.
int uvx_udp_send_to_ip(uvx_udp_t* xudp, const char* ip, int port, const void* data, unsigned int datalen);
int uvx_udp_send_to_addr(uvx_udp_t* xudp, const struct sockaddr* addr, const void* data, unsigned int datalen);
// set broadcast on (1) or off (0)
// returns 1 on success, or 0 if fails.
int uvx_udp_set_broadcast(uvx_udp_t* xudp, int on);
// shutdown the xudp normally.
// returns 1 on success, or 0 if fails.
int uvx_udp_shutdown(uvx_udp_t* xudp);
//-----------------------------------------------
// uvx_log: `uvx_log_t`
typedef struct uvx_log_t {
uv_loop_t* uvloop;
uvx_udp_t xudp;
union {
struct sockaddr addr;
struct sockaddr_in addr4;
struct sockaddr_in6 addr6;
} target_addr;
loge_t loge;
} uvx_log_t;
// predefined log levels
#define UVX_LOG_ALL LOGE_LOG_ALL
#define UVX_LOG_TRACE LOGE_LOG_TRACE
#define UVX_LOG_DEBUG LOGE_LOG_DEBUG
#define UVX_LOG_INFO LOGE_LOG_INFO
#define UVX_LOG_WARN LOGE_LOG_WARN
#define UVX_LOG_ERROR LOGE_LOG_ERROR
#define UVX_LOG_FATAL LOGE_LOG_FATAL
#define UVX_LOG_NONE LOGE_LOG_NONE
// init an uvx log with uv loop, ip, port and name.
// please pass in uninitialized xlog. name can be NULL, default to "xlog".
// logs are sent to its target (target_ip:target_port) through IPv4/IPv6 + UDP.
// returns 1 on success, or 0 if fails.
int uvx_log_init(uvx_log_t* xlog, uv_loop_t* loop, const char* target_ip, int target_port, const char* name);
// send a log to target through UDP.
// level: see UVX_LOG_*; tags: comma separated text; msg: log content text.
// file and line: the source file path+name and line number.
// parameter tags/msg/file can be NULL, and may be truncated if too long.
// all text parameters should be utf-8 encoded, or utf-8 compatible.
// returns 1 on success, or 0 if fails.
// note: be limited by libuv, we should call `uvx_log_send` only in its main loop thread.
int uvx_log_send(uvx_log_t* xlog, int level, const char* tags, const char* msg, const char* file, int line);
// the binary version of `uvx_log_send`, `msg` is a binary data instead of a text.
// see `uvx_log_send` for more details.
int uvx_log_send_bin(uvx_log_t* xlog, int level, const char* tags,
const void* msg, unsigned int msglen, const char* file, int line);
// serialize a log into bytes stream, ready to be sent by `uvx_log_send_serialized` later.
// buf and bufsize: an output buffer and its size, recommend 1024, can be smaller or larger.
// the other parameters are as same as `uvx_log_send`.
// returns the serialized data size in bytes, which is ensure not exceed bufsize and 1024.
// maybe returns 0, which means nothing was serialized (e.g. when the log was disabled).
// example:
// char buf[1024];
// unsigned int len = uvx_log_serialize(xlog, buf, sizeof(buf), ...);
// uvx_log_send_serialized(xlog, buf, len);
unsigned int
uvx_log_serialize(uvx_log_t* xlog, void* buf, unsigned int bufsize,
int level, const char* tags, const char* msg, const char* file, int line);
// the binary version of `uvx_log_serialize`, `msg` is a binary data instead of a text.
// returns 0 if the msg's size is too long.
// see `uvx_log_serialize` for more details.
unsigned int
uvx_log_serialize_bin(uvx_log_t* xlog, void* buf, unsigned int bufsize, int level, const char* tags,
const void* msg, unsigned int msglen, const char* file, int line);
// send a serialized log to target through UDP.
// the parameter `data`/`datalen` must be serialized by `uvx_log_serialize[_bin]` before.
// returns 1 on success, or 0 if fails.
// note: be limited by libuv, we should call `uvx_log_send_serialized` only in its main loop thread.
int uvx_log_send_serialized(uvx_log_t* xlog, const void* data, unsigned int datalen);
// to enable (if enabled==1) or disable (if enabled==0) the log
void uvx_log_enable(uvx_log_t* xlog, int enabled);
// a printf-like UVX_LOG utility macro, to format and send a log.
// parameters:
// log: an uvx_log_t* which already uvx_log_start()-ed
// level: one of UVX_LOG_* consts
// tags: comma separated text
// msgfmt: the format text for msg, e.g. "something %s %d or else"
// ...: the values that match %* in msgfmt
// examples:
// UVX_LOG(&log, UVX_LOG_INFO, "uvx,liigo", "%d %s", 123, "liigo");
// UVX_LOG(&log, UVX_LOG_INFO, "uvx,liigo", "pure text without format", NULL);
#define UVX_LOG(xlog,level,tags,msgfmt,...) {\
char uvx_tmp_msg_[LOGE_MAXBUF]; /* avoid name conflict with outer-scope names */ \
snprintf(uvx_tmp_msg_, sizeof(uvx_tmp_msg_), msgfmt, __VA_ARGS__);\
uvx_log_send(xlog, level, tags, uvx_tmp_msg_, __FILE__, __LINE__);\
}
// only serialize a log, but not send it.
// `bufsize` will be rewrite to fill in the serialized size.
// example:
// char buf[1024]; unsigned int len = sizeof(buf);
// UVX_LOG_SERIALIZE(&xlog, buf, len, UVX_LOG_INFO, "author", "name: %s, sex: %d", "Liigo", 1);
// uvx_log_send_serialized(&xlog, buf, len);
#define UVX_LOG_SERIALIZE(xlog,buf,bufsize,level,tags,msgfmt,...) {\
char uvx_tmp_msg_[LOGE_MAXBUF]; /* avoid name conflict with outer-scope names */ \
snprintf(uvx_tmp_msg_, sizeof(uvx_tmp_msg_), msgfmt, __VA_ARGS__);\
bufsize = uvx_log_serialize(xlog, buf, bufsize, level, tags, uvx_tmp_msg_, __FILE__, __LINE__);\
}
//-----------------------------------------------
// other
// get text presentation ip address, writing to ipbuf and returns ipbuf. writing port if not NULL.
// support IPv4 (buflen = 16) and IPv6 (buflen = 40).
const char* uvx_get_ip_port(const struct sockaddr* addr, char* ipbuf, int buflen, int* port);
// get binary presentation ip address, writing to ipbuf and returns lengh in bytes of it. writing port if not NULL.
// support IPv4 (buflen = 4) and IPv6 (buflen = 16).
int uvx_get_raw_ip_port(const struct sockaddr* addr, unsigned char* ipbuf, int* port);
// get the tcp-client's text presentation ip address, writing to ipbuf and returns ipbuf. writing port if not NULL.
// support IPv4 (buflen = 16) and IPv6 (buflen = 40).
const char* uvx_get_tcp_ip_port(uv_tcp_t* uvclient, char* ipbuf, int buflen, int* port);
// send data to a libuv stream. don't use `data` any more, it will be `free`ed later.
// please make sure that `data` was `malloc`ed before, so that it can be `free`ed correctly.
// returns 1 on success, or 0 if fails.
int uvx_send_to_stream(uv_stream_t* stream, void* data, unsigned int size);
// deprecated, use uvx_send_to_stream() instead.
int uvx_send_mem(automem_t* mem, uv_stream_t* stream);
#if defined(_WIN32) && !defined(__GNUC__)
#include <stdarg.h>
// Emulate snprintf() on Windows, _snprintf() doesn't zero-terminate the buffer on overflow...
int snprintf(char* buf, size_t len, const char* fmt, ...);
#endif
#ifdef __cplusplus
} // extern "C"
#endif
#endif //__LIIGO_UVX_H__