Compare commits

...

107 Commits

Author SHA1 Message Date
yancey
17694ecaa9 fix more sanitizer complaint 2023-07-22 18:16:33 -04:00
yancey
c3debb0290 sync udp2raw bugfix 2023-07-22 14:45:53 -04:00
Yancey Wang
3ac5e6b40d
Update CMakeLists.txt 2023-06-04 21:45:31 -04:00
Yancey Wang
ca5444b4a7
Update ISSUE_TEMPLATE.md 2023-05-10 00:07:31 -04:00
yancey
61b24a3697 supress complier warning 2023-02-07 06:40:40 -05:00
yancey
41cac842a8 fix indent problem with clang-format 2023-02-07 05:34:06 -05:00
yancey
43dfb12d9e rework pr 2023-02-07 05:32:40 -05:00
Yancey Wang
dab380366d
Merge pull request #297 from wwbfred/branch_libev
Fix a bug mentioned in #290
2023-02-07 05:25:37 -05:00
yancey
a0c9cce148 add .clang-format 2023-02-07 05:03:49 -05:00
yancey
0775131ab5 update cmake 2023-02-07 04:54:42 -05:00
yancey
49b3b6ec11 add CMakeLists.txt 2023-02-06 07:59:37 -05:00
Fred Wu
c6437cccbb
Update misc.cpp 2022-07-02 16:03:34 +08:00
Fred Wu
0261c75c29
Update fec_manager.h 2022-07-02 16:02:30 +08:00
Fred Wu
89e7e294f2
Update misc.cpp
Fix a bug which resets all parameters to default value when writing a fec parameter to the fifo file.
2022-07-02 15:59:02 +08:00
Fred Wu
3b1b5d32d6
Clone a fec_parameter_t with fec changed only 2022-07-02 15:48:39 +08:00
Yancey Wang
fd72361dea
Update README.md 2021-12-09 09:26:53 -05:00
wangyu
3375c6ac9d refine huge packet log 2021-01-16 14:40:03 -05:00
wangyu
af607e4bfa add warning when packet is truncated 2021-01-16 14:08:12 -05:00
wangyu-
6dcc97155b
Update ISSUE_TEMPLATE.md 2020-09-20 05:16:31 -04:00
wangyu-
d607c5ff92
Update ISSUE_TEMPLATE.md 2020-09-20 05:13:01 -04:00
wangyu
13d5e40473 update makefile 2020-08-18 03:24:10 -04:00
wangyu
7d349251d0 update 2020-07-14 01:33:59 -04:00
wangyu
0c941e5dbd add target release2 2020-07-14 00:10:32 -04:00
wangyu
55606b63f9 refactor --bind and --interface, fix --key 2020-07-13 22:23:17 -04:00
wangyu-
5bb65af72c
Merge pull request #227 from Llorx/branch_libev
Bind to address
2020-07-13 21:12:14 -04:00
wangyu
fbe6c2a308 support cross compile for win and mac 2020-07-13 12:51:49 -04:00
llorx
b270199031 Error reporting 2019-11-05 00:04:02 +01:00
llorx
c6b0c3e5bc Small condition merge 2019-11-04 23:44:22 +01:00
llorx
939b347129 Bind to interface
Allow binding to interface instead of IP with SO_BINDTODEVICE.
2019-11-04 23:26:11 +01:00
llorx
79dccfd697 Bind to address
Allow binding to address before sending UDP packets.
2019-11-04 22:46:41 +01:00
root
ecc90928d3 some useless change 2019-04-08 07:48:11 -04:00
root
f7c2d15adc Merge branch 'branch_libev' of https://github.com/wangyu-/UDPspeeder into branch_libev 2019-04-08 07:31:18 -04:00
root
cacf0bcfcb fix clock goes backward, simpilify timer, hopefully it works 2019-04-08 07:31:13 -04:00
wangyu-
bf818a04a8 improved log for fec_done 2019-01-21 05:57:42 -06:00
wangyu-
f0a7b28a63 add fec_done in fec_group_t 2019-01-20 18:19:10 -06:00
wangyu-
8db00a87f4 update help page 2018-08-20 12:04:47 -05:00
wangyu-
9a622ce463 updates for tinyfec 2018-08-20 07:58:32 -05:00
wangyu-
e7153d101d fix last commit 2018-08-18 11:52:26 -05:00
wangyu-
16bb483eba option --debug-fec-enc and --debug-fec-dec 2018-08-18 11:45:30 -05:00
wangyu-
f881374eff improve debug log 2018-08-09 07:23:17 -05:00
wangyu-
7c1285fa15 fixed typo 2018-08-09 07:15:16 -05:00
wangyu-
7ab59c754b fix last commit 2018-08-08 13:12:14 -05:00
wangyu-
ee0392ed03 new option disable-checksum 2018-08-08 13:11:37 -05:00
wangyu-
bc45ae23b8 added OPT into makefile 2018-08-08 04:05:42 -05:00
wangyu-
c82ff54e4d fixed a dup log, and other trival changes 2018-08-06 20:57:35 -05:00
wangyu-
4b2865cf2e trival 2018-08-06 19:04:23 -05:00
wangyu-
34d2ad19d2 add a log for debuging 2018-08-06 01:36:26 -05:00
wangyu-
1f946ab705 fixed default value 2018-08-05 23:55:15 -05:00
wangyu-
f1334aed81 fixed a log 2018-08-05 22:34:37 -05:00
wangyu-
8d1ab91cc3 improve fine-grained fec 2018-08-05 22:30:22 -05:00
wangyu-
3e248b414c fix last commit 2018-08-05 14:02:28 -05:00
wangyu-
25da3a2b03 better tuneable --fec works 2018-08-05 12:55:07 -05:00
wangyu-
489805d90e prepare for better tuneable fec parameter 2018-08-05 10:51:33 -05:00
wangyu-
000fa673ed fixed bug of server_clear_function called at client side 2018-08-04 10:57:08 -05:00
wangyu-
513bed7382 changed struct to c++11 unresticted union 2018-07-28 03:48:57 -05:00
wangyu-
7e8d02dac1 fixed a log 2018-07-28 00:40:36 -05:00
wangyu-
d4564b6249 fixed ipv6 in -r 2018-07-25 05:10:16 -05:00
wangyu-
4d4a9673e1 fixed clear_function 2018-07-25 05:01:47 -05:00
wangyu-
461035828a now it compiles 2018-07-25 04:51:31 -05:00
wangyu-
9c1a8c859d prepare for ipv6 2018-07-25 03:39:48 -05:00
U-DESKTOP-T772REH\wangyu
649a55e51a fix typo 2018-07-05 23:10:34 +08:00
root
0c19a03cb4 small tuning 2018-07-05 08:09:33 +00:00
root
b00d8e6927 add expire time for anti_replay 2018-07-05 07:53:19 +00:00
Charlie Root
90edcfb59c add .gitattribute 2018-06-23 07:54:21 +00:00
Charlie Root
f736d78ec2 add freebsd target 2018-06-23 07:51:36 +00:00
wangyu
a6a3305874 fix compile error on mac 2018-06-20 03:27:33 -07:00
U-DESKTOP-T772REH\wangyu
4f1ff04faa suppress a log 2018-06-20 01:30:18 +08:00
U-DESKTOP-T772REH\wangyu
4b02fdb4bd fix close and errno, disable log color by default on mingw 2018-06-19 21:29:03 +08:00
U-DESKTOP-T772REH\wangyu
8c21ff55ab works under mingw, but not finish yet 2018-06-19 18:06:28 +08:00
Charlie Root
7e6cb5c7e6 fix compile error on freebsd 2018-06-15 05:45:08 +00:00
wangyu
4292123100 trival 2018-06-04 02:02:47 -07:00
wangyu
c5a52cdb6e fix warnings 2018-06-04 02:01:57 -07:00
wangyu-
74488ba47f fix warnings 2018-06-04 03:56:44 -05:00
wangyu-
a94c75d6b7 Merge branch 'branch_libev' of https://github.com/wangyu-/UDPspeeder into branch_libev 2018-06-04 03:00:47 -05:00
wangyu-
8827c8469c fix test, fix core 2018-06-04 02:59:08 -05:00
wangyu-
d355031613 trival 2018-06-04 00:39:06 -05:00
wangyu-
d4ee2d314b change random number generator 2018-06-04 00:36:23 -05:00
U-DESKTOP-T772REH\wangyu
2051e47399 port to cygwin 2018-06-04 11:52:33 +08:00
wangyu
36f55b7e22 fix itimerspec 2018-06-03 20:43:57 -07:00
wangyu
9fd6428525 Merge branch 'branch_libev' of https://github.com/wangyu-/UDPspeeder into branch_libev 2018-06-03 20:39:34 -07:00
wangyu
5b9da8dd43 port to mac 2018-06-03 20:39:31 -07:00
wangyu-
20799f78c2 add signal handler 2018-06-03 22:15:05 -05:00
wangyu-
5fcf83e61a server works 2018-06-03 12:06:47 -05:00
wangyu-
1c9c5f1d39 client works 2018-06-01 09:57:38 -05:00
wangyu-
d244eddcf9 remove useless comments 2018-06-01 03:05:19 -05:00
wangyu-
d2cedec091 before port to libev 2018-06-01 02:51:49 -05:00
wangyu-
d56e34bc22 fixed problem of --interval doesnt work 2018-05-21 22:28:38 -05:00
wangyu-
07ca7f2a35
Update README.zh-cn.md 2018-03-08 22:04:18 +08:00
wangyu-
27dbeaedcd
Update README.md 2018-02-26 08:09:01 +08:00
wangyu-
aa96ba6885
Update README.md 2018-02-26 08:02:53 +08:00
wangyu-
8d1e773688
Update README.md 2018-02-26 08:01:31 +08:00
wangyu-
2547462ce9
Update README.md 2018-02-22 04:13:17 +08:00
wangyu-
5321005028
Update README.zh-cn.md 2018-02-22 04:12:37 +08:00
wangyu-
03ebe71dde
Update README.md 2018-02-22 04:07:14 +08:00
wangyu-
a57f92da4b
Update README.zh-cn.md 2018-02-22 04:06:57 +08:00
wangyu-
19aed91978
Update README.md 2018-02-22 04:06:07 +08:00
wangyu-
8f97c7df70
Update README.md 2018-02-22 03:46:17 +08:00
wangyu-
a444132e24
Update README.zh-cn.md 2018-02-22 03:04:06 +08:00
wangyu-
5ecb56cd96
Update README.md 2018-02-19 18:08:45 +08:00
wangyu-
98191c5b36
Add files via upload 2018-02-19 15:46:54 +08:00
wangyu-
3a0acf0de2 improve format of readme.md 2017-12-14 10:24:50 -06:00
wangyu-
08b3fdeb9e
Add files via upload 2017-11-27 22:39:42 +08:00
wangyu-
f6475a5134 Update README.zh-cn.md 2017-11-26 23:03:19 -06:00
wangyu-
454696673d
Add files via upload 2017-11-27 02:17:28 +08:00
wangyu-
bed88233f6 moved a log 2017-11-26 03:55:14 -06:00
wangyu-
d2bb8f31f4 fixed a statistic error when --disable-fec is used 2017-11-26 02:35:27 -06:00
wangyu-
5839aebbf6 Update README.zh-cn.md 2017-11-25 08:12:53 -06:00
72 changed files with 26951 additions and 4540 deletions

4
.clang-format Normal file
View File

@ -0,0 +1,4 @@
SortIncludes: false
BasedOnStyle: Google
ColumnLimit: 0
IndentWidth: 4

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
libev/* linguist-vendored

35
CMakeLists.txt Normal file
View File

@ -0,0 +1,35 @@
#note: experimental
# currently only used for generating `compile_commands.json` for clangd
# to build this project, it's suggested to use `makefile` instead
cmake_minimum_required(VERSION 3.7)
project(speederv2)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_CXX_STANDARD 11)
set(SOURCE_FILES
main.cpp
log.cpp
common.cpp
lib/fec.cpp
lib/rs.cpp
packet.cpp
delay_manager.cpp
fd_manager.cpp
connection.cpp
fec_manager.cpp
misc.cpp
tunnel_client.cpp
tunnel_server.cpp
my_ev.cpp
)
set(CMAKE_CXX_FLAGS "-Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -Wno-missing-field-initializers -O2 -g -fsanitize=address,undefined")
#target_include_directories(speederv2 PRIVATE .)
#set(CMAKE_LINK_LIBRARY_FLAG "-lrt")
add_executable(speederv2 ${SOURCE_FILES})
target_link_libraries(speederv2 rt)
target_link_libraries(speederv2 pthread)
include_directories(SYSTEM "libev")
include_directories(".")

View File

@ -1,6 +1 @@
For English speaking user
https://github.com/wangyu-/UDPspeeder/wiki/Issue-Guide
中文用户请看:
https://github.com/wangyu-/UDPspeeder/wiki/发Issue前请看
(否则Issue可能被忽略或被直接关掉)
English Only.

View File

@ -1,8 +1,9 @@
# UDPspeeder
Network Speed-Up Tool. Boost your Connection on a High Lantency High Packet-Loss Link by using Forward Error Correction.
When used alone, UDPspeeder speeds-up only UDP connection. Nevertheless, if you used UDPspeeder + any UDP-based VPN together,
you can speed-up any traffic(include TCP/UDP/ICMP), currently OpenVPN/L2TP/ShadowVPN are confirmed to be supported。
A Tunnel which Improves your Network Quality on a High-latency Lossy Link by using Forward Error Correction.
When used alone, UDPspeeder improves only UDP connection. Nevertheless, if you used UDPspeeder + any UDP-based VPN together,
you can improve any traffic(include TCP/UDP/ICMP), currently OpenVPN/L2TP/ShadowVPN are confirmed to be supported。
![](/images/en/udpspeeder.PNG)
@ -12,21 +13,9 @@ or
Assume your local network to your server is lossy. Just establish a VPN connection to your server with UDPspeeder + any UDP-based VPN, access your server via this VPN connection, then your connection quality will be significantly improved. With well-tuned parameters , you can easily reduce IP or UDP/ICMP packet-loss-rate to less than 0.01% . Besides reducing packet-loss-rate, UDPspeeder can also significantly improve your TCP latency and TCP single-thread download speed.
[简体中文](/doc/README.zh-cn.md)(内容更丰富)
###### Note
You can use udp2raw with UDPspeeder together to get better speed on some ISP with UDP QoS(UDP throttling).
udp2raw's repo
https://github.com/wangyu-/udp2raw-tunnel
You can also try tinyFecVPN, a lightweight high-performance VPN with build-in FEC support:
tinyFecVPN's repo:
https://github.com/wangyu-/tinyFecVPN
[UDPspeeder Wiki](https://github.com/wangyu-/UDPspeeder/wiki)
[简体中文](/doc/README.zh-cn.md)
# Efficacy
tested on a link with 100ms latency and 10% packet loss at both direction
@ -67,7 +56,7 @@ Check wikipedia for more info, https://en.wikipedia.org/wiki/ReedSolomon_erro
### Installing
Download binary release from https://github.com/wangyu-/UDPspeeder/releases
### Running (speed-up UDP only)
### Running (improves UDP traffic only)
Assume your server ip is 44.55.66.77, you have a service listening on udp port 7777.
```bash
@ -86,11 +75,17 @@ Now connecting to UDP port 3333 at the client side is equivalent to connecting t
`-k` enables simple XOR encryption
# Improves all traffic with OpenVPN + UDPspeeder
See [UDPspeeder + openvpn config guide](https://github.com/wangyu-/UDPspeeder/wiki/UDPspeeder-openvpn-config-guide).
# Advanced Topic
### Full Options
```
UDPspeeder V2
git version: 6f55b8a2fc build date: Nov 19 2017 06:11:23
git version: 3e248b414c build date: Aug 5 2018 21:59:52
repository: https://github.com/wangyu-/UDPspeeder
usage:
@ -115,6 +110,8 @@ advanced options:
do not use if you dont know what it means.
-i,--interval <number> scatter each fec group to a interval of <number> ms, to protect burst packet loss.
default value: 0. do not use if you dont know what it means.
-f,--fec x1:y1,x2:y2,.. similiar to -f/--fec above,fine-grained fec parameters,may help save bandwidth.
example: "-f 1:3,2:4,10:6,20:10". check repo for details
--random-drop <number> simulate packet loss, unit: 0.01%. default value: 0.
--disable-obscure <number> disable obscure, to save a bit bandwidth and cpu.
developer options:
@ -145,6 +142,19 @@ echo timeout 5 > fifo.file
echo queue-len 100 > fifo.file
echo mode 0 > fifo.file
```
### Speed-Up any traffic with OpenVPN + UDPspeeder
Check [UDPspeeder + openvpn config guide](/doc/udpspeeder_openvpn.md).
# wiki
Check wiki for more info:
https://github.com/wangyu-/UDPspeeder/wiki
# Related repo
You can also try tinyfecVPN, a lightweight high-performance VPN with UDPspeeder's function built-in, repo:
https://github.com/wangyu-/tinyfecVPN
You can use udp2raw with UDPspeeder together to get better speed on some ISP with UDP QoS(UDP throttling), repo:
https://github.com/wangyu-/udp2raw-tunnel

1290
common.cpp

File diff suppressed because it is too large Load Diff

528
common.h
View File

@ -10,50 +10,56 @@
//#define __STDC_FORMAT_MACROS 1
#include <inttypes.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<getopt.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <getopt.h>
#include<unistd.h>
#include<errno.h>
#include <sys/epoll.h>
#include <sys/wait.h>
#include <sys/socket.h> //for socket ofcourse
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h> //for exit(0);
#include <errno.h> //For errno - the error number
#include <netinet/tcp.h> //Provides declarations for tcp header
#include <netinet/udp.h>
#include <netinet/ip.h> //Provides declarations for ip header
#include <netinet/if_ether.h>
#include <arpa/inet.h>
#include <stdlib.h> //for exit(0);
#include <errno.h> //For errno - the error number
//#include <netinet/tcp.h> //Provides declarations for tcp header
//#include <netinet/udp.h>
//#include <netinet/ip.h> //Provides declarations for ip header
//#include <netinet/if_ether.h>
#include <fcntl.h>
#include <byteswap.h>
#include <arpa/inet.h>
#include <linux/if_ether.h>
#include <linux/filter.h>
#include <sys/time.h>
#include <time.h>
#include <sys/timerfd.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
//#include <netinet/in.h>
//#include <net/if.h>
#include <stdarg.h>
#include <assert.h>
#include <linux/if_packet.h>
#include <linux/if_tun.h>
#include<unordered_map>
#include<unordered_set>
#include<map>
#include<list>
using namespace std;
#if !defined(NO_LIBEV_EMBED)
#include <my_ev.h>
#else
#include "ev.h"
#endif
#if defined(__MINGW32__)
#include <winsock2.h>
#include <ws2tcpip.h>
typedef int socklen_t;
#else
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#endif
typedef unsigned long long u64_t; //this works on most platform,avoid using the PRId64
#include <unordered_map>
#include <unordered_set>
#include <map>
#include <list>
#include <string>
#include <vector>
using namespace std;
typedef unsigned long long u64_t; // this works on most platform,avoid using the PRId64
typedef long long i64_t;
typedef unsigned int u32_t;
@ -62,18 +68,48 @@ typedef int i32_t;
typedef unsigned short u16_t;
typedef short i16_t;
#if defined(__MINGW32__)
int inet_pton(int af, const char *src, void *dst);
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
#define setsockopt(a, b, c, d, e) setsockopt(a, b, c, (const char *)(d), e)
#endif
char *get_sock_error();
int get_sock_errno();
int init_ws();
#if defined(__MINGW32__)
typedef SOCKET my_fd_t;
inline int sock_close(my_fd_t fd) {
return closesocket(fd);
}
#else
typedef int my_fd_t;
inline int sock_close(my_fd_t fd) {
return close(fd);
}
#endif
struct my_itimerspec {
struct timespec it_interval; /* Timer interval */
struct timespec it_value; /* Initial expiration */
};
typedef u64_t my_time_t;
const int max_data_len=2200;
const int buf_len=max_data_len+200;
const int max_addr_len = 100;
const int max_data_len = 3600;
const int buf_len = max_data_len + 200;
const int default_mtu = 1250;
//const u32_t timer_interval=400;
// const u32_t timer_interval=400;
////const u32_t conv_timeout=180000;
//const u32_t conv_timeout=40000;//for test
const u32_t conv_timeout=180000;
const int max_conv_num=10000;
const int max_conn_num=200;
// const u32_t conv_timeout=40000;//for test
const u32_t conv_timeout = 180000;
const int max_conv_num = 10000;
const int max_conn_num = 200;
/*
const u32_t max_handshake_conn_num=10000;
@ -86,46 +122,50 @@ const u32_t client_retry_interval=1000;
const u32_t server_handshake_timeout=10000;// this should be much longer than clients. client retry initially ,server retry passtively*/
const int conv_clear_ratio=30; //conv grabage collecter check 1/30 of all conv one time
const int conn_clear_ratio=50;
const int conv_clear_min=1;
const int conn_clear_min=1;
const int conv_clear_ratio = 30; // conv grabage collecter check 1/30 of all conv one time
const int conn_clear_ratio = 50;
const int conv_clear_min = 1;
const int conn_clear_min = 1;
const u32_t conv_clear_interval=1000;
const u32_t conn_clear_interval=1000;
const u32_t conv_clear_interval = 1000;
const u32_t conn_clear_interval = 1000;
const i32_t max_fail_time = 0; // disable
const i32_t max_fail_time=0;//disable
const u32_t heartbeat_interval = 1000;
const u32_t heartbeat_interval=1000;
const u32_t timer_interval = 400; // this should be smaller than heartbeat_interval and retry interval;
const u32_t timer_interval=400;//this should be smaller than heartbeat_interval and retry interval;
// const uint32_t conv_timeout=120000; //120 second
// const u32_t conv_timeout=120000; //for test
//const uint32_t conv_timeout=120000; //120 second
//const u32_t conv_timeout=120000; //for test
const u32_t client_conn_timeout=10000;
const u32_t client_conn_uplink_timeout=client_conn_timeout+2000;
//const uint32_t server_conn_timeout=conv_timeout+60000;//this should be 60s+ longer than conv_timeout,so that conv_manager can destruct convs gradually,to avoid latency glicth
const u32_t server_conn_timeout=conv_timeout+20000;//for test
const u32_t client_conn_timeout = 10000;
const u32_t client_conn_uplink_timeout = client_conn_timeout + 2000;
// const uint32_t server_conn_timeout=conv_timeout+60000;//this should be 60s+ longer than conv_timeout,so that conv_manager can destruct convs gradually,to avoid latency glicth
const u32_t server_conn_timeout = conv_timeout + 20000; // for test
extern int about_to_exit;
enum raw_mode_t{mode_faketcp=0,mode_udp,mode_icmp,mode_end};
enum raw_mode_t { mode_faketcp = 0,
mode_udp,
mode_icmp,
mode_end };
extern raw_mode_t raw_mode;
enum program_mode_t {unset_mode=0,client_mode,server_mode};
extern program_mode_t client_or_server;
extern unordered_map<int, const char*> raw_mode_tostring ;
enum program_mode_t { unset_mode = 0,
client_mode,
server_mode };
extern program_mode_t program_mode;
extern unordered_map<int, const char *> raw_mode_tostring;
enum working_mode_t {unset_working_mode=0,tunnel_mode,tun_dev_mode};
enum working_mode_t { unset_working_mode = 0,
tunnel_mode,
tun_dev_mode };
extern working_mode_t working_mode;
extern int socket_buf_size;
typedef u32_t id_t;
// typedef u32_t id_t;
typedef u64_t iv_t;
@ -135,113 +175,355 @@ typedef u64_t anti_replay_seq_t;
typedef u64_t fd64_t;
//enum dest_type{none=0,type_fd64_ip_port,type_fd64,type_fd64_ip_port_conv,type_fd64_conv/*,type_fd*/};
enum dest_type{none=0,type_fd64_ip_port,type_fd64,type_fd,type_write_fd,type_fd_ip_port/*,type_fd*/};
// enum dest_type{none=0,type_fd64_ip_port,type_fd64,type_fd64_ip_port_conv,type_fd64_conv/*,type_fd*/};
enum dest_type { none = 0,
type_fd64_addr,
type_fd64,
type_fd,
type_write_fd,
type_fd_addr /*,type_fd*/ };
/*
struct ip_port_t
{
u32_t ip;
int port;
void from_u64(u64_t u64);
u64_t to_u64();
char * to_s();
u32_t ip;
int port;
void from_u64(u64_t u64);
u64_t to_u64();
char * to_s();
};
struct fd64_ip_port_t
{
fd64_t fd64;
ip_port_t ip_port;
fd64_t fd64;
ip_port_t ip_port;
};
struct fd_ip_port_t
{
int fd;
ip_port_t ip_port;
};
union inner_t
{
fd64_t fd64;
int fd;
fd64_ip_port_t fd64_ip_port;
fd_ip_port_t fd_ip_port;
};
struct dest_t
{
dest_type type;
inner_t inner;
u32_t conv;
int cook=0;
};
struct fd_info_t
{
ip_port_t ip_port;
};
int fd;
ip_port_t ip_port;
};*/
struct pseudo_header {
u_int32_t source_address;
u_int32_t dest_address;
u_int8_t placeholder;
u_int8_t protocol;
u_int16_t tcp_length;
u32_t source_address;
u32_t dest_address;
unsigned char placeholder;
unsigned char protocol;
unsigned short tcp_length;
};
u32_t djb2(unsigned char *str, int len);
u32_t sdbm(unsigned char *str, int len);
struct address_t // TODO scope id
{
struct hash_function {
u32_t operator()(const address_t &key) const {
return sdbm((unsigned char *)&key.inner, sizeof(key.inner));
}
};
union storage_t // sockaddr_storage is too huge, we dont use it.
{
sockaddr_in ipv4;
sockaddr_in6 ipv6;
};
storage_t inner;
/*address_t()
{
clear();
}*/
void clear() {
memset(&inner, 0, sizeof(inner));
}
/*
int from_ip_port(u32_t ip, int port)
{
clear();
inner.ipv4.sin_family=AF_INET;
inner.ipv4.sin_port=htons(port);
inner.ipv4.sin_addr.s_addr=ip;
return 0;
}*/
int from_ip_port_new(int type, void *ip, int port) {
clear();
if (type == AF_INET) {
inner.ipv4.sin_family = AF_INET;
inner.ipv4.sin_port = htons(port);
inner.ipv4.sin_addr.s_addr = *((u32_t *)ip);
} else if (type == AF_INET6) {
inner.ipv6.sin6_family = AF_INET6;
inner.ipv6.sin6_port = htons(port);
inner.ipv6.sin6_addr = *((in6_addr *)ip);
}
return 0;
}
int from_str(char *str);
int from_str_ip_only(char *str);
int from_sockaddr(sockaddr *, socklen_t);
char *get_str();
void to_str(char *);
inline int is_vaild() {
u32_t ret = ((sockaddr *)&inner)->sa_family;
return (ret == AF_INET || ret == AF_INET6);
}
inline u32_t get_type() {
assert(is_vaild());
u32_t ret = ((sockaddr *)&inner)->sa_family;
return ret;
}
inline u32_t get_len() {
u32_t type = get_type();
switch (type) {
case AF_INET:
return sizeof(sockaddr_in);
case AF_INET6:
return sizeof(sockaddr_in6);
default:
assert(0 == 1);
}
return -1;
}
inline u32_t get_port() {
u32_t type = get_type();
switch (type) {
case AF_INET:
return ntohs(inner.ipv4.sin_port);
case AF_INET6:
return ntohs(inner.ipv6.sin6_port);
default:
assert(0 == 1);
}
return -1;
}
inline void set_port(int port) {
u32_t type = get_type();
switch (type) {
case AF_INET:
inner.ipv4.sin_port = htons(port);
break;
case AF_INET6:
inner.ipv6.sin6_port = htons(port);
break;
default:
assert(0 == 1);
}
return;
}
bool operator==(const address_t &b) const {
// return this->data==b.data;
return memcmp(&this->inner, &b.inner, sizeof(this->inner)) == 0;
}
int new_connected_udp_fd();
char *get_ip();
};
namespace std {
template <>
struct hash<address_t> {
std::size_t operator()(const address_t &key) const {
// return address_t::hash_function(k);
return sdbm((unsigned char *)&key.inner, sizeof(key.inner));
}
};
} // namespace std
struct fd64_addr_t {
fd64_t fd64;
address_t addr;
};
struct fd_addr_t {
int fd;
address_t addr;
};
union inner_t {
fd64_t fd64;
int fd;
fd64_addr_t fd64_addr;
fd_addr_t fd_addr;
};
struct dest_t {
dest_type type;
inner_t inner;
u32_t conv;
int cook = 0;
};
struct fd_info_t {
address_t addr;
ev_io io_watcher;
};
u64_t get_current_time();
// u64_t get_current_time_rough();
u64_t get_current_time_us();
u64_t pack_u64(u32_t a,u32_t b);
u64_t pack_u64(u32_t a, u32_t b);
u32_t get_u64_h(u64_t a);
u32_t get_u64_l(u64_t a);
void write_u16(char *,u16_t a);
void write_u16(char *, u16_t a);
u16_t read_u16(char *);
void write_u32(char *,u32_t a);
void write_u32(char *, u32_t a);
u32_t read_u32(char *);
void write_u64(char *,u64_t a);
void write_u64(char *, u64_t a);
u64_t read_uu64(char *);
char * my_ntoa(u32_t ip);
char *my_ntoa(u32_t ip);
void myexit(int a);
void init_random_number_fd();
u64_t get_true_random_number_64();
u32_t get_true_random_number();
u32_t get_true_random_number_nz();
u64_t get_fake_random_number_64();
u32_t get_fake_random_number();
u32_t get_fake_random_number_nz();
u64_t ntoh64(u64_t a);
u64_t hton64(u64_t a);
bool larger_than_u16(uint16_t a,uint16_t b);
bool larger_than_u32(u32_t a,u32_t b);
bool larger_than_u16(uint16_t a, uint16_t b);
bool larger_than_u32(u32_t a, u32_t b);
void setnonblocking(int sock);
int set_buf_size(int fd,int socket_buf_size,int force_socket_buf=0);
int set_buf_size(int fd, int socket_buf_size);
unsigned short csum(const unsigned short *ptr,int nbytes);
unsigned short tcp_csum(const pseudo_header & ph,const unsigned short *ptr,int nbytes);
unsigned short csum(const unsigned short *ptr, int nbytes);
unsigned short tcp_csum(const pseudo_header &ph, const unsigned short *ptr, int nbytes);
void signal_handler(int sig);
int numbers_to_char(id_t id1,id_t id2,id_t id3,char * &data,int &len);
int char_to_numbers(const char * data,int len,id_t &id1,id_t &id2,id_t &id3);
void signal_handler(int sig);
// int numbers_to_char(id_t id1,id_t id2,id_t id3,char * &data,int &len);
// int char_to_numbers(const char * data,int len,id_t &id1,id_t &id2,id_t &id3);
void myexit(int a);
int add_iptables_rule(char *);
int clear_iptables_rule();
void get_true_random_chars(char * s,int len);
int random_between(u32_t a,u32_t b);
void get_fake_random_chars(char *s, int len);
int random_between(u32_t a, u32_t b);
int set_timer_ms(int epollfd,int &timer_fd,u32_t timer_interval);
int set_timer_ms(int epollfd, int &timer_fd, u32_t timer_interval);
int round_up_div(int a,int b);
int round_up_div(int a, int b);
int create_fifo(char * file);
int create_fifo(char *file);
/*
int create_new_udp(int &new_udp_fd,int remote_address_uint32,int remote_port);
*/
int new_listen_socket(int &fd,u32_t ip,int port);
int new_listen_socket(int &fd, u32_t ip, int port);
int new_connected_socket(int &fd,u32_t ip,int port);
int new_connected_socket(int &fd, u32_t ip, int port);
int new_listen_socket2(int &fd, address_t &addr);
int new_connected_socket2(int &fd, address_t &addr, address_t *bind_addr, char *out_interface);
struct not_copy_able_t {
not_copy_able_t() {
}
not_copy_able_t(const not_copy_able_t &other) {
assert(0 == 1);
}
const not_copy_able_t &operator=(const not_copy_able_t &other) {
assert(0 == 1);
return other;
}
};
template <class key_t>
struct lru_collector_t : not_copy_able_t {
// typedef void* key_t;
//#define key_t void*
struct lru_pair_t {
key_t key;
my_time_t ts;
};
unordered_map<key_t, typename list<lru_pair_t>::iterator> mp;
list<lru_pair_t> q;
int update(key_t key) {
assert(mp.find(key) != mp.end());
auto it = mp[key];
q.erase(it);
my_time_t value = get_current_time();
if (!q.empty()) {
assert(value >= q.front().ts);
}
lru_pair_t tmp;
tmp.key = key;
tmp.ts = value;
q.push_front(tmp);
mp[key] = q.begin();
return 0;
}
int new_key(key_t key) {
assert(mp.find(key) == mp.end());
my_time_t value = get_current_time();
if (!q.empty()) {
assert(value >= q.front().ts);
}
lru_pair_t tmp;
tmp.key = key;
tmp.ts = value;
q.push_front(tmp);
mp[key] = q.begin();
return 0;
}
int size() {
return q.size();
}
int empty() {
return q.empty();
}
void clear() {
mp.clear();
q.clear();
}
my_time_t ts_of(key_t key) {
assert(mp.find(key) != mp.end());
return mp[key]->ts;
}
my_time_t peek_back(key_t &key) {
assert(!q.empty());
auto it = q.end();
it--;
key = it->key;
return it->ts;
}
void erase(key_t key) {
assert(mp.find(key) != mp.end());
q.erase(mp[key]);
mp.erase(key);
}
/*
void erase_back()
{
assert(!q.empty());
auto it=q.end(); it--;
key_t key=it->key;
erase(key);
}*/
};
vector<string> string_to_vec(const char *s, const char *sp);
#endif /* COMMON_H_ */

View File

@ -7,287 +7,124 @@
#include "connection.h"
const int disable_conv_clear=0;//a udp connection in the multiplexer is called conversation in this program,conv for short.
// const int disable_conv_clear=0;//a udp connection in the multiplexer is called conversation in this program,conv for short.
const int disable_conn_clear=0;//a raw connection is called conn.
const int disable_conn_clear = 0; // a raw connection is called conn.
int report_interval=0;
int report_interval = 0;
void server_clear_function(u64_t u64)//used in conv_manager in server mode.for server we have to use one udp fd for one conv(udp connection),
//so we have to close the fd when conv expires
void server_clear_function(u64_t u64) // used in conv_manager in server mode.for server we have to use one udp fd for one conv(udp connection),
// so we have to close the fd when conv expires
{
fd64_t fd64=u64;
assert(fd_manager.exist(fd64));
fd_manager.fd64_close(fd64);
fd64_t fd64 = u64;
assert(fd_manager.exist(fd64));
ev_io &watcher = fd_manager.get_info(fd64).io_watcher;
address_t &addr = fd_manager.get_info(fd64).addr; //
assert(conn_manager.exist(addr)); //
struct ev_loop *loop = conn_manager.find_insert(addr).loop; // overkill ? should we just use ev_default_loop(0)?
ev_io_stop(loop, &watcher);
fd_manager.fd64_close(fd64);
}
////////////////////////////////////////////////////////////////////
conv_manager_t::conv_manager_t()
{
clear_it=conv_last_active_time.begin();
long long last_clear_time=0;
reserve();
conn_manager_t::conn_manager_t() {
mp.reserve(10007);
last_clear_time = 0;
}
conv_manager_t::~conv_manager_t()
{
clear();
}
int conv_manager_t::get_size()
{
return conv_to_u64.size();
}
void conv_manager_t::reserve()
{
u64_to_conv.reserve(10007);
conv_to_u64.reserve(10007);
conv_last_active_time.reserve(10007);
}
void conv_manager_t::clear()
{
//if(disable_conv_clear) return ;//////what was the purpose of this code?
if(client_or_server==server_mode)
{
for(auto it=conv_to_u64.begin();it!=conv_to_u64.end();it++)
{
//int fd=int((it->second<<32u)>>32u);
server_clear_function( it->second);
}
}
u64_to_conv.clear();
conv_to_u64.clear();
conv_last_active_time.clear();
clear_it=conv_last_active_time.begin();
}
u32_t conv_manager_t::get_new_conv()
{
u32_t conv=get_true_random_number_nz();
while(conv_to_u64.find(conv)!=conv_to_u64.end())
{
conv=get_true_random_number_nz();
}
return conv;
}
int conv_manager_t::is_conv_used(u32_t conv)
{
return conv_to_u64.find(conv)!=conv_to_u64.end();
}
int conv_manager_t::is_u64_used(u64_t u64)
{
return u64_to_conv.find(u64)!=u64_to_conv.end();
}
u32_t conv_manager_t::find_conv_by_u64(u64_t u64)
{
assert(is_u64_used(u64));
return u64_to_conv[u64];
}
u64_t conv_manager_t::find_u64_by_conv(u32_t conv)
{
assert(is_conv_used(conv));
return conv_to_u64[conv];
}
int conv_manager_t::update_active_time(u32_t conv)
{
assert(is_conv_used(conv));
return conv_last_active_time[conv]=get_current_time();
}
int conv_manager_t::insert_conv(u32_t conv,u64_t u64)//////todo add capacity ///done at upper level
{
assert(!is_conv_used(conv));
int bucket_size_before=conv_last_active_time.bucket_count();
u64_to_conv[u64]=conv;
conv_to_u64[conv]=u64;
conv_last_active_time[conv]=get_current_time();
int bucket_size_after=conv_last_active_time.bucket_count();
if(bucket_size_after!=bucket_size_before)
clear_it=conv_last_active_time.begin();
return 0;
}
int conv_manager_t::erase_conv(u32_t conv)
{
//if(disable_conv_clear) return 0;
assert(conv_last_active_time.find(conv)!=conv_last_active_time.end());
u64_t u64=conv_to_u64[conv];
if(client_or_server==server_mode)
{
server_clear_function(u64);
}
assert(conv_to_u64.find(conv)!=conv_to_u64.end());
conv_to_u64.erase(conv);
u64_to_conv.erase(u64);
conv_last_active_time.erase(conv);
return 0;
}
int conv_manager_t::clear_inactive(char * ip_port)
{
if(get_current_time()-last_clear_time>conv_clear_interval)
{
last_clear_time=get_current_time();
return clear_inactive0(ip_port);
}
return 0;
}
int conv_manager_t::clear_inactive0(char * ip_port)
{
if(disable_conv_clear) return 0;
//map<uint32_t,uint64_t>::iterator it;
int cnt=0;
auto it=clear_it;
int size=conv_last_active_time.size();
int num_to_clean=size/conv_clear_ratio+conv_clear_min; //clear 1/10 each time,to avoid latency glitch
num_to_clean=min(num_to_clean,size);
u64_t current_time=get_current_time();
for(;;)
{
if(cnt>=num_to_clean) break;
if(conv_last_active_time.begin()==conv_last_active_time.end()) break;
if(it==conv_last_active_time.end())
{
it=conv_last_active_time.begin();
}
if( current_time -it->second >conv_timeout )
{
//mylog(log_info,"inactive conv %u cleared \n",it->first);
//auto old_it=it;
//it++;
u32_t conv= it->first;
it++;
erase_conv(conv);
if(ip_port==0)
{
mylog(log_info,"conv %x cleared\n",conv);
}
else
{
mylog(log_info,"[%s]conv %x cleared\n",ip_port,conv);
}
}
else
{
it++;
}
cnt++;
}
clear_it=it;
return 0;
int conn_manager_t::exist(address_t addr) {
if (mp.find(addr) != mp.end()) {
return 1;
}
return 0;
}
////////////////////////////////////////////////////////////////////
conn_manager_t::conn_manager_t()
conn_info_t *&conn_manager_t::find_insert_p(address_t addr) // be aware,the adress may change after rehash
{
//ready_num=0;
mp.reserve(10007);
//fd64_mp.reserve(100007);
clear_it=mp.begin();
last_clear_time=0;
// u64_t u64=0;
// u64=ip;
// u64<<=32u;
// u64|=port;
unordered_map<address_t, conn_info_t *>::iterator it = mp.find(addr);
if (it == mp.end()) {
mp[addr] = new conn_info_t;
// lru.new_key(addr);
} else {
// lru.update(addr);
}
return mp[addr];
}
int conn_manager_t::exist(ip_port_t ip_port)
conn_info_t &conn_manager_t::find_insert(address_t addr) // be aware,the adress may change after rehash
{
u64_t u64=ip_port.to_u64();
if(mp.find(u64)!=mp.end())
{
return 1;
}
return 0;
// u64_t u64=0;
// u64=ip;
// u64<<=32u;
// u64|=port;
unordered_map<address_t, conn_info_t *>::iterator it = mp.find(addr);
if (it == mp.end()) {
mp[addr] = new conn_info_t;
// lru.new_key(addr);
} else {
// lru.update(addr);
}
return *mp[addr];
}
conn_info_t *& conn_manager_t::find_p(ip_port_t ip_port) //todo capacity ///done at upper level
//be aware,the adress may change after rehash
{
assert(exist(ip_port));
u64_t u64=ip_port.to_u64();
return mp[u64];
}
conn_info_t & conn_manager_t::find(ip_port_t ip_port) //be aware,the adress may change after rehash
{
assert(exist(ip_port));
u64_t u64=ip_port.to_u64();
return *mp[u64];
}
int conn_manager_t::insert(ip_port_t ip_port)
{
assert(!exist(ip_port));
int bucket_size_before=mp.bucket_count();
mp[ip_port.to_u64()]=new conn_info_t;
int bucket_size_after=mp.bucket_count();
if(bucket_size_after!=bucket_size_before)
clear_it=mp.begin();
return 0;
}
int conn_manager_t::erase(unordered_map<u64_t,conn_info_t*>::iterator erase_it)
{
////////todo close and erase timer_fd ,check fd64 empty ///dont need
delete(erase_it->second);
mp.erase(erase_it->first);
return 0;
}
int conn_manager_t::clear_inactive()
{
if(get_current_time()-last_clear_time>conn_clear_interval)
{
last_clear_time=get_current_time();
return clear_inactive0();
}
return 0;
int conn_manager_t::erase(unordered_map<address_t, conn_info_t *>::iterator erase_it) {
delete (erase_it->second);
mp.erase(erase_it->first);
return 0;
}
int conn_manager_t::clear_inactive0()
{
//mylog(log_info,"called\n");
unordered_map<u64_t,conn_info_t*>::iterator it;
unordered_map<u64_t,conn_info_t*>::iterator old_it;
if(disable_conn_clear) return 0;
//map<uint32_t,uint64_t>::iterator it;
int cnt=0;
it=clear_it;//TODO,write it back
int size=mp.size();
int num_to_clean=size/conn_clear_ratio+conn_clear_min; //clear 1/10 each time,to avoid latency glitch
//mylog(log_trace,"mp.size() %d\n", size);
num_to_clean=min(num_to_clean,(int)mp.size());
u64_t current_time=get_current_time();
//mylog(log_info,"here size=%d\n",(int)mp.size());
for(;;)
{
if(cnt>=num_to_clean) break;
if(mp.begin()==mp.end()) break;
if(it==mp.end())
{
it=mp.begin();
}
if(it->second->conv_manager.get_size() >0)
{
//mylog(log_info,"[%s:%d]size %d \n",my_ntoa(get_u64_h(it->first)),get_u64_l(it->first),(int)it->second->conv_manager.get_size());
it++;
}
else if(current_time<it->second->last_active_time+server_conn_timeout)
{
it++;
}
else
{
mylog(log_info,"[%s:%d]inactive conn cleared \n",my_ntoa(get_u64_h(it->first)),get_u64_l(it->first));
old_it=it;
it++;
erase(old_it);
}
cnt++;
}
clear_it=it;
return 0;
int conn_manager_t::clear_inactive() {
if (get_current_time() - last_clear_time > conn_clear_interval) {
last_clear_time = get_current_time();
return clear_inactive0();
}
return 0;
}
int conn_manager_t::clear_inactive0() {
// mylog(log_info,"called\n");
unordered_map<address_t, conn_info_t *>::iterator it;
unordered_map<address_t, conn_info_t *>::iterator old_it;
if (disable_conn_clear) return 0;
// map<uint32_t,uint64_t>::iterator it;
int cnt = 0;
it = clear_it; // TODO,write it back
int size = mp.size();
int num_to_clean = size / conn_clear_ratio + conn_clear_min; // clear 1/10 each time,to avoid latency glitch
// mylog(log_trace,"mp.size() %d\n", size);
num_to_clean = min(num_to_clean, (int)mp.size());
u64_t current_time = get_current_time();
// mylog(log_info,"here size=%d\n",(int)mp.size());
for (;;) {
if (cnt >= num_to_clean) break;
if (mp.begin() == mp.end()) break;
if (it == mp.end()) {
it = mp.begin();
}
if (it->second->conv_manager.s.get_size() > 0) {
// mylog(log_info,"[%s:%d]size %d \n",my_ntoa(get_u64_h(it->first)),get_u64_l(it->first),(int)it->second->conv_manager.get_size());
it++;
} else if (current_time < it->second->last_active_time + server_conn_timeout) {
it++;
} else {
address_t tmp_addr = it->first; // avoid making get_str() const;
mylog(log_info, "{%s} inactive conn cleared \n", tmp_addr.get_str());
old_it = it;
it++;
erase(old_it);
}
cnt++;
}
clear_it = it;
return 0;
}

View File

@ -19,134 +19,307 @@ extern int disable_anti_replay;
extern int report_interval;
const int disable_conv_clear = 0;
void server_clear_function(u64_t u64);
template <class T>
struct conv_manager_t // manage the udp connections
{
//typedef hash_map map;
unordered_map<u64_t,u32_t> u64_to_conv; //conv and u64 are both supposed to be uniq
unordered_map<u32_t,u64_t> conv_to_u64;
unordered_map<u32_t,u64_t> conv_last_active_time;
// typedef hash_map map;
unordered_map<T, u32_t> data_to_conv; // conv and u64 are both supposed to be uniq
unordered_map<u32_t, T> conv_to_data;
unordered_map<u32_t,u64_t>::iterator clear_it;
lru_collector_t<u32_t> lru;
// unordered_map<u32_t,u64_t> conv_last_active_time;
//void (*clear_function)(uint64_t u64) ;
// unordered_map<u32_t,u64_t>::iterator clear_it;
long long last_clear_time;
void (*additional_clear_function)(T data) = 0;
conv_manager_t();
conv_manager_t(const conv_manager_t &b)
{
assert(0==1);
}
~conv_manager_t();
int get_size();
void reserve();
void clear();
u32_t get_new_conv();
int is_conv_used(u32_t conv);
int is_u64_used(u64_t u64);
u32_t find_conv_by_u64(u64_t u64);
u64_t find_u64_by_conv(u32_t conv);
int update_active_time(u32_t conv);
int insert_conv(u32_t conv,u64_t u64);
int erase_conv(u32_t conv);
int clear_inactive(char * ip_port=0);
int clear_inactive0(char * ip_port);
long long last_clear_time;
conv_manager_t() {
// clear_it=conv_last_active_time.begin();
long long last_clear_time = 0;
additional_clear_function = 0;
}
~conv_manager_t() {
clear();
}
int get_size() {
return conv_to_data.size();
}
void reserve() {
data_to_conv.reserve(10007);
conv_to_data.reserve(10007);
// conv_last_active_time.reserve(10007);
lru.mp.reserve(10007);
}
void clear() {
if (disable_conv_clear) return;
if (additional_clear_function != 0) {
for (auto it = conv_to_data.begin(); it != conv_to_data.end(); it++) {
// int fd=int((it->second<<32u)>>32u);
additional_clear_function(it->second);
}
}
data_to_conv.clear();
conv_to_data.clear();
lru.clear();
// conv_last_active_time.clear();
// clear_it=conv_last_active_time.begin();
}
u32_t get_new_conv() {
u32_t conv = get_fake_random_number_nz();
while (conv_to_data.find(conv) != conv_to_data.end()) {
conv = get_fake_random_number_nz();
}
return conv;
}
int is_conv_used(u32_t conv) {
return conv_to_data.find(conv) != conv_to_data.end();
}
int is_data_used(T data) {
return data_to_conv.find(data) != data_to_conv.end();
}
u32_t find_conv_by_data(T data) {
return data_to_conv[data];
}
T find_data_by_conv(u32_t conv) {
return conv_to_data[conv];
}
int update_active_time(u32_t conv) {
// return conv_last_active_time[conv]=get_current_time();
lru.update(conv);
return 0;
}
int insert_conv(u32_t conv, T data) {
data_to_conv[data] = conv;
conv_to_data[conv] = data;
// conv_last_active_time[conv]=get_current_time();
lru.new_key(conv);
return 0;
}
int erase_conv(u32_t conv) {
if (disable_conv_clear) return 0;
T data = conv_to_data[conv];
if (additional_clear_function != 0) {
additional_clear_function(data);
}
conv_to_data.erase(conv);
data_to_conv.erase(data);
// conv_last_active_time.erase(conv);
lru.erase(conv);
return 0;
}
int clear_inactive(char *info = 0) {
if (get_current_time() - last_clear_time > conv_clear_interval) {
last_clear_time = get_current_time();
return clear_inactive0(info);
}
return 0;
}
int clear_inactive0(char *info) {
if (disable_conv_clear) return 0;
unordered_map<u32_t, u64_t>::iterator it;
unordered_map<u32_t, u64_t>::iterator old_it;
// map<uint32_t,uint64_t>::iterator it;
int cnt = 0;
// it=clear_it;
int size = lru.size();
int num_to_clean = size / conv_clear_ratio + conv_clear_min; // clear 1/10 each time,to avoid latency glitch
num_to_clean = min(num_to_clean, size);
my_time_t current_time = get_current_time();
for (;;) {
if (cnt >= num_to_clean) break;
if (lru.empty()) break;
u32_t conv;
my_time_t ts = lru.peek_back(conv);
if (current_time - ts < conv_timeout) break;
erase_conv(conv);
if (info == 0) {
mylog(log_info, "conv %x cleared\n", conv);
} else {
mylog(log_info, "[%s]conv %x cleared\n", info, conv);
}
cnt++;
}
return 0;
}
/*
conv_manager_t();
~conv_manager_t();
int get_size();
void reserve();
void clear();
u32_t get_new_conv();
int is_conv_used(u32_t conv);
int is_u64_used(T u64);
u32_t find_conv_by_u64(T u64);
T find_u64_by_conv(u32_t conv);
int update_active_time(u32_t conv);
int insert_conv(u32_t conv,T u64);
int erase_conv(u32_t conv);
int clear_inactive(char * ip_port=0);
int clear_inactive0(char * ip_port);*/
}; // g_conv_manager;
struct inner_stat_t {
u64_t input_packet_num;
u64_t input_packet_size;
u64_t output_packet_num;
u64_t output_packet_size;
};
struct stat_t {
u64_t last_report_time;
inner_stat_t normal_to_fec;
inner_stat_t fec_to_normal;
stat_t() {
clear();
}
void clear(){
memset(this, 0, sizeof(stat_t));
}
void report_as_client() {
if (report_interval != 0 && get_current_time() - last_report_time > u64_t(report_interval) * 1000) {
last_report_time = get_current_time();
inner_stat_t &a = normal_to_fec;
inner_stat_t &b = fec_to_normal;
mylog(log_info, "[report]client-->server:(original:%llu pkt;%llu byte) (fec:%llu pkt,%llu byte) server-->client:(original:%llu pkt;%llu byte) (fec:%llu pkt;%llu byte)\n",
a.input_packet_num, a.input_packet_size, a.output_packet_num, a.output_packet_size,
b.output_packet_num, b.output_packet_size, b.input_packet_num, b.input_packet_size);
}
}
void report_as_server(address_t &addr) {
if (report_interval != 0 && get_current_time() - last_report_time > u64_t(report_interval) * 1000) {
last_report_time = get_current_time();
inner_stat_t &a = fec_to_normal;
inner_stat_t &b = normal_to_fec;
mylog(log_info, "[report][%s]client-->server:(original:%llu pkt;%llu byte) (fec:%llu pkt;%llu byte) server-->client:(original:%llu pkt;%llu byte) (fec:%llu pkt;%llu byte)\n",
addr.get_str(),
a.output_packet_num, a.output_packet_size, a.input_packet_num, a.input_packet_size,
b.input_packet_num, b.input_packet_size, b.output_packet_num, b.output_packet_size);
}
}
};
struct inner_stat_t
struct conn_info_t : not_copy_able_t // stores info for a raw connection.for client ,there is only one connection,for server there can be thousand of connection since server can
// handle multiple clients
{
u64_t input_packet_num;
u64_t input_packet_size;
u64_t output_packet_num;
u64_t output_packet_size;
};
struct stat_t
{
u64_t last_report_time;
inner_stat_t normal_to_fec;
inner_stat_t fec_to_normal;
stat_t()
{
memset(this,0,sizeof(stat_t));
}
void report_as_client()
{
if(report_interval!=0 &&get_current_time()-last_report_time>u64_t(report_interval)*1000)
{
last_report_time=get_current_time();
inner_stat_t &a=normal_to_fec;
inner_stat_t &b=fec_to_normal;
mylog(log_info,"[report]client-->server:(original:%llu pkt;%llu byte) (fec:%llu pkt,%llu byte) server-->client:(original:%llu pkt;%llu byte) (fec:%llu pkt;%llu byte)\n",
a.input_packet_num,a.input_packet_size,a.output_packet_num,a.output_packet_size,
b.output_packet_num,b.output_packet_size,b.input_packet_num,b.input_packet_size
);
}
}
void report_as_server(ip_port_t &ip_port)
{
if(report_interval!=0 &&get_current_time()-last_report_time>u64_t(report_interval)*1000)
{
last_report_time=get_current_time();
inner_stat_t &a=fec_to_normal;
inner_stat_t &b=normal_to_fec;
mylog(log_info,"[report][%s]client-->server:(original:%llu pkt;%llu byte) (fec:%llu pkt;%llu byte) server-->client:(original:%llu pkt;%llu byte) (fec:%llu pkt;%llu byte)\n",
ip_port.to_s(),
a.output_packet_num,a.output_packet_size,a.input_packet_num,a.input_packet_size,
b.input_packet_num,b.input_packet_size,b.output_packet_num,b.output_packet_size
);
}
}
};
union tmp_union_t {
conv_manager_t<address_t> c;
conv_manager_t<u64_t> s;
// avoid templates here and there, avoid pointer and type cast
tmp_union_t() {
if (program_mode == client_mode) {
new (&c) conv_manager_t<address_t>();
} else {
assert(program_mode == server_mode);
new (&s) conv_manager_t<u64_t>();
}
}
~tmp_union_t() {
if (program_mode == client_mode) {
c.~conv_manager_t<address_t>();
} else {
assert(program_mode == server_mode);
s.~conv_manager_t<u64_t>();
}
}
} conv_manager;
fec_encode_manager_t fec_encode_manager;
fec_decode_manager_t fec_decode_manager;
ev_timer timer;
// my_timer_t timer;
struct conn_info_t //stores info for a raw connection.for client ,there is only one connection,for server there can be thousand of connection since server can
//handle multiple clients
{
conv_manager_t conv_manager;
fec_encode_manager_t fec_encode_manager;
fec_decode_manager_t fec_decode_manager;
my_timer_t timer;
//ip_port_t ip_port;
u64_t last_active_time;
stat_t stat;
conn_info_t()
{
}
void update_active_time()
{
last_active_time=get_current_time();
}
conn_info_t(const conn_info_t &b)
{
assert(0==1);
}
u64_t last_active_time;
stat_t stat;
struct ev_loop *loop = 0;
int local_listen_fd;
int remote_fd; // only used for client
fd64_t remote_fd64; // only used for client
// ip_port_t ip_port;
address_t addr; // only used for server
conn_info_t() {
if (program_mode == server_mode) {
conv_manager.s.additional_clear_function = server_clear_function;
} else {
assert(program_mode == client_mode);
}
}
~conn_info_t() {
if (loop)
ev_timer_stop(loop, &timer);
}
void update_active_time() {
last_active_time = get_current_time();
}
/*
conn_info_t(const conn_info_t &b)
{
assert(0==1);
}*/
};
/*
struct conn_manager_t //manager for connections. for client,we dont need conn_manager since there is only one connection.for server we use one conn_manager for all connections
{
unordered_map<u64_t,conn_info_t*> mp;//<ip,port> to conn_info_t;
unordered_map<u64_t,conn_info_t*>::iterator clear_it;
long long last_clear_time;
unordered_map<u64_t,conn_info_t*> mp;//<ip,port> to conn_info_t;
unordered_map<u64_t,conn_info_t*>::iterator clear_it;
long long last_clear_time;
conn_manager_t();
conn_manager_t(const conn_info_t &b)
{
assert(0==1);
}
int exist(ip_port_t);
conn_info_t *& find_p(ip_port_t); //be aware,the adress may change after rehash
conn_info_t & find(ip_port_t) ; //be aware,the adress may change after rehash
int insert(ip_port_t);
conn_manager_t();
conn_manager_t(const conn_info_t &b)
{
assert(0==1);
}
int exist(ip_port_t);
conn_info_t *& find_p(ip_port_t); //be aware,the adress may change after rehash
conn_info_t & find(ip_port_t) ; //be aware,the adress may change after rehash
int insert(ip_port_t);
int erase(unordered_map<u64_t,conn_info_t*>::iterator erase_it);
int clear_inactive();
int clear_inactive0();
int erase(unordered_map<u64_t,conn_info_t*>::iterator erase_it);
int clear_inactive();
int clear_inactive0();
};*/
struct conn_manager_t // manager for connections. for client,we dont need conn_manager since there is only one connection.for server we use one conn_manager for all connections
{
unordered_map<address_t, conn_info_t *> mp; // put it at end so that it de-consturcts first
unordered_map<address_t, conn_info_t *>::iterator clear_it;
long long last_clear_time;
conn_manager_t();
int exist(address_t addr);
conn_info_t *&find_insert_p(address_t addr); // be aware,the adress may change after rehash //not true?
conn_info_t &find_insert(address_t addr); // be aware,the adress may change after rehash
int erase(unordered_map<address_t, conn_info_t *>::iterator erase_it);
int clear_inactive();
int clear_inactive0();
};
extern conn_manager_t conn_manager;
#endif /* CONNECTION_H_ */

View File

@ -8,116 +8,106 @@
#include "log.h"
#include "packet.h"
int delay_data_t::handle()
{
return my_send(dest,data,len)>=0;
int delay_data_t::handle() {
return my_send(dest, data, len) >= 0;
}
delay_manager_t::delay_manager_t() {
capacity = 0;
delay_manager_t::delay_manager_t()
{
capacity=0;
// if ((timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK)) < 0)
//{
// mylog(log_fatal,"timer_fd create error");
// myexit(1);
// }
if ((timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK)) < 0)
{
mylog(log_fatal,"timer_fd create error");
myexit(1);
}
itimerspec zero_its;
memset(&zero_its, 0, sizeof(zero_its));
timerfd_settime(timer_fd, TFD_TIMER_ABSTIME, &zero_its, 0);
// itimerspec zero_its;
// memset(&zero_its, 0, sizeof(zero_its));
// timerfd_settime(timer_fd, TFD_TIMER_ABSTIME, &zero_its, 0);
}
delay_manager_t::~delay_manager_t()
{
//TODO ,we currently dont need to deconstruct it
delay_manager_t::~delay_manager_t() {
// TODO ,we currently dont need to deconstruct it
}
/*
int delay_manager_t::get_timer_fd()
{
return timer_fd;
}
//int add(my_time_t delay,const dest_t &dest,const char *data,int len);
int delay_manager_t::add(my_time_t delay,const dest_t &dest,char *data,int len)
{
delay_data_t delay_data;
delay_data.dest=dest;
//delay_data.data=data;
delay_data.len=len;
return timer_fd;
}*/
if(capacity!=0&&int(delay_mp.size()) >=capacity)
{
mylog(log_warn,"max pending packet reached,ignored\n");
return -1;
}
if(delay==0)
{
static char buf[buf_len];
delay_data.data=buf;
memcpy(buf,data,len);
int ret=delay_data.handle();
if (ret != 0) {
mylog(log_trace, "handle() return %d\n", ret);
}
return 0;
}
// int add(my_time_t delay,const dest_t &dest,const char *data,int len);
int delay_manager_t::add(my_time_t delay, const dest_t &dest, char *data, int len) {
delay_data_t delay_data;
delay_data.dest = dest;
// delay_data.data=data;
delay_data.len = len;
delay_data_t tmp=delay_data;
tmp.data=(char *)malloc(delay_data.len+100);
if(!tmp.data)
{
if (capacity != 0 && int(delay_mp.size()) >= capacity) {
mylog(log_warn, "max pending packet reached,ignored\n");
return -1;
}
if (delay == 0) {
static char buf[buf_len];
delay_data.data = buf;
memcpy(buf, data, len);
int ret = delay_data.handle();
if (ret != 0) {
mylog(log_trace, "handle() return %d\n", ret);
}
return 0;
}
delay_data_t tmp = delay_data;
tmp.data = (char *)malloc(delay_data.len + 100);
if (!tmp.data) {
mylog(log_warn, "malloc() returned null in delay_manager_t::add()");
return -1;
}
memcpy(tmp.data,data,delay_data.len);
memcpy(tmp.data, data, delay_data.len);
my_time_t tmp_time=get_current_time_us();
tmp_time+=delay;
my_time_t tmp_time = get_current_time_us();
tmp_time += delay;
delay_mp.insert(make_pair(tmp_time,tmp));
delay_mp.insert(make_pair(tmp_time, tmp));
return 0;
////check(); check everytime when add, is it better ??
return 0;
}
int delay_manager_t::check()
{
if(!delay_mp.empty())
{
my_time_t current_time;
int delay_manager_t::check() {
if (!delay_mp.empty()) {
my_time_t current_time;
multimap<my_time_t,delay_data_t>::iterator it;
while(1)
{
int ret=0;
it=delay_mp.begin();
if(it==delay_mp.end()) break;
multimap<my_time_t, delay_data_t>::iterator it;
while (1) {
int ret = 0;
it = delay_mp.begin();
if (it == delay_mp.end()) break;
current_time=get_current_time_us();
if(it->first <= current_time)
{
ret=it->second.handle();
if (ret != 0) {
mylog(log_trace, "handle() return %d\n", ret);
}
free(it->second.data);
delay_mp.erase(it);
}
else
{
break;
}
}
if(!delay_mp.empty())
{
itimerspec its;
memset(&its.it_interval,0,sizeof(its.it_interval));
its.it_value.tv_sec=delay_mp.begin()->first/1000000llu;
its.it_value.tv_nsec=(delay_mp.begin()->first%1000000llu)*1000llu;
timerfd_settime(timer_fd,TFD_TIMER_ABSTIME,&its,0);
}
}
return 0;
current_time = get_current_time_us();
if (it->first <= current_time) {
ret = it->second.handle();
if (ret != 0) {
mylog(log_trace, "handle() return %d\n", ret);
}
free(it->second.data);
delay_mp.erase(it);
} else {
break;
}
}
if (!delay_mp.empty()) {
const double m = 1000 * 1000;
double timer_value = delay_mp.begin()->first / m - get_current_time_us() / m; // be aware of negative value, and be aware of uint
if (timer_value < 0) timer_value = 0; // set it to 0 if negative, although libev support negative value
ev_timer_stop(loop, &timer);
ev_timer_set(&timer, timer_value, 0);
ev_timer_start(loop, &timer);
} else {
ev_timer_stop(loop, &timer); // not necessary
}
}
return 0;
}

View File

@ -12,120 +12,130 @@
#include "packet.h"
#include "log.h"
//enum delay_type_t {none=0,enum_sendto_u64,enum_send_fd,client_to_local,client_to_remote,server_to_local,server_to_remote};
// enum delay_type_t {none=0,enum_sendto_u64,enum_send_fd,client_to_local,client_to_remote,server_to_local,server_to_remote};
/*
struct fd_ip_port_t
{
int fd;
u32_t ip;
u32_t port;
int fd;
u32_t ip;
u32_t port;
};
union dest_t
{
fd_ip_port_t fd_ip_port;
int fd;
u64_t u64;
fd_ip_port_t fd_ip_port;
int fd;
u64_t u64;
};
*/
/*
struct my_timer_t
{
int timer_fd;
fd64_t timer_fd64;
my_timer_t()
{
if ((timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK)) < 0)
{
mylog(log_fatal,"timer_fd create error");
myexit(1);
}
timer_fd64=fd_manager.create(timer_fd);
}
my_timer_t(const my_timer_t &b)
{
assert(0==1);
}
~my_timer_t()
{
fd_manager.fd64_close(timer_fd64);
}
int add_fd_to_epoll(int epoll_fd)
{
epoll_event ev;;
ev.events = EPOLLIN;
ev.data.u64 = timer_fd;
int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, timer_fd, &ev);
if (ret!= 0) {
mylog(log_fatal,"add delay_manager.get_timer_fd() error\n");
myexit(-1);
}
return 0;
}
int add_fd64_to_epoll(int epoll_fd)
{
epoll_event ev;;
ev.events = EPOLLIN;
ev.data.u64 = timer_fd64;
int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, timer_fd, &ev);
if (ret!= 0) {
mylog(log_fatal,"add delay_manager.get_timer_fd() error\n");
myexit(-1);
}
return 0;
}
int get_timer_fd()
{
return timer_fd;
}
fd64_t get_timer_fd64()
{
return timer_fd64;
}
int set_timer_repeat_us(my_time_t my_time)
{
itimerspec its;
memset(&its,0,sizeof(its));
its.it_interval.tv_sec=my_time/1000000llu;
its.it_interval.tv_nsec=my_time%1000000llu*1000llu;
its.it_value.tv_nsec=1; //imidiately
timerfd_settime(timer_fd,0,&its,0);
return 0;
}
int set_timer_abs_us(my_time_t my_time)
{
itimerspec its;
memset(&its,0,sizeof(its));
its.it_value.tv_sec=my_time/1000000llu;
its.it_value.tv_nsec=my_time%1000000llu*1000llu;
timerfd_settime(timer_fd,TFD_TIMER_ABSTIME,&its,0);
return 0;
}
};
struct delay_data_t
{
dest_t dest;
//int left_time;//
char * data;
int len;
int handle();
int timer_fd;
fd64_t timer_fd64;
my_timer_t()
{
if ((timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK)) < 0)
{
mylog(log_fatal,"timer_fd create error");
myexit(1);
}
timer_fd64=fd_manager.create(timer_fd);
}
my_timer_t(const my_timer_t &b)
{
assert(0==1);
}
~my_timer_t()
{
fd_manager.fd64_close(timer_fd64);
}
int add_fd_to_epoll(int epoll_fd)
{
epoll_event ev;;
ev.events = EPOLLIN;
ev.data.u64 = timer_fd;
int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, timer_fd, &ev);
if (ret!= 0) {
mylog(log_fatal,"add delay_manager.get_timer_fd() error\n");
myexit(-1);
}
return 0;
}
int add_fd64_to_epoll(int epoll_fd)
{
epoll_event ev;;
ev.events = EPOLLIN;
ev.data.u64 = timer_fd64;
int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, timer_fd, &ev);
if (ret!= 0) {
mylog(log_fatal,"add delay_manager.get_timer_fd() error\n");
myexit(-1);
}
return 0;
}
int get_timer_fd()
{
return timer_fd;
}
fd64_t get_timer_fd64()
{
return timer_fd64;
}
int set_timer_repeat_us(my_time_t my_time)
{
itimerspec its;
memset(&its,0,sizeof(its));
its.it_interval.tv_sec=my_time/1000000llu;
its.it_interval.tv_nsec=my_time%1000000llu*1000llu;
its.it_value.tv_nsec=1; //imidiately
timerfd_settime(timer_fd,0,&its,0);
return 0;
}
int set_timer_abs_us(my_time_t my_time)
{
itimerspec its;
memset(&its,0,sizeof(its));
its.it_value.tv_sec=my_time/1000000llu;
its.it_value.tv_nsec=my_time%1000000llu*1000llu;
timerfd_settime(timer_fd,TFD_TIMER_ABSTIME,&its,0);
return 0;
}
};*/
struct delay_data_t {
dest_t dest;
// int left_time;//
char *data;
int len;
int handle();
};
struct delay_manager_t
{
int timer_fd;
int capacity;
multimap<my_time_t,delay_data_t> delay_mp; //unit us,1 us=0.001ms
delay_manager_t();
delay_manager_t(delay_manager_t &b)
{
assert(0==1);
}
int set_capacity(int a){capacity=a;return 0;}
~delay_manager_t();
int get_timer_fd();
int check();
int add(my_time_t delay,const dest_t &dest,char *data,int len);
struct delay_manager_t {
ev_timer timer;
struct ev_loop *loop = 0;
void (*cb)(struct ev_loop *loop, struct ev_timer *watcher, int revents) = 0;
// int timer_fd;
int capacity;
multimap<my_time_t, delay_data_t> delay_mp; // unit us,1 us=0.001ms
delay_manager_t();
delay_manager_t(delay_manager_t &b) {
assert(0 == 1);
}
void set_loop_and_cb(struct ev_loop *loop, void (*cb)(struct ev_loop *loop, struct ev_timer *watcher, int revents)) {
this->loop = loop;
this->cb = cb;
ev_init(&timer, cb);
}
int set_capacity(int a) {
capacity = a;
return 0;
}
~delay_manager_t();
ev_timer &get_timer();
int check();
int add(my_time_t delay, const dest_t &dest, char *data, int len);
};
#endif /* DELAY_MANAGER_H_ */

View File

@ -11,23 +11,25 @@
![image0](/images/Capture2.PNG)
对于某些运营商UDPspeeder跟udp2raw配合可以达到更好的速度udp2raw负责把UDP伪装成TCP来绕过运营商的UDP限速。
[English](/README.md)
udp2raw的repo:
[UDPspeeder Wiki](https://github.com/wangyu-/UDPspeeder/wiki)
https://github.com/wangyu-/udp2raw-tunnel
##### 提示
如果你嫌UDPspeeder+OpenVPN麻烦你可以尝试tinyFecVPN一个集成了UDPspeeder功能的VPN
如果你嫌UDPspeeder+OpenVPN麻烦你可以尝试tinyfecVPN一个集成了UDPspeeder功能的VPN
tinyFecVPN的repo:
tinyfecVPN的repo:
https://github.com/wangyu-/tinyFecVPN
https://github.com/wangyu-/tinyfecVPN
#### 效果
![image0](/images/cn/ping_compare_cn.PNG)
![image0](/images/cn/scp_compare.PNG)
#### 原理简介
主要原理是通过冗余数据来对抗网络的丢包发送冗余数据的方式支持FEC(Forward Error Correction)和多倍发包其中FEC算法是Reed-Solomon。
@ -48,6 +50,10 @@ FEC方式的原理图:
client支持多个udp连接server也支持多个client
#### 关键词
UDP加速器、双边UDP加速、全流量加速、开源加速器、游戏加速、网游加速器
# 简明操作说明
### 环境要求
@ -77,7 +83,7 @@ https://github.com/wangyu-/UDPspeeder/releases
现在client和server之间建立起了tunnel。想要连接44.55.66.77:7777只需要连接 127.0.0.1:3333。来回的所有的udp流量会被加速。
##### 备注:
##### 备注
`-f20:10`表示对每20个原始数据发送10个冗余包。`-f20:10``-f 20:10`都是可以的空格可以省略对于所有的单字节option都是如此。对于双字节option例如后面会提到的`--mode 0`,空格不可以省略。
@ -85,16 +91,22 @@ https://github.com/wangyu-/UDPspeeder/releases
推荐使用`--mode 0`选项否则你可能需要考虑MTU问题。
##### 注意
这里推荐的参数是给日常/非游戏情况下使用的;玩游戏请用 [使用经验](https://github.com/wangyu-/UDPspeeder/wiki/使用经验) 里推荐的参数。
##### 提示
对于某些运营商UDPspeeder跟udp2raw配合可以达到更好的速度udp2raw负责把UDP伪装成TCP来绕过运营商的UDP限速。
udp2raw的repo:
https://github.com/wangyu-/udp2raw-tunnel
# 进阶操作说明
### 命令选项
```
UDPspeeder V2
git version: 6f55b8a2fc build date: Nov 19 2017 06:11:23
git version: 3e248b414c build date: Aug 5 2018 21:59:52
repository: https://github.com/wangyu-/UDPspeeder
usage:
@ -119,6 +131,8 @@ advanced options:
do not use if you dont know what it means.
-i,--interval <number> scatter each fec group to a interval of <number> ms, to protect burst packet loss.
default value: 0. do not use if you dont know what it means.
-f,--fec x1:y1,x2:y2,.. similiar to -f/--fec above,fine-grained fec parameters,may help save bandwidth.
example: "-f 1:3,2:4,10:6,20:10". check repo for details
--random-drop <number> simulate packet loss, unit: 0.01%. default value: 0.
--disable-obscure <number> disable obscure, to save a bit bandwidth and cpu.
developer options:
@ -139,8 +153,6 @@ log and help options:
--disable-color disable log color
-h,--help print this help message
```
### 包发送选项,两端设置可以不同。 只影响本地包发送。
##### `-f` 选项
@ -159,7 +171,7 @@ log and help options:
数据发送和接受报告。开启后可以根据此数据推测出包速和丢包率等特征。`--report 10`表示每10秒生成一次报告。
##### `-i` 选项
指定一个时间窗口长度为n毫秒。同一个fec分组的数据在发送时候会被均匀分散到这n毫秒中。可以对抗突发性的丢包。默认值是0因为这个功能需要用到时钟在某些虚拟机里时钟不稳定可能会导致个别包出现非常大的延迟所以默认关掉了。这个功能很有用默认参数效果不理想时可以尝试打开,比如用`-i 10`。这个选项的跟通信原理上常说的`交错fec` `交织fec`的原理是差不多的。
指定一个时间窗口长度为n毫秒。同一个fec分组的数据在发送时候会被均匀分散到这n毫秒中可以对抗突发性的丢包默认值是0(也就是不开启此功能)。 这个功能很有用,在推荐的参数效果不理想时可以尝试打开,比如用`-i 10`、`-i 20`。这个选项的跟通信原理上常说的`交错fec` `交织fec`的原理是差不多的。
##### `-j` 选项
为原始数据的发送增加一个延迟抖动值。这样上层应用计算出来的RTT方差会更大以等待后续冗余包的到达不至于发生在冗余包到达之前就触发重传的尴尬。配合-t选项使用。正常情况下跨国网络本身的延迟抖动就很大可以不用设-j。这个功能也需要时钟默认关掉了不过一般情况应该不需要这个功能。
@ -197,89 +209,22 @@ echo mode 0 > fifo.file
##### `--disable-obscure`
UDPspeeder默认情况下会对每个发出的数据包随机填充和异或一些字节(4~32字节)这样通过抓包难以发现你发了冗余数据防止VPS被封。这个功能只是为了小心谨慎即使你关掉这个功能基本上也没问题关掉可以省一些带宽和CPU。`--disable-obscure`可以关掉这个功能。
# 推荐参数
https://github.com/wangyu-/UDPspeeder/wiki/推荐设置
# 使用经验
https://github.com/wangyu-/UDPspeeder/wiki/使用经验
# 应用
#### UDPspeeder + OpenVPN加速任何流量也适用于其他VPN
![image0](/images/Capture2.PNG)
可以和BBR/锐速叠加不过BBR/锐速部署在VPS上只对本地和VPS间的流量有效对从本地和第三方服务器间的流量无效。
需要在服务端开启ipforward和NAT。在客户端改路由表可以手动修改也可以由OpenVPN的redirect-gateway选项自动加好
Linux具体配置: [UDPspeeder + openvpn config guide](/doc/udpspeeder_openvpn.md).
Windows具体配置: [win10系统UDPspeeder+OpenVPN的完整设置](https://github.com/wangyu-/UDPspeeder/wiki/win10系统UDPspeeder-OpenVPN的完整设置)
如果UDPspeeder + OpenVPN对你来说显得太麻烦了你可以尝试一下tinyFecVPN,一个集成了UDPspeeder功能的VPN:
https://github.com/wangyu-/tinyFecVPN/
#### UDPspeeder + kcptun/finalspeed + $*** 同时加速tcp和udp流量
如果你需要用加速的tcp看视频和下载文件这样效果可能比没有BBR的UDPspeeder+vpn方案更好。另外如果你需要玩游戏但是嫌配VPN麻烦也可以用这种方案。
![image0](/images/cn/speeder_kcptun.PNG)
具体配置方法简介:
假设$\*\*\* server监听在在44.55.66.77的443端口(tcp和udp同时)。用kcptun把tcp 443映射到本地的tcp 1234用UDPspeeder把udp 443的映射到本地的udp 1234。
然后让$\*\*\* client 去连127.0.0.1:1234就可以了tcp和udp都被加速了。完整命令
```
run at server side:
./kcp_server -l ":4000" -t "127.0.0.1:443" -mode fast2
./speederv2 -s -l0.0.0.0:4001 -r127.0.0.1:443 -f20:10 -k "passwd"
run at client side:
./kcp_client -l ":1234" -r "44.55.66.77:4000" -mode fast2
./speederv2 -c -l0.0.0.0:1234 -r44.55.66.77:4001 -f20:10 -k "passwd"
```
这就是全部的命令了。issue里有很多人困惑于怎么把tcp和udp流量"分开",其实很简单就可以做到。
如果只需要加速UDP不需要加速TCP可以把kcptun换成其他的任意端口转发方式比如ncat/socat/ssh tunnel/iptables/[tinyPortMapper](https://github.com/wangyu-/tinyPortMapper/releases)。
如果你没有kcptun只有BBR/锐速的话也可以把kcptun换成ncat/socat/ssh tunnel/iptables/[tinyPortMapper](https://github.com/wangyu-/tinyPortMapper/releases)。这样TCP流量由锐速/BBR加速UDP由UDPspeeder加速。
另外,即使你不想使用$\*\*\*的TCP功能你也必须把$\*\*\*的TCP端口转发过来否则无法使用UDP功能这是socks5协议的工作方式决定的。($\*\*\*-redir方式不受此限制)
#### UDPspeeder + openvpn + $*** 混合方案也适用于其他VPN
也是我正在用的方案。优点是可以随时在vpn和$\*\*\*方案间快速切换。
实际部署起来比图中看起来的还要简单。不需要改路由表不需要写iptables规则和开启NAT需要做的只是用openvpn分配的ip访问$*** server。
![image0](/images/cn/speeder_vpn_s.PNG)
(也可以把图中的$*** server换成其他的socks5 server这样就不需要$*** client了)
可以和BBR/锐速叠加BBR/锐速只要部署在VPS上就有效。
也可以用[tinyFecVPN](https://github.com/wangyu-/tinyFecVPN/) + $\*\*\* ,配置起来更简单。
# 应用实例
#### win10系统UDPspeeder+OpenVPN的完整设置
https://github.com/wangyu-/UDPspeeder/wiki/win10系统UDPspeeder-OpenVPN的完整设置
#### UDPspeeder+OpenVPN运行在linux上透明加速linux本机的网络
https://github.com/wangyu-/tinyFecVPN/wiki/tinyFecVPN运行在linux上透明加速linux本机的网络
#### UDPspeeder+OpenVPN运行在虚拟机中加速windows和局域网内其他主机的网络
https://github.com/wangyu-/tinyFecVPN/wiki/tinyFecVPN运行在虚拟机中加速windows和局域网内其他主机的网络
#### 用树莓派做路由器,搭建透明代理,加速游戏主机的网络
https://github.com/wangyu-/UDPspeeder/wiki/用树莓派做路由器,搭建透明代理,加速游戏主机的网络
# 编译教程
暂时先参考udp2raw的这篇教程几乎一样的过程。
https://github.com/wangyu-/udp2raw-tunnel/blob/master/doc/build_guide.zh-cn.md
# wiki
更多内容请看 wiki:
https://github.com/wangyu-/UDPspeeder/wiki

View File

@ -5,59 +5,48 @@
* Author: root
*/
#include "fd_manager.h"
int fd_manager_t::fd_exist(int fd)
{
return fd_to_fd64_mp.find(fd)!=fd_to_fd64_mp.end();
int fd_manager_t::fd_exist(int fd) {
return fd_to_fd64_mp.find(fd) != fd_to_fd64_mp.end();
}
int fd_manager_t::exist(fd64_t fd64)
{
return fd64_to_fd_mp.find(fd64)!=fd64_to_fd_mp.end();
int fd_manager_t::exist(fd64_t fd64) {
return fd64_to_fd_mp.find(fd64) != fd64_to_fd_mp.end();
}
int fd_manager_t::to_fd(fd64_t fd64)
{
assert(exist(fd64));
return fd64_to_fd_mp[fd64];
int fd_manager_t::to_fd(fd64_t fd64) {
assert(exist(fd64));
return fd64_to_fd_mp[fd64];
}
void fd_manager_t::fd64_close(fd64_t fd64)
{
assert(exist(fd64));
int fd=fd64_to_fd_mp[fd64];
fd64_to_fd_mp.erase(fd64);
fd_to_fd64_mp.erase(fd);
if(exist_info(fd64))
{
fd_info_mp.erase(fd64);
}
close(fd);
void fd_manager_t::fd64_close(fd64_t fd64) {
assert(exist(fd64));
int fd = fd64_to_fd_mp[fd64];
fd64_to_fd_mp.erase(fd64);
fd_to_fd64_mp.erase(fd);
if (exist_info(fd64)) {
fd_info_mp.erase(fd64);
}
sock_close(fd);
}
void fd_manager_t::reserve(int n)
{
fd_to_fd64_mp.reserve(n);
fd64_to_fd_mp.reserve(n);
fd_info_mp.reserve(n);
void fd_manager_t::reserve(int n) {
fd_to_fd64_mp.reserve(n);
fd64_to_fd_mp.reserve(n);
fd_info_mp.reserve(n);
}
u64_t fd_manager_t::create(int fd)
{
assert(!fd_exist(fd));
fd64_t fd64=counter++;
fd_to_fd64_mp[fd]=fd64;
fd64_to_fd_mp[fd64]=fd;
return fd64;
u64_t fd_manager_t::create(int fd) {
assert(!fd_exist(fd));
fd64_t fd64 = counter++;
fd_to_fd64_mp[fd] = fd64;
fd64_to_fd_mp[fd64] = fd;
return fd64;
}
fd_manager_t::fd_manager_t()
{
counter=u32_t(-1);
counter+=100;
reserve(10007);
fd_manager_t::fd_manager_t() {
counter = u32_t(-1);
counter += 100;
reserve(10007);
}
fd_info_t & fd_manager_t::get_info(fd64_t fd64)
{
assert(exist(fd64));
return fd_info_mp[fd64];
fd_info_t& fd_manager_t::get_info(fd64_t fd64) {
assert(exist(fd64));
return fd_info_mp[fd64];
}
int fd_manager_t::exist_info(fd64_t fd64)
{
return fd_info_mp.find(fd64)!=fd_info_mp.end();
int fd_manager_t::exist_info(fd64_t fd64) {
return fd_info_mp.find(fd64) != fd_info_mp.end();
}

View File

@ -11,27 +11,26 @@
#include "common.h"
#include "packet.h"
struct fd_manager_t //conver fd to a uniq 64bit number,avoid fd value conflict caused by close and re-create
//this class is not strictly necessary,it just makes epoll fd handling easier
struct fd_manager_t // conver fd to a uniq 64bit number,avoid fd value conflict caused by close and re-create
// this class is not strictly necessary,it just makes epoll fd handling easier
{
fd_info_t & get_info(fd64_t fd64);
int exist_info(fd64_t);
int exist(fd64_t fd64);
int to_fd(fd64_t);
void fd64_close(fd64_t fd64);
void reserve(int n);
u64_t create(int fd);
fd_manager_t();
private:
u64_t counter;
unordered_map<int,fd64_t> fd_to_fd64_mp;
unordered_map<fd64_t,int> fd64_to_fd_mp;
unordered_map<fd64_t,fd_info_t> fd_info_mp;
int fd_exist(int fd);
//void remove_fd(int fd);
//fd64_t fd_to_fd64(int fd);
fd_info_t& get_info(fd64_t fd64);
int exist_info(fd64_t);
int exist(fd64_t fd64);
int to_fd(fd64_t);
void fd64_close(fd64_t fd64);
void reserve(int n);
u64_t create(int fd);
fd_manager_t();
private:
u64_t counter;
unordered_map<int, fd64_t> fd_to_fd64_mp;
unordered_map<fd64_t, int> fd64_to_fd_mp;
unordered_map<fd64_t, fd_info_t> fd_info_mp;
int fd_exist(int fd);
// void remove_fd(int fd);
// fd64_t fd_to_fd64(int fd);
};
extern fd_manager_t fd_manager;

File diff suppressed because it is too large Load Diff

View File

@ -12,243 +12,426 @@
#include "log.h"
#include "lib/rs.h"
const int max_blob_packet_num=30000;//how many packet can be contain in a blob_t ,can be set very large
const u32_t anti_replay_buff_size=30000;//can be set very large
const int max_blob_packet_num = 30000; // how many packet can be contain in a blob_t ,can be set very large
const u32_t anti_replay_buff_size = 30000; // can be set very large
const int max_fec_packet_num=255;// this is the limitation of the rs lib
const int max_fec_packet_num = 255; // this is the limitation of the rs lib
extern u32_t fec_buff_num;
const int rs_str_len = max_fec_packet_num * 10 + 100;
extern int header_overhead;
extern int debug_fec_enc;
extern int debug_fec_dec;
/*begin for first time init or dynamic update*/
extern int g_fec_data_num;
extern int g_fec_redundant_num;
extern int g_fec_mtu;
extern int g_fec_queue_len;
extern int g_fec_timeout; //8ms
extern int g_fec_mode;
extern int dynamic_update_fec;
/*end for first time init or dynamic update*/
struct fec_parameter_t {
int version = 0;
int mtu = default_mtu;
int queue_len = 200;
int timeout = 8 * 1000;
int mode = 0;
int rs_cnt = 0;
struct rs_parameter_t // parameters for reed solomon
{
unsigned char x; // AKA fec_data_num (x should be same as <index of rs_par>+1 at the moment)
unsigned char y; // fec_redundant_num
} rs_par[max_fec_packet_num + 10];
struct anti_replay_t
{
int rs_from_str(char *s) // todo inefficient
{
vector<string> str_vec = string_to_vec(s, ",");
if (str_vec.size() < 1) {
mylog(log_warn, "failed to parse [%s]\n", s);
return -1;
}
vector<rs_parameter_t> par_vec;
for (int i = 0; i < (int)str_vec.size(); i++) {
rs_parameter_t tmp_par;
string &tmp_str = str_vec[i];
int x, y;
if (sscanf((char *)tmp_str.c_str(), "%d:%d", &x, &y) != 2) {
mylog(log_warn, "failed to parse [%s]\n", tmp_str.c_str());
return -1;
}
if (x < 1 || y < 0 || x + y > max_fec_packet_num) {
mylog(log_warn, "invaild value x=%d y=%d, x should >=1, y should >=0, x +y should <%d\n", x, y, max_fec_packet_num);
return -1;
}
tmp_par.x = x;
tmp_par.y = y;
par_vec.push_back(tmp_par);
}
assert(par_vec.size() == str_vec.size());
u64_t replay_buffer[anti_replay_buff_size];
unordered_set<u32_t> st;
int index;
anti_replay_t()
{
clear();
}
int clear()
{
memset(replay_buffer,-1,sizeof(replay_buffer));
st.clear();
st.rehash(anti_replay_buff_size*3);
index=0;
return 0;
}
void set_invaild(u32_t seq)
{
int found_problem = 0;
for (int i = 1; i < (int)par_vec.size(); i++) {
if (par_vec[i].x <= par_vec[i - 1].x) {
mylog(log_warn, "error in [%s], x in x:y should be in ascend order\n", s);
return -1;
}
int now_x = par_vec[i].x;
int now_y = par_vec[i].y;
int pre_x = par_vec[i - 1].x;
int pre_y = par_vec[i - 1].y;
if(st.find(seq)!=st.end() )
{
mylog(log_trace,"seq %u exist\n",seq);
return;
//return 0;
}
if(replay_buffer[index]!=u64_t(i64_t(-1)))
{
assert(st.find(replay_buffer[index])!=st.end());
st.erase(replay_buffer[index]);
}
replay_buffer[index]=seq;
st.insert(seq);
index++;
if(index==int(anti_replay_buff_size)) index=0;
//return 1; //for complier check
}
int is_vaild(u32_t seq)
{
return st.find(seq)==st.end();
}
double now_ratio = double(par_vec[i].y) / par_vec[i].x;
double pre_ratio = double(par_vec[i - 1].y) / par_vec[i - 1].x;
if (pre_ratio + 0.0001 < now_ratio) {
if (found_problem == 0) {
mylog(log_warn, "possible problems: %d/%d<%d/%d", pre_y, pre_x, now_y, now_x);
found_problem = 1;
} else {
log_bare(log_warn, ", %d/%d<%d/%d", pre_y, pre_x, now_y, now_x);
}
}
}
if (found_problem) {
log_bare(log_warn, " in %s\n", s);
}
{ // special treatment for first parameter
int x = par_vec[0].x;
int y = par_vec[0].y;
for (int i = 1; i <= x; i++) {
rs_par[i - 1].x = i;
rs_par[i - 1].y = y;
}
}
for (int i = 1; i < (int)par_vec.size(); i++) {
int now_x = par_vec[i].x;
int now_y = par_vec[i].y;
int pre_x = par_vec[i - 1].x;
int pre_y = par_vec[i - 1].y;
rs_par[now_x - 1].x = now_x;
rs_par[now_x - 1].y = now_y;
double now_ratio = double(par_vec[i].y) / par_vec[i].x;
double pre_ratio = double(par_vec[i - 1].y) / par_vec[i - 1].x;
// double k= double(now_y-pre_y)/double(now_x-pre_x);
for (int j = pre_x + 1; j <= now_x - 1; j++) {
int in_x = j;
//////// int in_y= double(pre_y) + double(in_x-pre_x)*k+ 0.9999;// round to upper
double distance = now_x - pre_x;
/////// double in_ratio=pre_ratio*(1.0-(in_x-pre_x)/distance) + now_ratio *(1.0- (now_x-in_x)/distance);
////// int in_y= in_x*in_ratio + 0.9999;
int in_y = pre_y + (now_y - pre_y) * (in_x - pre_x) / distance + 0.9999;
if (in_x + in_y > max_fec_packet_num) {
in_y = max_fec_packet_num - in_x;
assert(in_y >= 0 && in_y <= max_fec_packet_num);
}
rs_par[in_x - 1].x = in_x;
rs_par[in_x - 1].y = in_y;
}
}
rs_cnt = par_vec[par_vec.size() - 1].x;
return 0;
}
char *rs_to_str() // todo inefficient
{
static char res[rs_str_len];
string tmp_string;
char tmp_buf[100];
assert(rs_cnt >= 1);
for (int i = 0; i < rs_cnt; i++) {
sprintf(tmp_buf, "%d:%d", int(rs_par[i].x), int(rs_par[i].y));
if (i != 0)
tmp_string += ",";
tmp_string += tmp_buf;
}
strcpy(res, tmp_string.c_str());
return res;
}
rs_parameter_t get_tail() {
assert(rs_cnt >= 1);
return rs_par[rs_cnt - 1];
}
int clone(fec_parameter_t &other) {
version = other.version;
mtu = other.mtu;
queue_len = other.queue_len;
timeout = other.timeout;
mode = other.mode;
assert(other.rs_cnt >= 1);
rs_cnt = other.rs_cnt;
memcpy(rs_par, other.rs_par, sizeof(rs_parameter_t) * rs_cnt);
return 0;
}
int copy_fec(fec_parameter_t &other) {
assert(other.rs_cnt >= 1);
rs_cnt = other.rs_cnt;
memcpy(rs_par, other.rs_par, sizeof(rs_parameter_t) * rs_cnt);
return 0;
}
};
struct blob_encode_t
{
char input_buf[(max_fec_packet_num+5)*buf_len];
int current_len;
int counter;
extern fec_parameter_t g_fec_par;
// extern int dynamic_update_fec;
char *output_buf[max_fec_packet_num+100];
const int anti_replay_timeout = 120 * 1000; // 120s
blob_encode_t();
struct anti_replay_t {
struct info_t {
my_time_t my_time;
int index;
};
u64_t replay_buffer[anti_replay_buff_size];
unordered_map<u32_t, info_t> mp;
int index;
anti_replay_t() {
clear();
}
int clear() {
memset(replay_buffer, -1, sizeof(replay_buffer));
mp.clear();
mp.rehash(anti_replay_buff_size * 3);
index = 0;
return 0;
}
void set_invaild(u32_t seq) {
if (is_vaild(seq) == 0) {
mylog(log_trace, "seq %u exist\n", seq);
// assert(mp.find(seq)!=mp.end());
// mp[seq].my_time=get_current_time_rough();
return;
}
if (replay_buffer[index] != u64_t(i64_t(-1))) {
assert(mp.find(replay_buffer[index]) != mp.end());
mp.erase(replay_buffer[index]);
}
replay_buffer[index] = seq;
assert(mp.find(seq) == mp.end());
mp[seq].my_time = get_current_time();
mp[seq].index = index;
index++;
if (index == int(anti_replay_buff_size)) index = 0;
}
int is_vaild(u32_t seq) {
if (mp.find(seq) == mp.end()) return 1;
if (get_current_time() - mp[seq].my_time > anti_replay_timeout) {
replay_buffer[mp[seq].index] = u64_t(i64_t(-1));
mp.erase(seq);
return 1;
}
return 0;
}
};
struct blob_encode_t {
char input_buf[(max_fec_packet_num + 5) * buf_len];
int current_len;
int counter;
char *output_buf[max_fec_packet_num + 100];
blob_encode_t();
int clear();
int get_num();
int get_shard_len(int n);
int get_shard_len(int n,int next_packet_len);
int get_shard_len(int n, int next_packet_len);
int input(char *s,int len); //len=use len=0 for second and following packet
int output(int n,char ** &s_arr,int & len);
int input(char *s, int len); // len=use len=0 for second and following packet
int output(int n, char **&s_arr, int &len);
};
struct blob_decode_t
{
char input_buf[(max_fec_packet_num+5)*buf_len];
int current_len;
int last_len;
int counter;
struct blob_decode_t {
char input_buf[(max_fec_packet_num + 5) * buf_len];
int current_len;
int last_len;
int counter;
char *output_buf[max_blob_packet_num+100];
int output_len[max_blob_packet_num+100];
char *output_buf[max_blob_packet_num + 100];
int output_len[max_blob_packet_num + 100];
blob_decode_t();
int clear();
int input(char *input,int len);
int output(int &n,char ** &output,int *&len_arr);
blob_decode_t();
int clear();
int input(char *input, int len);
int output(int &n, char **&output, int *&len_arr);
};
class fec_encode_manager_t
{
private:
u32_t seq;
class fec_encode_manager_t : not_copy_able_t {
private:
u32_t seq;
int fec_mode;
int fec_data_num,fec_redundant_num;
int fec_mtu;
int fec_queue_len;
int fec_timeout;
// int fec_mode;
// int fec_data_num,fec_redundant_num;
// int fec_mtu;
// int fec_queue_len;
// int fec_timeout;
fec_parameter_t fec_par;
my_time_t first_packet_time;
my_time_t first_packet_time_for_output;
my_time_t first_packet_time;
my_time_t first_packet_time_for_output;
blob_encode_t blob_encode;
char input_buf[max_fec_packet_num + 5][buf_len];
int input_len[max_fec_packet_num + 100];
blob_encode_t blob_encode;
char input_buf[max_fec_packet_num+5][buf_len];
int input_len[max_fec_packet_num+100];
char *output_buf[max_fec_packet_num + 100];
int output_len[max_fec_packet_num + 100];
char *output_buf[max_fec_packet_num+100];
int output_len[max_fec_packet_num+100];
int counter;
// int timer_fd;
// u64_t timer_fd64;
int counter;
int timer_fd;
u64_t timer_fd64;
int ready_for_output;
u32_t output_n;
int ready_for_output;
u32_t output_n;
int append(char *s, int len);
ev_timer timer;
struct ev_loop *loop = 0;
void (*cb)(struct ev_loop *loop, struct ev_timer *watcher, int revents) = 0;
int append(char *s,int len);
public:
fec_encode_manager_t();
~fec_encode_manager_t();
public:
fec_encode_manager_t();
~fec_encode_manager_t();
fec_parameter_t &get_fec_par() {
return fec_par;
}
void set_data(void *data) {
timer.data = data;
}
int clear()
{
counter=0;
blob_encode.clear();
ready_for_output=0;
void set_loop_and_cb(struct ev_loop *loop, void (*cb)(struct ev_loop *loop, struct ev_timer *watcher, int revents)) {
this->loop = loop;
this->cb = cb;
ev_init(&timer, cb);
}
itimerspec zero_its;
memset(&zero_its, 0, sizeof(zero_its));
int clear_data() {
counter = 0;
blob_encode.clear();
ready_for_output = 0;
timerfd_settime(timer_fd, TFD_TIMER_ABSTIME, &zero_its, 0);
seq = (u32_t)get_fake_random_number(); // TODO temp solution for a bug.
seq=(u32_t)get_true_random_number(); //TODO temp solution for a bug.
if (loop) {
ev_timer_stop(loop, &timer);
}
return 0;
}
int clear_all() {
// itimerspec zero_its;
// memset(&zero_its, 0, sizeof(zero_its));
// timerfd_settime(timer_fd, TFD_TIMER_ABSTIME, &zero_its, 0);
return 0;
}
if (loop) {
ev_timer_stop(loop, &timer);
loop = 0;
cb = 0;
}
my_time_t get_first_packet_time()
{
return first_packet_time_for_output;
}
clear_data();
int get_pending_time()
{
return fec_timeout;
}
return 0;
}
int get_type()
{
return fec_mode;
}
u64_t get_timer_fd64();
int reset_fec_parameter(int data_num,int redundant_num,int mtu,int pending_num,int pending_time,int type);
int input(char *s,int len/*,int &is_first_packet*/);
int output(int &n,char ** &s_arr,int *&len);
my_time_t get_first_packet_time() {
return first_packet_time_for_output;
}
int get_pending_time() {
return fec_par.timeout;
}
int get_type() {
return fec_par.mode;
}
// u64_t get_timer_fd64();
int reset_fec_parameter(int data_num, int redundant_num, int mtu, int pending_num, int pending_time, int type);
int input(char *s, int len /*,int &is_first_packet*/);
int output(int &n, char **&s_arr, int *&len);
};
struct fec_data_t
{
int used;
u32_t seq;
int type;
int data_num;
int redundant_num;
int idx;
char buf[buf_len];
int len;
struct fec_data_t {
int used;
u32_t seq;
int type;
int data_num;
int redundant_num;
int idx;
char buf[buf_len];
int len;
};
struct fec_group_t
{
int type=-1;
int data_num=-1;
int redundant_num=-1;
int len=-1;
//int data_counter=0;
map<int,int> group_mp;
struct fec_group_t {
int type = -1;
int data_num = -1;
int redundant_num = -1;
int len = -1;
int fec_done = 0;
// int data_counter=0;
map<int, int> group_mp;
};
class fec_decode_manager_t
{
anti_replay_t anti_replay;
fec_data_t *fec_data;
unordered_map<u32_t, fec_group_t> mp;
blob_decode_t blob_decode;
class fec_decode_manager_t : not_copy_able_t {
anti_replay_t anti_replay;
fec_data_t *fec_data = 0;
unordered_map<u32_t, fec_group_t> mp;
blob_decode_t blob_decode;
int index;
int index;
int output_n;
char ** output_s_arr;
int * output_len_arr;
int ready_for_output;
int output_n;
char **output_s_arr;
int *output_len_arr;
int ready_for_output;
char *output_s_arr_buf[max_fec_packet_num+100];//only for type=1,for type=0 the buf inside blot_t is used
int output_len_arr_buf[max_fec_packet_num+100];//same
char *output_s_arr_buf[max_fec_packet_num + 100]; // only for type=1,for type=0 the buf inside blot_t is used
int output_len_arr_buf[max_fec_packet_num + 100]; // same
public:
fec_decode_manager_t()
{
fec_data=new fec_data_t[fec_buff_num+5];
clear();
}
fec_decode_manager_t(const fec_decode_manager_t &b)
{
assert(0==1);//not allowed to copy
}
~fec_decode_manager_t()
{
delete fec_data;
}
int clear()
{
anti_replay.clear();
mp.clear();
mp.rehash(fec_buff_num*3);
public:
fec_decode_manager_t() {
fec_data = new fec_data_t[fec_buff_num + 5];
assert(fec_data != 0);
clear();
}
/*
fec_decode_manager_t(const fec_decode_manager_t &b)
{
assert(0==1);//not allowed to copy
}*/
~fec_decode_manager_t() {
mylog(log_debug, "fec_decode_manager destroyed\n");
if (fec_data != 0) {
mylog(log_debug, "fec_data freed\n");
delete[] fec_data;
}
}
int clear() {
anti_replay.clear();
mp.clear();
mp.rehash(fec_buff_num * 3);
for(int i=0;i<(int)fec_buff_num;i++)
fec_data[i].used=0;
ready_for_output=0;
index=0;
for (int i = 0; i < (int)fec_buff_num; i++)
fec_data[i].used = 0;
ready_for_output = 0;
index = 0;
return 0;
}
return 0;
}
//int re_init();
int input(char *s,int len);
int output(int &n,char ** &s_arr,int* &len_arr);
// int re_init();
int input(char *s, int len);
int output(int &n, char **&s_arr, int *&len_arr);
};
#endif /* FEC_MANAGER_H_ */

BIN
images/cn/merlin1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

BIN
images/cn/merlin2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

BIN
images/cn/merlin3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View File

@ -50,7 +50,7 @@ typedef unsigned long u_long;
/*
* compatibility stuff
*/
#ifdef MSDOS /* but also for others, e.g. sun... */
#if defined(MSDOS)||defined(__MINGW32__) /* but also for others, e.g. sun... */
#define NEED_BCOPY
#define bcmp(a,b,n) memcmp(a,b,n)
#endif
@ -195,7 +195,7 @@ static gf gf_mul_table[GF_SIZE + 1][GF_SIZE + 1];
#define gf_mul(x,y) gf_mul_table[x][y]
#define USE_GF_MULC register gf * __gf_mulc_
#define USE_GF_MULC gf * __gf_mulc_
#define GF_MULC0(c) __gf_mulc_ = gf_mul_table[c]
#define GF_ADDMULC(dst, x) dst ^= __gf_mulc_[x]
@ -220,7 +220,7 @@ gf_mul(x,y)
}
#define init_mul_table()
#define USE_GF_MULC register gf * __gf_mulc_
#define USE_GF_MULC gf * __gf_mulc_
#define GF_MULC0(c) __gf_mulc_ = &gf_exp[ gf_log[c] ]
#define GF_ADDMULC(dst, x) { if (x) dst ^= __gf_mulc_[ gf_log[x] ] ; }
#endif
@ -341,7 +341,7 @@ static void
addmul1(gf *dst1, gf *src1, gf c, int sz)
{
USE_GF_MULC ;
register gf *dst = dst1, *src = src1 ;
gf *dst = dst1, *src = src1 ;
gf *lim = &dst[sz - UNROLL + 1] ;
GF_MULC0(c) ;

View File

@ -1,76 +0,0 @@
/*
* rs.c
*
* Created on: Sep 14, 2017
* Author: root
*/
#include "rs.h"
#include "stdlib.h"
#include "string.h"
void rs_encode(void *code,char *data[],int size)
{
int k=get_k(code);
int n=get_n(code);
for(int i=k;i<n;i++)
{
fec_encode(code, (void **)data, data[i],i, size);
}
return ;
}
int rs_decode(void *code,char *data[],int size)
{
int k=get_k(code);
int n=get_n(code);
int index[n];
int count=0;
for(int i=0;i<n;i++)
{
if(data[i]!=0)
{
index[count++]=i;
}
}
if(count<k)
return -1;
for(int i=0;i<n;i++)
{
if(i<count)
data[i]=data[index[i]];
else
data[i]=0;
}
return fec_decode(code,(void**)data,index,size);
}
static void * (*table)[256]=0;
void* get_code(int k,int n)
{
if (table==0)
{
table=(void* (*)[256]) malloc(sizeof(void*)*256*256);
if(!table)
{
return table;
}
memset(table,0,sizeof(void*)*256*256);
}
if(table[k][n]==0)
{
table[k][n]=fec_new(k,n);
}
return table[k][n];
}
void rs_encode2(int k,int n,char *data[],int size)
{
void* code=get_code(k,n);
rs_encode(code,data,size);
}
int rs_decode2(int k,int n,char *data[],int size)
{
void* code=get_code(k,n);
return rs_decode(code,data,size);
}

64
lib/rs.cpp Normal file
View File

@ -0,0 +1,64 @@
/*
* rs.c
*
* Created on: Sep 14, 2017
* Author: root
*/
#include "rs.h"
#include "stdlib.h"
#include "string.h"
void rs_encode(void *code, char *data[], int size) {
int k = get_k(code);
int n = get_n(code);
for (int i = k; i < n; i++) {
fec_encode(code, (void **)data, data[i], i, size);
}
return;
}
int rs_decode(void *code, char *data[], int size) {
int k = get_k(code);
int n = get_n(code);
int index[n];
int count = 0;
for (int i = 0; i < n; i++) {
if (data[i] != 0) {
index[count++] = i;
}
}
if (count < k)
return -1;
for (int i = 0; i < n; i++) {
if (i < count)
data[i] = data[index[i]];
else
data[i] = 0;
}
return fec_decode(code, (void **)data, index, size);
}
static void *(*table)[256] = 0;
void *get_code(int k, int n) {
if (table == 0) {
table = (void *(*)[256])malloc(sizeof(void *) * 256 * 256);
if (!table) {
return table;
}
memset(table, 0, sizeof(void *) * 256 * 256);
}
if (table[k][n] == 0) {
table[k][n] = fec_new(k, n);
}
return table[k][n];
}
void rs_encode2(int k, int n, char *data[], int size) {
void *code = get_code(k, n);
rs_encode(code, data, size);
}
int rs_decode2(int k, int n, char *data[], int size) {
void *code = get_code(k, n);
return rs_decode(code, data, size);
}

View File

@ -20,8 +20,7 @@
//
// info:
// the function will always succeed,except malloc fail.if malloc fail,it will call exit()
void rs_encode(void *code,char *data[],int size);
void rs_encode(void *code, char *data[], int size);
// input:
// data[0.....n-1] points to original data and redundate data,in right order
@ -37,15 +36,10 @@ void rs_encode(void *code,char *data[],int size);
// advanced info:
// 1. rs_decode wont malloc memory for those zero pointers in data[0.....k-1]. instead it will re-use the memory of other non-zero pointers (and let data[0.....k-1] point to those memory).
// 2. if the input data[0.....n-1] contains x non-zero pointers,after called rs_decode,there will still be exactly x non-zero poninters in data[0.....n-1],just the order may change.
int rs_decode(void *code,char *data[],int size);
void rs_encode2(int k,int n,char *data[],int size);
int rs_decode2(int k,int n,char *data[],int size);
int rs_decode(void *code, char *data[], int size);
void rs_encode2(int k, int n, char *data[], int size);
int rs_decode2(int k, int n, char *data[], int size);
#endif /* LIB_RS_H_ */

31
libev/CVS/Entries Normal file
View File

@ -0,0 +1,31 @@
/Changes/1.315/Wed Jun 21 14:42:30 2017//
/LICENSE/1.11/Thu Jan 16 11:51:05 2014//
/Makefile.am/1.9/Wed Dec 21 18:16:08 2011//
/README/1.21/Fri Mar 30 17:43:55 2012//
/README.embed/1.29/Sat Nov 24 10:10:26 2007//
/Symbols.ev/1.14/Tue Jan 11 13:45:28 2011//
/Symbols.event/1.4/Tue May 8 15:52:13 2012//
/autogen.sh/1.3/Mon May 30 15:28:54 2011//
/configure.ac/1.42/Wed Dec 28 04:22:06 2016//
/ev++.h/1.63/Fri Dec 1 06:37:30 2017//
/ev.3/1.107/Wed Jun 21 14:42:30 2017//
/ev.c/1.481/Thu Jun 1 20:25:50 2017//
/ev.h/1.187/Wed Dec 28 04:22:06 2016//
/ev.pod/1.441/Thu Jul 13 10:46:52 2017//
/ev_epoll.c/1.72/Wed Jun 21 14:42:30 2017//
/ev_kqueue.c/1.56/Thu Feb 18 04:48:05 2016//
/ev_poll.c/1.40/Thu Feb 18 04:48:05 2016//
/ev_port.c/1.29/Thu Feb 18 04:48:05 2016//
/ev_select.c/1.56/Thu Feb 18 04:48:05 2016//
/ev_vars.h/1.58/Tue Sep 9 21:51:35 2014//
/ev_win32.c/1.18/Thu Nov 12 07:02:37 2015//
/ev_wrap.h/1.38/Tue Nov 6 20:56:50 2012//
/event.c/1.52/Mon Apr 2 23:14:41 2012//
/event.h/1.26/Mon Apr 2 23:15:27 2012//
/event_compat.h/1.8/Wed Feb 16 08:02:51 2011//
/import_libevent/1.29/Tue Apr 15 04:34:07 2008//
/libev.m4/1.16/Mon Oct 28 12:36:44 2013//
/update_ev_c/1.2/Wed Jan 18 12:13:14 2012//
/update_ev_wrap/1.6/Sun May 6 13:09:29 2012//
/update_symbols/1.1/Wed Dec 19 01:59:29 2007//
D

1
libev/CVS/Repository Normal file
View File

@ -0,0 +1 @@
libev

1
libev/CVS/Root Normal file
View File

@ -0,0 +1 @@
:pserver:anonymous@cvs.schmorp.de/schmorpforge

517
libev/Changes vendored Normal file
View File

@ -0,0 +1,517 @@
Revision history for libev, a high-performance and full-featured event loop.
- ANDROID => __ANDROID__ (reported by enh@google.com).
- disable epoll_create1 on android because it has broken header files
and google is unwilling to fix them (reported by enh@google.com).
4.24 Wed Dec 28 05:19:55 CET 2016
- bump version to 4.24, as the release tarball inexplicably
didn't have the right version in ev.h, even though the cvs-tagged
version did have the right one (reported by Ales Teska).
4.23 Wed Nov 16 18:23:41 CET 2016
- move some declarations at the beginning to help certain retarded
microsoft compilers, even though their documentation claims
otherwise (reported by Ruslan Osmanov).
4.22 Sun Dec 20 22:11:50 CET 2015
- when epoll detects unremovable fds in the fd set, rebuild
only the epoll descriptor, not the signal pipe, to avoid
SIGPIPE in ev_async_send. This doesn't solve it on fork,
so document what needs to be done in ev_loop_fork
(analyzed by Benjamin Mahler).
- remove superfluous sys/timeb.h include on win32
(analyzed by Jason Madden).
- updated libecb.
4.20 Sat Jun 20 13:01:43 CEST 2015
- prefer noexcept over throw () with C++ 11.
- update ecb.h due to incompatibilities with c11.
- fix a potential aliasing issue when reading and writing
watcher callbacks.
4.19 Thu Sep 25 08:18:25 CEST 2014
- ev.h wasn't valid C++ anymore, which tripped compilers other than
clang, msvc or gcc (analyzed by Raphael 'kena' Poss). Unfortunately,
C++ doesn't support typedefs for function pointers fully, so the affected
declarations have to spell out the types each time.
- when not using autoconf, tighten the check for clock_gettime and related
functionality.
4.18 Fri Sep 5 17:55:26 CEST 2014
- events on files were not always generated properly with the
epoll backend (testcase by Assaf Inbal).
- mark event pipe fd as cloexec after a fork (analyzed by Sami Farin).
- (ecb) support m68k, m88k and sh (patch by Miod Vallat).
- use a reasonable fallback for EV_NSIG instead of erroring out
when we can't detect the signal set size.
- in the absence of autoconf, do not use the clock syscall
on glibc >= 2.17 (avoids the syscall AND -lrt on systems
doing clock_gettime in userspace).
- ensure extern "C" function pointers are used for externally-visible
loop callbacks (not watcher callbacks yet).
- (ecb) work around memory barriers and volatile apparently both being
broken in visual studio 2008 and later (analysed and patch by Nicolas Noble).
4.15 Fri Mar 1 12:04:50 CET 2013
- destroying a non-default loop would stop the global waitpid
watcher (Denis Bilenko).
- queueing pending watchers of higher priority from a watcher now invokes
them in a timely fashion (reported by Denis Bilenko).
- add throw() to all libev functions that cannot throw exceptions, for
further code size decrease when compiling for C++.
- add throw () to callbacks that must not throw exceptions (allocator,
syserr, loop acquire/release, periodic reschedule cbs).
- fix event_base_loop return code, add event_get_callback, event_base_new,
event_base_get_method calls to improve libevent 1.x emulation and add
some libevent 2.x functionality (based on a patch by Jeff Davey).
- add more memory fences to fix a bug reported by Jeff Davey. Better
be overfenced than underprotected.
- ev_run now returns a boolean status (true meaning watchers are
still active).
- ev_once: undef EV_ERROR in ev_kqueue.c, to avoid clashing with
libev's EV_ERROR (reported by 191919).
- (ecb) add memory fence support for xlC (Darin McBride).
- (ecb) add memory fence support for gcc-mips (Anton Kirilov).
- (ecb) add memory fence support for gcc-alpha (Christian Weisgerber).
- work around some kernels losing file descriptors by leaking
the kqueue descriptor in the child.
- work around linux inotify not reporting IN_ATTRIB changes for directories
in many cases.
- include sys/syscall.h instead of plain syscall.h.
- check for io watcher loops in ev_verify, check for the most
common reported usage bug in ev_io_start.
- choose socket vs. WSASocket at compiletime using EV_USE_WSASOCKET.
- always use WSASend/WSARecv directly on windows, hoping that this
works in all cases (unlike read/write/send/recv...).
- try to detect signals around a fork faster (test program by
Denis Bilenko).
- work around recent glibc versions that leak memory in realloc.
- rename ev::embed::set to ev::embed::set_embed to avoid clashing
the watcher base set (loop) method.
- rewrite the async/signal pipe logic to always keep a valid fd, which
simplifies (and hopefully correctifies :) the race checking
on fork, at the cost of one extra fd.
- add fat, msdos, jffs2, ramfs, ntfs and btrfs to the list of
inotify-supporting filesystems.
- move orig_CFLAGS assignment to after AC_INIT, as newer autoconf
versions ignore it before
(https://bugzilla.redhat.com/show_bug.cgi?id=908096).
- add some untested android support.
- enum expressions must be of type int (reported by Juan Pablo L).
4.11 Sat Feb 4 19:52:39 CET 2012
- INCOMPATIBLE CHANGE: ev_timer_again now clears the pending status, as
was documented already, but not implemented in the repeating case.
- new compiletime symbols: EV_NO_SMP and EV_NO_THREADS.
- fix a race where the workaround against the epoll fork bugs
caused signals to not be handled anymore.
- correct backend_fudge for most backends, and implement a windows
specific workaround to avoid looping because we call both
select and Sleep, both with different time resolutions.
- document range and guarantees of ev_sleep.
- document reasonable ranges for periodics interval and offset.
- rename backend_fudge to backend_mintime to avoid future confusion :)
- change the default periodic reschedule function to hopefully be more
exact and correct even in corner cases or in the far future.
- do not rely on -lm anymore: use it when available but use our
own floor () if it is missing. This should make it easier to embed,
as no external libraries are required.
- strategically import macros from libecb and mark rarely-used functions
as cache-cold (saving almost 2k code size on typical amd64 setups).
- add Symbols.ev and Symbols.event files, that were missing.
- fix backend_mintime value for epoll (was 1/1024, is 1/1000 now).
- fix #3 "be smart about timeouts" to not "deadlock" when
timeout == now, also improve the section overall.
- avoid "AVOIDING FINISHING BEFORE RETURNING" idiom.
- support new EV_API_STATIC mode to make all libev symbols
static.
- supply default CFLAGS of -g -O3 with gcc when original CFLAGS
were empty.
4.04 Wed Feb 16 09:01:51 CET 2011
- fix two problems in the native win32 backend, where reuse of fd's
with different underlying handles caused handles not to be removed
or added to the select set (analyzed and tested by Bert Belder).
- do no rely on ceil() in ev_e?poll.c.
- backport libev to HP-UX versions before 11 v3.
- configure did not detect nanosleep and clock_gettime properly when
they are available in the libc (as opposed to -lrt).
4.03 Tue Jan 11 14:37:25 CET 2011
- officially support polling files with all backends.
- support files, /dev/zero etc. the same way as select in the epoll
backend, by generating events on our own.
- ports backend: work around solaris bug 6874410 and many related ones
(EINTR, maybe more), with no performance loss (note that the solaris
bug report is actually wrong, reality is far more bizarre and broken
than that).
- define EV_READ/EV_WRITE as macros in event.h, as some programs use
#ifdef to test for them.
- new (experimental) function: ev_feed_signal.
- new (to become default) EVFLAG_NOSIGMASK flag.
- new EVBACKEND_MASK symbol.
- updated COMMON IDIOMS SECTION.
4.01 Fri Nov 5 21:51:29 CET 2010
- automake fucked it up, apparently, --add-missing -f is not quite enough
to make it update its files, so 4.00 didn't install ev++.h and
event.h on make install. grrr.
- ev_loop(count|depth) didn't return anything (Robin Haberkorn).
- change EV_UNDEF to 0xffffffff to silence some overzealous compilers.
- use "(libev) " prefix for all libev error messages now.
4.00 Mon Oct 25 12:32:12 CEST 2010
- "PORTING FROM LIBEV 3.X TO 4.X" (in ev.pod) is recommended reading.
- ev_embed_stop did not correctly stop the watcher (very good
testcase by Vladimir Timofeev).
- ev_run will now always update the current loop time - it erroneously
didn't when idle watchers were active, causing timers not to fire.
- fix a bug where a timeout of zero caused the timer not to fire
in the libevent emulation (testcase by Péter Szabó).
- applied win32 fixes by Michael Lenaghan (also James Mansion).
- replace EV_MINIMAL by EV_FEATURES.
- prefer EPOLL_CTL_ADD over EPOLL_CTL_MOD in some more cases, as it
seems the former is *much* faster than the latter.
- linux kernel version detection (for inotify bug workarounds)
did not work properly.
- reduce the number of spurious wake-ups with the ports backend.
- remove dependency on sys/queue.h on freebsd (patch by Vanilla Hsu).
- do async init within ev_async_start, not ev_async_set, which avoids
an API quirk where the set function must be called in the C++ API
even when there is nothing to set.
- add (undocumented) EV_ENABLE when adding events with kqueue,
this might help with OS X, which seems to need it despite documenting
not to need it (helpfully pointed out by Tilghman Lesher).
- do not use poll by default on freebsd, it's broken (what isn't
on freebsd...).
- allow to embed epoll on kernels >= 2.6.32.
- configure now prepends -O3, not appends it, so one can still
override it.
- ev.pod: greatly expanded the portability section, added a porting
section, a description of watcher states and made lots of minor fixes.
- disable poll backend on AIX, the poll header spams the namespace
and it's not worth working around dead platforms (reported
and analyzed by Aivars Kalvans).
- improve header file compatibility of the standalone eventfd code
in an obscure case.
- implement EV_AVOID_STDIO option.
- do not use sscanf to parse linux version number (smaller, faster,
no sscanf dependency).
- new EV_CHILD_ENABLE and EV_SIGNAL_ENABLE configurable settings.
- update libev.m4 HAVE_CLOCK_SYSCALL test for newer glibcs.
- add section on accept() problems to the manpage.
- rename EV_TIMEOUT to EV_TIMER.
- rename ev_loop_count/depth/verify/loop/unloop.
- remove ev_default_destroy and ev_default_fork.
- switch to two-digit minor version.
- work around an apparent gentoo compiler bug.
- define _DARWIN_UNLIMITED_SELECT. just so.
- use enum instead of #define for most constants.
- improve compatibility to older C++ compilers.
- (experimental) ev_run/ev_default_loop/ev_break/ev_loop_new have now
default arguments when compiled as C++.
- enable automake dependency tracking.
- ev_loop_new no longer leaks memory when loop creation failed.
- new ev_cleanup watcher type.
3.9 Thu Dec 31 07:59:59 CET 2009
- signalfd is no longer used by default and has to be requested
explicitly - this means that easy to catch bugs become hard to
catch race conditions, but the users have spoken.
- point out the unspecified signal mask in the documentation, and
that this is a race condition regardless of EV_SIGNALFD.
- backport inotify code to C89.
- inotify file descriptors could leak into child processes.
- ev_stat watchers could keep an erroneous extra ref on the loop,
preventing exit when unregistering all watchers (testcases
provided by ry@tinyclouds.org).
- implement EV_WIN32_HANDLE_TO_FD and EV_WIN32_CLOSE_FD configuration
symbols to make it easier for apps to do their own fd management.
- support EV_IDLE_ENABLE being disabled in ev++.h
(patch by Didier Spezia).
- take advantage of inotify_init1, if available, to set cloexec/nonblock
on fd creation, to avoid races.
- the signal handling pipe wasn't always initialised under windows
(analysed by lekma).
- changed minimum glibc requirement from glibc 2.9 to 2.7, for
signalfd.
- add missing string.h include (Denis F. Latypoff).
- only replace ev_stat.prev when we detect an actual difference,
so prev is (almost) always different to attr. this might
have caused the problems with 04_stat.t.
- add ev::timer->remaining () method to C++ API.
3.8 Sun Aug 9 14:30:45 CEST 2009
- incompatible change: do not necessarily reset signal handler
to SIG_DFL when a sighandler is stopped.
- ev_default_destroy did not properly free or zero some members,
potentially causing crashes and memory corruption on repeated
ev_default_destroy/ev_default_loop calls.
- take advantage of signalfd on GNU/Linux systems.
- document that the signal mask might be in an unspecified
state when using libev's signal handling.
- take advantage of some GNU/Linux calls to set cloexec/nonblock
on fd creation, to avoid race conditions.
3.7 Fri Jul 17 16:36:32 CEST 2009
- ev_unloop and ev_loop wrongly used a global variable to exit loops,
instead of using a per-loop variable (bug caught by accident...).
- the ev_set_io_collect_interval interpretation has changed.
- add new functionality: ev_set_userdata, ev_userdata,
ev_set_invoke_pending_cb, ev_set_loop_release_cb,
ev_invoke_pending, ev_pending_count, together with a long example
about thread locking.
- add ev_timer_remaining (as requested by Denis F. Latypoff).
- add ev_loop_depth.
- calling ev_unloop in fork/prepare watchers will no longer poll
for new events.
- Denis F. Latypoff corrected many typos in example code snippets.
- honor autoconf detection of EV_USE_CLOCK_SYSCALL, also double-
check that the syscall number is available before trying to
use it (reported by ry@tinyclouds).
- use GetSystemTimeAsFileTime instead of _timeb on windows, for
slightly higher accuracy.
- properly declare ev_loop_verify and ev_now_update even when
!EV_MULTIPLICITY.
- do not compile in any priority code when EV_MAXPRI == EV_MINPRI.
- support EV_MINIMAL==2 for a reduced API.
- actually 0-initialise struct sigaction when installing signals.
- add section on hibernate and stopped processes to ev_timer docs.
3.6 Tue Apr 28 02:49:30 CEST 2009
- multiple timers becoming ready within an event loop iteration
will be invoked in the "correct" order now.
- do not leave the event loop early just because we have no active
watchers, fixing a problem when embedding a kqueue loop
that has active kernel events but no registered watchers
(reported by blacksand blacksand).
- correctly zero the idx values for arrays, so destroying and
reinitialising the default loop actually works (patch by
Malek Hadj-Ali).
- implement ev_suspend and ev_resume.
- new EV_CUSTOM revents flag for use by applications.
- add documentation section about priorities.
- add a glossary to the documentation.
- extend the ev_fork description slightly.
- optimize a jump out of call_pending.
3.53 Sun Feb 15 02:38:20 CET 2009
- fix a bug in event pipe creation on win32 that would cause a
failed assertion on event loop creation (patch by Malek Hadj-Ali).
- probe for CLOCK_REALTIME support at runtime as well and fall
back to gettimeofday if there is an error, to support older
operating systems with newer header files/libraries.
- prefer gettimeofday over clock_gettime with USE_CLOCK_SYSCALL
(default most everywhere), otherwise not.
3.52 Wed Jan 7 21:43:02 CET 2009
- fix compilation of select backend in fd_set mode when NFDBITS is
missing (to get it to compile on QNX, reported by Rodrigo Campos).
- better select-nfds handling when select backend is in fd_set mode.
- diagnose fd_set overruns when select backend is in fd_set mode.
- due to a thinko, instead of disabling everything but
select on the borked OS X platform, everything but select was
allowed (reported by Emanuele Giaquinta).
- actually verify that local and remote port are matching in
libev's socketpair emulation, which makes denial-of-service
attacks harder (but not impossible - it's windows). Make sure
it even works under vista, which thinks that getpeer/sockname
should return fantasy port numbers.
- include "libev" in all assertion messages for potentially
clearer diagnostics.
- event_get_version (libevent compatibility) returned
a useless string instead of the expected version string
(patch by W.C.A. Wijngaards).
3.51 Wed Dec 24 23:00:11 CET 2008
- fix a bug where an inotify watcher was added twice, causing
freezes on hash collisions (reported and analysed by Graham Leggett).
- new config symbol, EV_USE_CLOCK_SYSCALL, to make libev use
a direct syscall - slower, but no dependency on librt et al.
- assume negative return values != -1 signals success of port_getn
(http://cvs.epicsol.org/cgi/viewcvs.cgi/epic5/source/newio.c?rev=1.52)
(no known failure reports, but it doesn't hurt).
- fork detection in ev_embed now stops and restarts the watcher
automatically.
- EXPERIMENTAL: default the method to operator () in ev++.h,
to make it nicer to use functors (requested by Benedek László).
- fixed const object callbacks in ev++.h.
- replaced loop_ref argument of watcher.set (loop) by a direct
ev_loop * in ev++.h, to avoid clashes with functor patch.
- do not try to watch the empty string via inotify.
- inotify watchers could be leaked under certain circumstances.
- OS X 10.5 is actually even more broken than earlier versions,
so fall back to select on that piece of garbage.
- fixed some weirdness in the ev_embed documentation.
3.49 Wed Nov 19 11:26:53 CET 2008
- ev_stat watchers will now use inotify as a mere hint on
kernels <2.6.25, or if the filesystem is not in the
"known to be good" list.
- better mingw32 compatibility (it's not as borked as native win32)
(analysed by Roger Pack).
- include stdio.h in the example program, as too many people are
confused by the weird C language otherwise. I guess the next thing
I get told is that the "..." ellipses in the examples don't compile
with their C compiler.
3.48 Thu Oct 30 09:02:37 CET 2008
- further optimise away the EPOLL_CTL_ADD/MOD combo in the epoll
backend by assuming the kernel event mask hasn't changed if
ADD fails with EEXIST.
- work around spurious event notification bugs in epoll by using
a 32-bit generation counter. recreate kernel state if we receive
spurious notifications or unwanted events. this is very costly,
but I didn't come up with this horrible design.
- use memset to initialise most arrays now and do away with the
init functions.
- expand time-out strategies into a "Be smart about timeouts" section.
- drop the "struct" from all ev_watcher declarations in the
documentation and did other clarifications (yeah, it was a mistake
to have a struct AND a function called ev_loop).
- fix a bug where ev_default would not initialise the default
loop again after it was destroyed with ev_default_destroy.
- rename syserr to ev_syserr to avoid name clashes when embedding,
do similar changes for event.c.
3.45 Tue Oct 21 21:59:26 CEST 2008
- disable inotify usage on linux <2.6.25, as it is broken
(reported by Yoann Vandoorselaere).
- ev_stat erroneously would try to add inotify watchers
even when inotify wasn't available (this should only
have a performance impact).
- ev_once now passes both timeout and io to the callback if both
occur concurrently, instead of giving timeouts precedence.
- disable EV_USE_INOTIFY when sys/inotify.h is too old.
3.44 Mon Sep 29 05:18:39 CEST 2008
- embed watchers now automatically invoke ev_loop_fork on the
embedded loop when the parent loop forks.
- new function: ev_now_update (loop).
- verify_watcher was not marked static.
- improve the "associating..." manpage section.
- documentation tweaks here and there.
3.43 Sun Jul 6 05:34:41 CEST 2008
- include more include files on windows to get struct _stati64
(reported by Chris Hulbert, but doesn't quite fix his issue).
- add missing #include <io.h> in ev.c on windows (reported by
Matt Tolton).
3.42 Tue Jun 17 12:12:07 CEST 2008
- work around yet another windows bug: FD_SET actually adds fd's
multiple times to the fd_*SET*, despite official MSN docs claiming
otherwise. Reported and well-analysed by Matt Tolton.
- define NFDBITS to 0 when EV_SELECT_IS_WINSOCKET to make it compile
(reported any analysed by Chris Hulbert).
- fix a bug in ev_ebadf (this function is only used to catch
programming errors in the libev user). reported by Matt Tolton.
- fix a bug in fd_intern on win32 (could lead to compile errors
under some circumstances, but would work correctly if it compiles).
reported by Matt Tolton.
- (try to) work around missing lstat on windows.
- pass in the write fd set as except fd set under windows. windows
is so uncontrollably lame that it requires this. this means that
switching off oobinline is not supported (but tcp/ip doesn't
have oob, so that would be stupid anyways.
- use posix module symbol to auto-detect monotonic clock presence
and some other default values.
3.41 Fri May 23 18:42:54 CEST 2008
- work around an obscure bug in winsocket select: if you
provide only empty fd sets then select returns WSAEINVAL. how sucky.
- improve timer scheduling stability and reduce use of time_epsilon.
- use 1-based 2-heap for EV_MINIMAL, simplifies code, reduces
codesize and makes for better cache-efficiency.
- use 3-based 4-heap for !EV_MINIMAL. this makes better use
of cpu cache lines and gives better growth behaviour than
2-based heaps.
- cache timestamp within heap for !EV_MINIMAL, to avoid random
memory accesses.
- document/add EV_USE_4HEAP and EV_HEAP_CACHE_AT.
- fix a potential aliasing issue in ev_timer_again.
- add/document ev_periodic_at, retract direct access to ->at.
- improve ev_stat docs.
- add portability requirements section.
- fix manpage headers etc.
- normalise WSA error codes to lower range on windows.
- add consistency check code that can be called automatically
or on demand to check for internal structures (ev_loop_verify).
3.31 Wed Apr 16 20:45:04 CEST 2008
- added last minute fix for ev_poll.c by Brandon Black.
3.3 Wed Apr 16 19:04:10 CEST 2008
- event_base_loopexit should return 0 on success
(W.C.A. Wijngaards).
- added linux eventfd support.
- try to autodetect epoll and inotify support
by libc header version if not using autoconf.
- new symbols: EV_DEFAULT_UC and EV_DEFAULT_UC_.
- declare functions defined in ev.h as inline if
C99 or gcc are available.
- enable inlining with gcc versions 2 and 3.
- work around broken poll implementations potentially
not clearing revents field in ev_poll (Brandon Black)
(no such systems are known at this time).
- work around a bug in realloc on openbsd and darwin,
also makes the erroneous valgrind complaints
go away (noted by various people).
- fix ev_async_pending, add c++ wrapper for ev_async
(based on patch sent by Johannes Deisenhofer).
- add sensible set method to ev::embed.
- made integer constants type int in ev.h.
3.2 Wed Apr 2 17:11:19 CEST 2008
- fix a 64 bit overflow issue in the select backend,
by using fd_mask instead of int for the mask.
- rename internal sighandler to avoid clash with very old perls.
- entering ev_loop will not clear the ONESHOT or NONBLOCKING
flags of any outer loops anymore.
- add ev_async_pending.
3.1 Thu Mar 13 13:45:22 CET 2008
- implement ev_async watchers.
- only initialise signal pipe on demand.
- make use of sig_atomic_t configurable.
- improved documentation.
3.0 Mon Jan 28 13:14:47 CET 2008
- API/ABI bump to version 3.0.
- ev++.h includes "ev.h" by default now, not <ev.h>.
- slightly improved documentation.
- speed up signal detection after a fork.
- only optionally return trace status changed in ev_child
watchers.
- experimental (and undocumented) loop wrappers for ev++.h.
2.01 Tue Dec 25 08:04:41 CET 2007
- separate Changes file.
- fix ev_path_set => ev_stat_set typo.
- remove event_compat.h from the libev tarball.
- change how include files are found.
- doc updates.
- update licenses, explicitly allow for GPL relicensing.
2.0 Sat Dec 22 17:47:03 CET 2007
- new ev_sleep, ev_set_(io|timeout)_collect_interval.
- removed epoll from embeddable fd set.
- fix embed watchers.
- renamed ev_embed.loop to other.
- added exported Symbol tables.
- undefine member wrapper macros at the end of ev.c.
- respect EV_H in ev++.h.
1.86 Tue Dec 18 02:36:57 CET 2007
- fix memleak on loop destroy (not relevant for perl).
1.85 Fri Dec 14 20:32:40 CET 2007
- fix some aliasing issues w.r.t. timers and periodics
(not relevant for perl).
(for historic versions refer to EV/Changes, found in the Perl interface)
0.1 Wed Oct 31 21:31:48 CET 2007
- original version; hacked together in <24h.

37
libev/LICENSE vendored Normal file
View File

@ -0,0 +1,37 @@
All files in libev are
Copyright (c)2007,2008,2009,2010,2011,2012,2013 Marc Alexander Lehmann.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Alternatively, the contents of this package may be used under the terms
of the GNU General Public License ("GPL") version 2 or any later version,
in which case the provisions of the GPL are applicable instead of the
above. If you wish to allow the use of your version of this package only
under the terms of the GPL and not to allow others to use your version of
this file under the BSD license, indicate your decision by deleting the
provisions above and replace them with the notice and other provisions
required by the GPL in this and the other files of this package. If you do
not delete the provisions above, a recipient may use your version of this
file under either the BSD or the GPL.

20
libev/Makefile.am vendored Normal file
View File

@ -0,0 +1,20 @@
AUTOMAKE_OPTIONS = foreign
VERSION_INFO = 4:0:0
EXTRA_DIST = LICENSE Changes libev.m4 autogen.sh \
ev_vars.h ev_wrap.h \
ev_epoll.c ev_select.c ev_poll.c ev_kqueue.c ev_port.c ev_win32.c \
ev.3 ev.pod Symbols.ev Symbols.event
man_MANS = ev.3
include_HEADERS = ev.h ev++.h event.h
lib_LTLIBRARIES = libev.la
libev_la_SOURCES = ev.c event.c
libev_la_LDFLAGS = -version-info $(VERSION_INFO)
ev.3: ev.pod
pod2man -n LIBEV -r "libev-$(VERSION)" -c "libev - high performance full featured event loop" -s3 <$< >$@

58
libev/README vendored Normal file
View File

@ -0,0 +1,58 @@
libev is a high-performance event loop/event model with lots of features.
(see benchmark at http://libev.schmorp.de/bench.html)
ABOUT
Homepage: http://software.schmorp.de/pkg/libev
Mailinglist: libev@lists.schmorp.de
http://lists.schmorp.de/cgi-bin/mailman/listinfo/libev
Library Documentation: http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod
Libev is modelled (very losely) after libevent and the Event perl
module, but is faster, scales better and is more correct, and also more
featureful. And also smaller. Yay.
Some of the specialties of libev not commonly found elsewhere are:
- extensive and detailed, readable documentation (not doxygen garbage).
- fully supports fork, can detect fork in various ways and automatically
re-arms kernel mechanisms that do not support fork.
- highly optimised select, poll, epoll, kqueue and event ports backends.
- filesystem object (path) watching (with optional linux inotify support).
- wallclock-based times (using absolute time, cron-like).
- relative timers/timeouts (handle time jumps).
- fast intra-thread communication between multiple
event loops (with optional fast linux eventfd backend).
- extremely easy to embed (fully documented, no dependencies,
autoconf supported but optional).
- very small codebase, no bloated library, simple code.
- fully extensible by being able to plug into the event loop,
integrate other event loops, integrate other event loop users.
- very little memory use (small watchers, small event loop data).
- optional C++ interface allowing method and function callbacks
at no extra memory or runtime overhead.
- optional Perl interface with similar characteristics (capable
of running Glib/Gtk2 on libev).
- support for other languages (multiple C++ interfaces, D, Ruby,
Python) available from third-parties.
Examples of programs that embed libev: the EV perl module, node.js,
auditd, rxvt-unicode, gvpe (GNU Virtual Private Ethernet), the
Deliantra MMORPG server (http://www.deliantra.net/), Rubinius (a
next-generation Ruby VM), the Ebb web server, the Rev event toolkit.
CONTRIBUTORS
libev was written and designed by Marc Lehmann and Emanuele Giaquinta.
The following people sent in patches or made other noteworthy
contributions to the design (for minor patches, see the Changes
file. If I forgot to include you, please shout at me, it was an
accident):
W.C.A. Wijngaards
Christopher Layne
Chris Brody

3
libev/README.embed vendored Normal file
View File

@ -0,0 +1,3 @@
This file is now included in the main libev documentation, see
http://cvs.schmorp.de/libev/ev.html

73
libev/Symbols.ev vendored Normal file
View File

@ -0,0 +1,73 @@
ev_async_send
ev_async_start
ev_async_stop
ev_backend
ev_break
ev_check_start
ev_check_stop
ev_child_start
ev_child_stop
ev_cleanup_start
ev_cleanup_stop
ev_clear_pending
ev_default_loop
ev_default_loop_ptr
ev_depth
ev_embed_start
ev_embed_stop
ev_embed_sweep
ev_embeddable_backends
ev_feed_event
ev_feed_fd_event
ev_feed_signal
ev_feed_signal_event
ev_fork_start
ev_fork_stop
ev_idle_start
ev_idle_stop
ev_invoke
ev_invoke_pending
ev_io_start
ev_io_stop
ev_iteration
ev_loop_destroy
ev_loop_fork
ev_loop_new
ev_now
ev_now_update
ev_once
ev_pending_count
ev_periodic_again
ev_periodic_start
ev_periodic_stop
ev_prepare_start
ev_prepare_stop
ev_recommended_backends
ev_ref
ev_resume
ev_run
ev_set_allocator
ev_set_invoke_pending_cb
ev_set_io_collect_interval
ev_set_loop_release_cb
ev_set_syserr_cb
ev_set_timeout_collect_interval
ev_set_userdata
ev_signal_start
ev_signal_stop
ev_sleep
ev_stat_start
ev_stat_stat
ev_stat_stop
ev_supported_backends
ev_suspend
ev_time
ev_timer_again
ev_timer_remaining
ev_timer_start
ev_timer_stop
ev_unref
ev_userdata
ev_verify
ev_version_major
ev_version_minor

24
libev/Symbols.event vendored Normal file
View File

@ -0,0 +1,24 @@
event_active
event_add
event_base_dispatch
event_base_free
event_base_get_method
event_base_loop
event_base_loopexit
event_base_new
event_base_once
event_base_priority_init
event_base_set
event_del
event_dispatch
event_get_callback
event_get_method
event_get_version
event_init
event_loop
event_loopexit
event_once
event_pending
event_priority_init
event_priority_set
event_set

3
libev/autogen.sh vendored Normal file
View File

@ -0,0 +1,3 @@
#!/bin/sh
autoreconf --install --symlink --force

27
libev/configure.ac vendored Normal file
View File

@ -0,0 +1,27 @@
AC_INIT
orig_CFLAGS="$CFLAGS"
AC_CONFIG_SRCDIR([ev_epoll.c])
dnl also update ev.h!
AM_INIT_AUTOMAKE(libev,4.24)
AC_CONFIG_HEADERS([config.h])
AM_MAINTAINER_MODE
AC_PROG_CC
dnl Supply default CFLAGS, if not specified
if test -z "$orig_CFLAGS"; then
if test x$GCC = xyes; then
CFLAGS="-g -O3"
fi
fi
AC_PROG_INSTALL
AC_PROG_LIBTOOL
m4_include([libev.m4])
AC_CONFIG_FILES([Makefile])
AC_OUTPUT

816
libev/ev++.h vendored Normal file
View File

@ -0,0 +1,816 @@
/*
* libev simple C++ wrapper classes
*
* Copyright (c) 2007,2008,2010 Marc Alexander Lehmann <libev@schmorp.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modifica-
* tion, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-
* CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-
* CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-
* ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License ("GPL") version 2 or any later version,
* in which case the provisions of the GPL are applicable instead of
* the above. If you wish to allow the use of your version of this file
* only under the terms of the GPL and not to allow others to use your
* version of this file under the BSD license, indicate your decision
* by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete the
* provisions above, a recipient may use your version of this file under
* either the BSD or the GPL.
*/
#ifndef EVPP_H__
#define EVPP_H__
#ifdef EV_H
# include EV_H
#else
# include "ev.h"
#endif
#ifndef EV_USE_STDEXCEPT
# define EV_USE_STDEXCEPT 1
#endif
#if EV_USE_STDEXCEPT
# include <stdexcept>
#endif
namespace ev {
typedef ev_tstamp tstamp;
enum {
UNDEF = EV_UNDEF,
NONE = EV_NONE,
READ = EV_READ,
WRITE = EV_WRITE,
#if EV_COMPAT3
TIMEOUT = EV_TIMEOUT,
#endif
TIMER = EV_TIMER,
PERIODIC = EV_PERIODIC,
SIGNAL = EV_SIGNAL,
CHILD = EV_CHILD,
STAT = EV_STAT,
IDLE = EV_IDLE,
CHECK = EV_CHECK,
PREPARE = EV_PREPARE,
FORK = EV_FORK,
ASYNC = EV_ASYNC,
EMBED = EV_EMBED,
# undef ERROR // some systems stupidly #define ERROR
ERROR = EV_ERROR
};
enum
{
AUTO = EVFLAG_AUTO,
NOENV = EVFLAG_NOENV,
FORKCHECK = EVFLAG_FORKCHECK,
SELECT = EVBACKEND_SELECT,
POLL = EVBACKEND_POLL,
EPOLL = EVBACKEND_EPOLL,
KQUEUE = EVBACKEND_KQUEUE,
DEVPOLL = EVBACKEND_DEVPOLL,
PORT = EVBACKEND_PORT
};
enum
{
#if EV_COMPAT3
NONBLOCK = EVLOOP_NONBLOCK,
ONESHOT = EVLOOP_ONESHOT,
#endif
NOWAIT = EVRUN_NOWAIT,
ONCE = EVRUN_ONCE
};
enum how_t
{
ONE = EVBREAK_ONE,
ALL = EVBREAK_ALL
};
struct bad_loop
#if EV_USE_STDEXCEPT
: std::runtime_error
#endif
{
#if EV_USE_STDEXCEPT
bad_loop ()
: std::runtime_error ("libev event loop cannot be initialized, bad value of LIBEV_FLAGS?")
{
}
#endif
};
#ifdef EV_AX
# undef EV_AX
#endif
#ifdef EV_AX_
# undef EV_AX_
#endif
#if EV_MULTIPLICITY
# define EV_AX raw_loop
# define EV_AX_ raw_loop,
#else
# define EV_AX
# define EV_AX_
#endif
struct loop_ref
{
loop_ref (EV_P) throw ()
#if EV_MULTIPLICITY
: EV_AX (EV_A)
#endif
{
}
bool operator == (const loop_ref &other) const throw ()
{
#if EV_MULTIPLICITY
return EV_AX == other.EV_AX;
#else
return true;
#endif
}
bool operator != (const loop_ref &other) const throw ()
{
#if EV_MULTIPLICITY
return ! (*this == other);
#else
return false;
#endif
}
#if EV_MULTIPLICITY
bool operator == (const EV_P) const throw ()
{
return this->EV_AX == EV_A;
}
bool operator != (const EV_P) const throw ()
{
return ! (*this == EV_A);
}
operator struct ev_loop * () const throw ()
{
return EV_AX;
}
operator const struct ev_loop * () const throw ()
{
return EV_AX;
}
bool is_default () const throw ()
{
return EV_AX == ev_default_loop (0);
}
#endif
#if EV_COMPAT3
void loop (int flags = 0)
{
ev_run (EV_AX_ flags);
}
void unloop (how_t how = ONE) throw ()
{
ev_break (EV_AX_ how);
}
#endif
void run (int flags = 0)
{
ev_run (EV_AX_ flags);
}
void break_loop (how_t how = ONE) throw ()
{
ev_break (EV_AX_ how);
}
void post_fork () throw ()
{
ev_loop_fork (EV_AX);
}
unsigned int backend () const throw ()
{
return ev_backend (EV_AX);
}
tstamp now () const throw ()
{
return ev_now (EV_AX);
}
void ref () throw ()
{
ev_ref (EV_AX);
}
void unref () throw ()
{
ev_unref (EV_AX);
}
#if EV_FEATURE_API
unsigned int iteration () const throw ()
{
return ev_iteration (EV_AX);
}
unsigned int depth () const throw ()
{
return ev_depth (EV_AX);
}
void set_io_collect_interval (tstamp interval) throw ()
{
ev_set_io_collect_interval (EV_AX_ interval);
}
void set_timeout_collect_interval (tstamp interval) throw ()
{
ev_set_timeout_collect_interval (EV_AX_ interval);
}
#endif
// function callback
void once (int fd, int events, tstamp timeout, void (*cb)(int, void *), void *arg = 0) throw ()
{
ev_once (EV_AX_ fd, events, timeout, cb, arg);
}
// method callback
template<class K, void (K::*method)(int)>
void once (int fd, int events, tstamp timeout, K *object) throw ()
{
once (fd, events, timeout, method_thunk<K, method>, object);
}
// default method == operator ()
template<class K>
void once (int fd, int events, tstamp timeout, K *object) throw ()
{
once (fd, events, timeout, method_thunk<K, &K::operator ()>, object);
}
template<class K, void (K::*method)(int)>
static void method_thunk (int revents, void *arg)
{
(static_cast<K *>(arg)->*method)
(revents);
}
// no-argument method callback
template<class K, void (K::*method)()>
void once (int fd, int events, tstamp timeout, K *object) throw ()
{
once (fd, events, timeout, method_noargs_thunk<K, method>, object);
}
template<class K, void (K::*method)()>
static void method_noargs_thunk (int revents, void *arg)
{
(static_cast<K *>(arg)->*method)
();
}
// simpler function callback
template<void (*cb)(int)>
void once (int fd, int events, tstamp timeout) throw ()
{
once (fd, events, timeout, simpler_func_thunk<cb>);
}
template<void (*cb)(int)>
static void simpler_func_thunk (int revents, void *arg)
{
(*cb)
(revents);
}
// simplest function callback
template<void (*cb)()>
void once (int fd, int events, tstamp timeout) throw ()
{
once (fd, events, timeout, simplest_func_thunk<cb>);
}
template<void (*cb)()>
static void simplest_func_thunk (int revents, void *arg)
{
(*cb)
();
}
void feed_fd_event (int fd, int revents) throw ()
{
ev_feed_fd_event (EV_AX_ fd, revents);
}
void feed_signal_event (int signum) throw ()
{
ev_feed_signal_event (EV_AX_ signum);
}
#if EV_MULTIPLICITY
struct ev_loop* EV_AX;
#endif
};
#if EV_MULTIPLICITY
struct dynamic_loop : loop_ref
{
dynamic_loop (unsigned int flags = AUTO) throw (bad_loop)
: loop_ref (ev_loop_new (flags))
{
if (!EV_AX)
throw bad_loop ();
}
~dynamic_loop () throw ()
{
ev_loop_destroy (EV_AX);
EV_AX = 0;
}
private:
dynamic_loop (const dynamic_loop &);
dynamic_loop & operator= (const dynamic_loop &);
};
#endif
struct default_loop : loop_ref
{
default_loop (unsigned int flags = AUTO) throw (bad_loop)
#if EV_MULTIPLICITY
: loop_ref (ev_default_loop (flags))
#endif
{
if (
#if EV_MULTIPLICITY
!EV_AX
#else
!ev_default_loop (flags)
#endif
)
throw bad_loop ();
}
private:
default_loop (const default_loop &);
default_loop &operator = (const default_loop &);
};
inline loop_ref get_default_loop () throw ()
{
#if EV_MULTIPLICITY
return ev_default_loop (0);
#else
return loop_ref ();
#endif
}
#undef EV_AX
#undef EV_AX_
#undef EV_PX
#undef EV_PX_
#if EV_MULTIPLICITY
# define EV_PX loop_ref EV_A
# define EV_PX_ loop_ref EV_A_
#else
# define EV_PX
# define EV_PX_
#endif
template<class ev_watcher, class watcher>
struct base : ev_watcher
{
#if EV_MULTIPLICITY
EV_PX;
// loop set
void set (EV_P) throw ()
{
this->EV_A = EV_A;
}
#endif
base (EV_PX) throw ()
#if EV_MULTIPLICITY
: EV_A (EV_A)
#endif
{
ev_init (this, 0);
}
void set_ (const void *data, void (*cb)(EV_P_ ev_watcher *w, int revents)) throw ()
{
this->data = (void *)data;
ev_set_cb (static_cast<ev_watcher *>(this), cb);
}
// function callback
template<void (*function)(watcher &w, int)>
void set (void *data = 0) throw ()
{
set_ (data, function_thunk<function>);
}
template<void (*function)(watcher &w, int)>
static void function_thunk (EV_P_ ev_watcher *w, int revents)
{
function
(*static_cast<watcher *>(w), revents);
}
// method callback
template<class K, void (K::*method)(watcher &w, int)>
void set (K *object) throw ()
{
set_ (object, method_thunk<K, method>);
}
// default method == operator ()
template<class K>
void set (K *object) throw ()
{
set_ (object, method_thunk<K, &K::operator ()>);
}
template<class K, void (K::*method)(watcher &w, int)>
static void method_thunk (EV_P_ ev_watcher *w, int revents)
{
(static_cast<K *>(w->data)->*method)
(*static_cast<watcher *>(w), revents);
}
// no-argument callback
template<class K, void (K::*method)()>
void set (K *object) throw ()
{
set_ (object, method_noargs_thunk<K, method>);
}
template<class K, void (K::*method)()>
static void method_noargs_thunk (EV_P_ ev_watcher *w, int revents)
{
(static_cast<K *>(w->data)->*method)
();
}
void operator ()(int events = EV_UNDEF)
{
return
ev_cb (static_cast<ev_watcher *>(this))
(static_cast<ev_watcher *>(this), events);
}
bool is_active () const throw ()
{
return ev_is_active (static_cast<const ev_watcher *>(this));
}
bool is_pending () const throw ()
{
return ev_is_pending (static_cast<const ev_watcher *>(this));
}
void feed_event (int revents) throw ()
{
ev_feed_event (EV_A_ static_cast<ev_watcher *>(this), revents);
}
};
inline tstamp now (EV_P) throw ()
{
return ev_now (EV_A);
}
inline void delay (tstamp interval) throw ()
{
ev_sleep (interval);
}
inline int version_major () throw ()
{
return ev_version_major ();
}
inline int version_minor () throw ()
{
return ev_version_minor ();
}
inline unsigned int supported_backends () throw ()
{
return ev_supported_backends ();
}
inline unsigned int recommended_backends () throw ()
{
return ev_recommended_backends ();
}
inline unsigned int embeddable_backends () throw ()
{
return ev_embeddable_backends ();
}
inline void set_allocator (void *(*cb)(void *ptr, long size) throw ()) throw ()
{
ev_set_allocator (cb);
}
inline void set_syserr_cb (void (*cb)(const char *msg) throw ()) throw ()
{
ev_set_syserr_cb (cb);
}
#if EV_MULTIPLICITY
#define EV_CONSTRUCT(cppstem,cstem) \
(EV_PX = get_default_loop ()) throw () \
: base<ev_ ## cstem, cppstem> (EV_A) \
{ \
}
#else
#define EV_CONSTRUCT(cppstem,cstem) \
() throw () \
{ \
}
#endif
/* using a template here would require quite a few more lines,
* so a macro solution was chosen */
#define EV_BEGIN_WATCHER(cppstem,cstem) \
\
struct cppstem : base<ev_ ## cstem, cppstem> \
{ \
void start () throw () \
{ \
ev_ ## cstem ## _start (EV_A_ static_cast<ev_ ## cstem *>(this)); \
} \
\
void stop () throw () \
{ \
ev_ ## cstem ## _stop (EV_A_ static_cast<ev_ ## cstem *>(this)); \
} \
\
cppstem EV_CONSTRUCT(cppstem,cstem) \
\
~cppstem () throw () \
{ \
stop (); \
} \
\
using base<ev_ ## cstem, cppstem>::set; \
\
private: \
\
cppstem (const cppstem &o); \
\
cppstem &operator =(const cppstem &o); \
\
public:
#define EV_END_WATCHER(cppstem,cstem) \
};
EV_BEGIN_WATCHER (io, io)
void set (int fd, int events) throw ()
{
int active = is_active ();
if (active) stop ();
ev_io_set (static_cast<ev_io *>(this), fd, events);
if (active) start ();
}
void set (int events) throw ()
{
int active = is_active ();
if (active) stop ();
ev_io_set (static_cast<ev_io *>(this), fd, events);
if (active) start ();
}
void start (int fd, int events) throw ()
{
set (fd, events);
start ();
}
EV_END_WATCHER (io, io)
EV_BEGIN_WATCHER (timer, timer)
void set (ev_tstamp after, ev_tstamp repeat = 0.) throw ()
{
int active = is_active ();
if (active) stop ();
ev_timer_set (static_cast<ev_timer *>(this), after, repeat);
if (active) start ();
}
void start (ev_tstamp after, ev_tstamp repeat = 0.) throw ()
{
set (after, repeat);
start ();
}
void again () throw ()
{
ev_timer_again (EV_A_ static_cast<ev_timer *>(this));
}
ev_tstamp remaining ()
{
return ev_timer_remaining (EV_A_ static_cast<ev_timer *>(this));
}
EV_END_WATCHER (timer, timer)
#if EV_PERIODIC_ENABLE
EV_BEGIN_WATCHER (periodic, periodic)
void set (ev_tstamp at, ev_tstamp interval = 0.) throw ()
{
int active = is_active ();
if (active) stop ();
ev_periodic_set (static_cast<ev_periodic *>(this), at, interval, 0);
if (active) start ();
}
void start (ev_tstamp at, ev_tstamp interval = 0.) throw ()
{
set (at, interval);
start ();
}
void again () throw ()
{
ev_periodic_again (EV_A_ static_cast<ev_periodic *>(this));
}
EV_END_WATCHER (periodic, periodic)
#endif
#if EV_SIGNAL_ENABLE
EV_BEGIN_WATCHER (sig, signal)
void set (int signum) throw ()
{
int active = is_active ();
if (active) stop ();
ev_signal_set (static_cast<ev_signal *>(this), signum);
if (active) start ();
}
void start (int signum) throw ()
{
set (signum);
start ();
}
EV_END_WATCHER (sig, signal)
#endif
#if EV_CHILD_ENABLE
EV_BEGIN_WATCHER (child, child)
void set (int pid, int trace = 0) throw ()
{
int active = is_active ();
if (active) stop ();
ev_child_set (static_cast<ev_child *>(this), pid, trace);
if (active) start ();
}
void start (int pid, int trace = 0) throw ()
{
set (pid, trace);
start ();
}
EV_END_WATCHER (child, child)
#endif
#if EV_STAT_ENABLE
EV_BEGIN_WATCHER (stat, stat)
void set (const char *path, ev_tstamp interval = 0.) throw ()
{
int active = is_active ();
if (active) stop ();
ev_stat_set (static_cast<ev_stat *>(this), path, interval);
if (active) start ();
}
void start (const char *path, ev_tstamp interval = 0.) throw ()
{
stop ();
set (path, interval);
start ();
}
void update () throw ()
{
ev_stat_stat (EV_A_ static_cast<ev_stat *>(this));
}
EV_END_WATCHER (stat, stat)
#endif
#if EV_IDLE_ENABLE
EV_BEGIN_WATCHER (idle, idle)
void set () throw () { }
EV_END_WATCHER (idle, idle)
#endif
#if EV_PREPARE_ENABLE
EV_BEGIN_WATCHER (prepare, prepare)
void set () throw () { }
EV_END_WATCHER (prepare, prepare)
#endif
#if EV_CHECK_ENABLE
EV_BEGIN_WATCHER (check, check)
void set () throw () { }
EV_END_WATCHER (check, check)
#endif
#if EV_EMBED_ENABLE
EV_BEGIN_WATCHER (embed, embed)
void set_embed (struct ev_loop *embedded_loop) throw ()
{
int active = is_active ();
if (active) stop ();
ev_embed_set (static_cast<ev_embed *>(this), embedded_loop);
if (active) start ();
}
void start (struct ev_loop *embedded_loop) throw ()
{
set (embedded_loop);
start ();
}
void sweep ()
{
ev_embed_sweep (EV_A_ static_cast<ev_embed *>(this));
}
EV_END_WATCHER (embed, embed)
#endif
#if EV_FORK_ENABLE
EV_BEGIN_WATCHER (fork, fork)
void set () throw () { }
EV_END_WATCHER (fork, fork)
#endif
#if EV_ASYNC_ENABLE
EV_BEGIN_WATCHER (async, async)
void send () throw ()
{
ev_async_send (EV_A_ static_cast<ev_async *>(this));
}
bool async_pending () throw ()
{
return ev_async_pending (static_cast<ev_async *>(this));
}
EV_END_WATCHER (async, async)
#endif
#undef EV_PX
#undef EV_PX_
#undef EV_CONSTRUCT
#undef EV_BEGIN_WATCHER
#undef EV_END_WATCHER
}
#endif

5647
libev/ev.3 vendored Normal file

File diff suppressed because it is too large Load Diff

5143
libev/ev.c vendored Normal file

File diff suppressed because it is too large Load Diff

854
libev/ev.h vendored Normal file
View File

@ -0,0 +1,854 @@
/*
* libev native API header
*
* Copyright (c) 2007,2008,2009,2010,2011,2012,2015 Marc Alexander Lehmann <libev@schmorp.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modifica-
* tion, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-
* CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-
* CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-
* ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License ("GPL") version 2 or any later version,
* in which case the provisions of the GPL are applicable instead of
* the above. If you wish to allow the use of your version of this file
* only under the terms of the GPL and not to allow others to use your
* version of this file under the BSD license, indicate your decision
* by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete the
* provisions above, a recipient may use your version of this file under
* either the BSD or the GPL.
*/
#ifndef EV_H_
#define EV_H_
#ifdef __cplusplus
# define EV_CPP(x) x
# if __cplusplus >= 201103L
# define EV_THROW noexcept
# else
# define EV_THROW throw ()
# endif
#else
# define EV_CPP(x)
# define EV_THROW
#endif
EV_CPP(extern "C" {)
/*****************************************************************************/
/* pre-4.0 compatibility */
#ifndef EV_COMPAT3
# define EV_COMPAT3 1
#endif
#ifndef EV_FEATURES
# if defined __OPTIMIZE_SIZE__
# define EV_FEATURES 0x7c
# else
# define EV_FEATURES 0x7f
# endif
#endif
#define EV_FEATURE_CODE ((EV_FEATURES) & 1)
#define EV_FEATURE_DATA ((EV_FEATURES) & 2)
#define EV_FEATURE_CONFIG ((EV_FEATURES) & 4)
#define EV_FEATURE_API ((EV_FEATURES) & 8)
#define EV_FEATURE_WATCHERS ((EV_FEATURES) & 16)
#define EV_FEATURE_BACKENDS ((EV_FEATURES) & 32)
#define EV_FEATURE_OS ((EV_FEATURES) & 64)
/* these priorities are inclusive, higher priorities will be invoked earlier */
#ifndef EV_MINPRI
# define EV_MINPRI (EV_FEATURE_CONFIG ? -2 : 0)
#endif
#ifndef EV_MAXPRI
# define EV_MAXPRI (EV_FEATURE_CONFIG ? +2 : 0)
#endif
#ifndef EV_MULTIPLICITY
# define EV_MULTIPLICITY EV_FEATURE_CONFIG
#endif
#ifndef EV_PERIODIC_ENABLE
# define EV_PERIODIC_ENABLE EV_FEATURE_WATCHERS
#endif
#ifndef EV_STAT_ENABLE
# define EV_STAT_ENABLE EV_FEATURE_WATCHERS
#endif
#ifndef EV_PREPARE_ENABLE
# define EV_PREPARE_ENABLE EV_FEATURE_WATCHERS
#endif
#ifndef EV_CHECK_ENABLE
# define EV_CHECK_ENABLE EV_FEATURE_WATCHERS
#endif
#ifndef EV_IDLE_ENABLE
# define EV_IDLE_ENABLE EV_FEATURE_WATCHERS
#endif
#ifndef EV_FORK_ENABLE
# define EV_FORK_ENABLE EV_FEATURE_WATCHERS
#endif
#ifndef EV_CLEANUP_ENABLE
# define EV_CLEANUP_ENABLE EV_FEATURE_WATCHERS
#endif
#ifndef EV_SIGNAL_ENABLE
# define EV_SIGNAL_ENABLE EV_FEATURE_WATCHERS
#endif
#ifndef EV_CHILD_ENABLE
# ifdef _WIN32
# define EV_CHILD_ENABLE 0
# else
# define EV_CHILD_ENABLE EV_FEATURE_WATCHERS
#endif
#endif
#ifndef EV_ASYNC_ENABLE
# define EV_ASYNC_ENABLE EV_FEATURE_WATCHERS
#endif
#ifndef EV_EMBED_ENABLE
# define EV_EMBED_ENABLE EV_FEATURE_WATCHERS
#endif
#ifndef EV_WALK_ENABLE
# define EV_WALK_ENABLE 0 /* not yet */
#endif
/*****************************************************************************/
#if EV_CHILD_ENABLE && !EV_SIGNAL_ENABLE
# undef EV_SIGNAL_ENABLE
# define EV_SIGNAL_ENABLE 1
#endif
/*****************************************************************************/
typedef double ev_tstamp;
#include <string.h> /* for memmove */
#ifndef EV_ATOMIC_T
# include <signal.h>
# define EV_ATOMIC_T sig_atomic_t volatile
#endif
#if EV_STAT_ENABLE
# ifdef _WIN32
# include <time.h>
# include <sys/types.h>
# endif
# include <sys/stat.h>
#endif
/* support multiple event loops? */
#if EV_MULTIPLICITY
struct ev_loop;
# define EV_P struct ev_loop *loop /* a loop as sole parameter in a declaration */
# define EV_P_ EV_P, /* a loop as first of multiple parameters */
# define EV_A loop /* a loop as sole argument to a function call */
# define EV_A_ EV_A, /* a loop as first of multiple arguments */
# define EV_DEFAULT_UC ev_default_loop_uc_ () /* the default loop, if initialised, as sole arg */
# define EV_DEFAULT_UC_ EV_DEFAULT_UC, /* the default loop as first of multiple arguments */
# define EV_DEFAULT ev_default_loop (0) /* the default loop as sole arg */
# define EV_DEFAULT_ EV_DEFAULT, /* the default loop as first of multiple arguments */
#else
# define EV_P void
# define EV_P_
# define EV_A
# define EV_A_
# define EV_DEFAULT
# define EV_DEFAULT_
# define EV_DEFAULT_UC
# define EV_DEFAULT_UC_
# undef EV_EMBED_ENABLE
#endif
/* EV_INLINE is used for functions in header files */
#if __STDC_VERSION__ >= 199901L || __GNUC__ >= 3
# define EV_INLINE static inline
#else
# define EV_INLINE static
#endif
#ifdef EV_API_STATIC
# define EV_API_DECL static
#else
# define EV_API_DECL extern
#endif
/* EV_PROTOTYPES can be used to switch of prototype declarations */
#ifndef EV_PROTOTYPES
# define EV_PROTOTYPES 1
#endif
/*****************************************************************************/
#define EV_VERSION_MAJOR 4
#define EV_VERSION_MINOR 24
/* eventmask, revents, events... */
enum {
EV_UNDEF = (int)0xFFFFFFFF, /* guaranteed to be invalid */
EV_NONE = 0x00, /* no events */
EV_READ = 0x01, /* ev_io detected read will not block */
EV_WRITE = 0x02, /* ev_io detected write will not block */
EV__IOFDSET = 0x80, /* internal use only */
EV_IO = EV_READ, /* alias for type-detection */
EV_TIMER = 0x00000100, /* timer timed out */
#if EV_COMPAT3
EV_TIMEOUT = EV_TIMER, /* pre 4.0 API compatibility */
#endif
EV_PERIODIC = 0x00000200, /* periodic timer timed out */
EV_SIGNAL = 0x00000400, /* signal was received */
EV_CHILD = 0x00000800, /* child/pid had status change */
EV_STAT = 0x00001000, /* stat data changed */
EV_IDLE = 0x00002000, /* event loop is idling */
EV_PREPARE = 0x00004000, /* event loop about to poll */
EV_CHECK = 0x00008000, /* event loop finished poll */
EV_EMBED = 0x00010000, /* embedded event loop needs sweep */
EV_FORK = 0x00020000, /* event loop resumed in child */
EV_CLEANUP = 0x00040000, /* event loop resumed in child */
EV_ASYNC = 0x00080000, /* async intra-loop signal */
EV_CUSTOM = 0x01000000, /* for use by user code */
EV_ERROR = (int)0x80000000 /* sent when an error occurs */
};
/* can be used to add custom fields to all watchers, while losing binary compatibility */
#ifndef EV_COMMON
# define EV_COMMON void *data;
#endif
#ifndef EV_CB_DECLARE
# define EV_CB_DECLARE(type) void (*cb)(EV_P_ struct type *w, int revents);
#endif
#ifndef EV_CB_INVOKE
# define EV_CB_INVOKE(watcher,revents) (watcher)->cb (EV_A_ (watcher), (revents))
#endif
/* not official, do not use */
#define EV_CB(type,name) void name (EV_P_ struct ev_ ## type *w, int revents)
/*
* struct member types:
* private: you may look at them, but not change them,
* and they might not mean anything to you.
* ro: can be read anytime, but only changed when the watcher isn't active.
* rw: can be read and modified anytime, even when the watcher is active.
*
* some internal details that might be helpful for debugging:
*
* active is either 0, which means the watcher is not active,
* or the array index of the watcher (periodics, timers)
* or the array index + 1 (most other watchers)
* or simply 1 for watchers that aren't in some array.
* pending is either 0, in which case the watcher isn't,
* or the array index + 1 in the pendings array.
*/
#if EV_MINPRI == EV_MAXPRI
# define EV_DECL_PRIORITY
#elif !defined (EV_DECL_PRIORITY)
# define EV_DECL_PRIORITY int priority;
#endif
/* shared by all watchers */
#define EV_WATCHER(type) \
int active; /* private */ \
int pending; /* private */ \
EV_DECL_PRIORITY /* private */ \
EV_COMMON /* rw */ \
EV_CB_DECLARE (type) /* private */
#define EV_WATCHER_LIST(type) \
EV_WATCHER (type) \
struct ev_watcher_list *next; /* private */
#define EV_WATCHER_TIME(type) \
EV_WATCHER (type) \
ev_tstamp at; /* private */
/* base class, nothing to see here unless you subclass */
typedef struct ev_watcher
{
EV_WATCHER (ev_watcher)
} ev_watcher;
/* base class, nothing to see here unless you subclass */
typedef struct ev_watcher_list
{
EV_WATCHER_LIST (ev_watcher_list)
} ev_watcher_list;
/* base class, nothing to see here unless you subclass */
typedef struct ev_watcher_time
{
EV_WATCHER_TIME (ev_watcher_time)
} ev_watcher_time;
/* invoked when fd is either EV_READable or EV_WRITEable */
/* revent EV_READ, EV_WRITE */
typedef struct ev_io
{
EV_WATCHER_LIST (ev_io)
int fd; /* ro */
int events; /* ro */
} ev_io;
/* invoked after a specific time, repeatable (based on monotonic clock) */
/* revent EV_TIMEOUT */
typedef struct ev_timer
{
EV_WATCHER_TIME (ev_timer)
ev_tstamp repeat; /* rw */
} ev_timer;
/* invoked at some specific time, possibly repeating at regular intervals (based on UTC) */
/* revent EV_PERIODIC */
typedef struct ev_periodic
{
EV_WATCHER_TIME (ev_periodic)
ev_tstamp offset; /* rw */
ev_tstamp interval; /* rw */
ev_tstamp (*reschedule_cb)(struct ev_periodic *w, ev_tstamp now) EV_THROW; /* rw */
} ev_periodic;
/* invoked when the given signal has been received */
/* revent EV_SIGNAL */
typedef struct ev_signal
{
EV_WATCHER_LIST (ev_signal)
int signum; /* ro */
} ev_signal;
/* invoked when sigchld is received and waitpid indicates the given pid */
/* revent EV_CHILD */
/* does not support priorities */
typedef struct ev_child
{
EV_WATCHER_LIST (ev_child)
int flags; /* private */
int pid; /* ro */
int rpid; /* rw, holds the received pid */
int rstatus; /* rw, holds the exit status, use the macros from sys/wait.h */
} ev_child;
#if EV_STAT_ENABLE
/* st_nlink = 0 means missing file or other error */
# ifdef _WIN32
typedef struct _stati64 ev_statdata;
# else
typedef struct stat ev_statdata;
# endif
/* invoked each time the stat data changes for a given path */
/* revent EV_STAT */
typedef struct ev_stat
{
EV_WATCHER_LIST (ev_stat)
ev_timer timer; /* private */
ev_tstamp interval; /* ro */
const char *path; /* ro */
ev_statdata prev; /* ro */
ev_statdata attr; /* ro */
int wd; /* wd for inotify, fd for kqueue */
} ev_stat;
#endif
#if EV_IDLE_ENABLE
/* invoked when the nothing else needs to be done, keeps the process from blocking */
/* revent EV_IDLE */
typedef struct ev_idle
{
EV_WATCHER (ev_idle)
} ev_idle;
#endif
/* invoked for each run of the mainloop, just before the blocking call */
/* you can still change events in any way you like */
/* revent EV_PREPARE */
typedef struct ev_prepare
{
EV_WATCHER (ev_prepare)
} ev_prepare;
/* invoked for each run of the mainloop, just after the blocking call */
/* revent EV_CHECK */
typedef struct ev_check
{
EV_WATCHER (ev_check)
} ev_check;
#if EV_FORK_ENABLE
/* the callback gets invoked before check in the child process when a fork was detected */
/* revent EV_FORK */
typedef struct ev_fork
{
EV_WATCHER (ev_fork)
} ev_fork;
#endif
#if EV_CLEANUP_ENABLE
/* is invoked just before the loop gets destroyed */
/* revent EV_CLEANUP */
typedef struct ev_cleanup
{
EV_WATCHER (ev_cleanup)
} ev_cleanup;
#endif
#if EV_EMBED_ENABLE
/* used to embed an event loop inside another */
/* the callback gets invoked when the event loop has handled events, and can be 0 */
typedef struct ev_embed
{
EV_WATCHER (ev_embed)
struct ev_loop *other; /* ro */
ev_io io; /* private */
ev_prepare prepare; /* private */
ev_check check; /* unused */
ev_timer timer; /* unused */
ev_periodic periodic; /* unused */
ev_idle idle; /* unused */
ev_fork fork; /* private */
#if EV_CLEANUP_ENABLE
ev_cleanup cleanup; /* unused */
#endif
} ev_embed;
#endif
#if EV_ASYNC_ENABLE
/* invoked when somebody calls ev_async_send on the watcher */
/* revent EV_ASYNC */
typedef struct ev_async
{
EV_WATCHER (ev_async)
EV_ATOMIC_T sent; /* private */
} ev_async;
# define ev_async_pending(w) (+(w)->sent)
#endif
/* the presence of this union forces similar struct layout */
union ev_any_watcher
{
struct ev_watcher w;
struct ev_watcher_list wl;
struct ev_io io;
struct ev_timer timer;
struct ev_periodic periodic;
struct ev_signal signal;
struct ev_child child;
#if EV_STAT_ENABLE
struct ev_stat stat;
#endif
#if EV_IDLE_ENABLE
struct ev_idle idle;
#endif
struct ev_prepare prepare;
struct ev_check check;
#if EV_FORK_ENABLE
struct ev_fork fork;
#endif
#if EV_CLEANUP_ENABLE
struct ev_cleanup cleanup;
#endif
#if EV_EMBED_ENABLE
struct ev_embed embed;
#endif
#if EV_ASYNC_ENABLE
struct ev_async async;
#endif
};
/* flag bits for ev_default_loop and ev_loop_new */
enum {
/* the default */
EVFLAG_AUTO = 0x00000000U, /* not quite a mask */
/* flag bits */
EVFLAG_NOENV = 0x01000000U, /* do NOT consult environment */
EVFLAG_FORKCHECK = 0x02000000U, /* check for a fork in each iteration */
/* debugging/feature disable */
EVFLAG_NOINOTIFY = 0x00100000U, /* do not attempt to use inotify */
#if EV_COMPAT3
EVFLAG_NOSIGFD = 0, /* compatibility to pre-3.9 */
#endif
EVFLAG_SIGNALFD = 0x00200000U, /* attempt to use signalfd */
EVFLAG_NOSIGMASK = 0x00400000U /* avoid modifying the signal mask */
};
/* method bits to be ored together */
enum {
EVBACKEND_SELECT = 0x00000001U, /* available just about anywhere */
EVBACKEND_POLL = 0x00000002U, /* !win, !aix, broken on osx */
EVBACKEND_EPOLL = 0x00000004U, /* linux */
EVBACKEND_KQUEUE = 0x00000008U, /* bsd, broken on osx */
EVBACKEND_DEVPOLL = 0x00000010U, /* solaris 8 */ /* NYI */
EVBACKEND_PORT = 0x00000020U, /* solaris 10 */
EVBACKEND_ALL = 0x0000003FU, /* all known backends */
EVBACKEND_MASK = 0x0000FFFFU /* all future backends */
};
#if EV_PROTOTYPES
EV_API_DECL int ev_version_major (void) EV_THROW;
EV_API_DECL int ev_version_minor (void) EV_THROW;
EV_API_DECL unsigned int ev_supported_backends (void) EV_THROW;
EV_API_DECL unsigned int ev_recommended_backends (void) EV_THROW;
EV_API_DECL unsigned int ev_embeddable_backends (void) EV_THROW;
EV_API_DECL ev_tstamp ev_time (void) EV_THROW;
EV_API_DECL void ev_sleep (ev_tstamp delay) EV_THROW; /* sleep for a while */
/* Sets the allocation function to use, works like realloc.
* It is used to allocate and free memory.
* If it returns zero when memory needs to be allocated, the library might abort
* or take some potentially destructive action.
* The default is your system realloc function.
*/
EV_API_DECL void ev_set_allocator (void *(*cb)(void *ptr, long size) EV_THROW) EV_THROW;
/* set the callback function to call on a
* retryable syscall error
* (such as failed select, poll, epoll_wait)
*/
EV_API_DECL void ev_set_syserr_cb (void (*cb)(const char *msg) EV_THROW) EV_THROW;
#if EV_MULTIPLICITY
/* the default loop is the only one that handles signals and child watchers */
/* you can call this as often as you like */
EV_API_DECL struct ev_loop *ev_default_loop (unsigned int flags EV_CPP (= 0)) EV_THROW;
#ifdef EV_API_STATIC
EV_API_DECL struct ev_loop *ev_default_loop_ptr;
#endif
EV_INLINE struct ev_loop *
ev_default_loop_uc_ (void) EV_THROW
{
extern struct ev_loop *ev_default_loop_ptr;
return ev_default_loop_ptr;
}
EV_INLINE int
ev_is_default_loop (EV_P) EV_THROW
{
return EV_A == EV_DEFAULT_UC;
}
/* create and destroy alternative loops that don't handle signals */
EV_API_DECL struct ev_loop *ev_loop_new (unsigned int flags EV_CPP (= 0)) EV_THROW;
EV_API_DECL ev_tstamp ev_now (EV_P) EV_THROW; /* time w.r.t. timers and the eventloop, updated after each poll */
#else
EV_API_DECL int ev_default_loop (unsigned int flags EV_CPP (= 0)) EV_THROW; /* returns true when successful */
EV_API_DECL ev_tstamp ev_rt_now;
EV_INLINE ev_tstamp
ev_now (void) EV_THROW
{
return ev_rt_now;
}
/* looks weird, but ev_is_default_loop (EV_A) still works if this exists */
EV_INLINE int
ev_is_default_loop (void) EV_THROW
{
return 1;
}
#endif /* multiplicity */
/* destroy event loops, also works for the default loop */
EV_API_DECL void ev_loop_destroy (EV_P);
/* this needs to be called after fork, to duplicate the loop */
/* when you want to re-use it in the child */
/* you can call it in either the parent or the child */
/* you can actually call it at any time, anywhere :) */
EV_API_DECL void ev_loop_fork (EV_P) EV_THROW;
EV_API_DECL unsigned int ev_backend (EV_P) EV_THROW; /* backend in use by loop */
EV_API_DECL void ev_now_update (EV_P) EV_THROW; /* update event loop time */
#if EV_WALK_ENABLE
/* walk (almost) all watchers in the loop of a given type, invoking the */
/* callback on every such watcher. The callback might stop the watcher, */
/* but do nothing else with the loop */
EV_API_DECL void ev_walk (EV_P_ int types, void (*cb)(EV_P_ int type, void *w)) EV_THROW;
#endif
#endif /* prototypes */
/* ev_run flags values */
enum {
EVRUN_NOWAIT = 1, /* do not block/wait */
EVRUN_ONCE = 2 /* block *once* only */
};
/* ev_break how values */
enum {
EVBREAK_CANCEL = 0, /* undo unloop */
EVBREAK_ONE = 1, /* unloop once */
EVBREAK_ALL = 2 /* unloop all loops */
};
#if EV_PROTOTYPES
EV_API_DECL int ev_run (EV_P_ int flags EV_CPP (= 0));
EV_API_DECL void ev_break (EV_P_ int how EV_CPP (= EVBREAK_ONE)) EV_THROW; /* break out of the loop */
/*
* ref/unref can be used to add or remove a refcount on the mainloop. every watcher
* keeps one reference. if you have a long-running watcher you never unregister that
* should not keep ev_loop from running, unref() after starting, and ref() before stopping.
*/
EV_API_DECL void ev_ref (EV_P) EV_THROW;
EV_API_DECL void ev_unref (EV_P) EV_THROW;
/*
* convenience function, wait for a single event, without registering an event watcher
* if timeout is < 0, do wait indefinitely
*/
EV_API_DECL void ev_once (EV_P_ int fd, int events, ev_tstamp timeout, void (*cb)(int revents, void *arg), void *arg) EV_THROW;
# if EV_FEATURE_API
EV_API_DECL unsigned int ev_iteration (EV_P) EV_THROW; /* number of loop iterations */
EV_API_DECL unsigned int ev_depth (EV_P) EV_THROW; /* #ev_loop enters - #ev_loop leaves */
EV_API_DECL void ev_verify (EV_P) EV_THROW; /* abort if loop data corrupted */
EV_API_DECL void ev_set_io_collect_interval (EV_P_ ev_tstamp interval) EV_THROW; /* sleep at least this time, default 0 */
EV_API_DECL void ev_set_timeout_collect_interval (EV_P_ ev_tstamp interval) EV_THROW; /* sleep at least this time, default 0 */
/* advanced stuff for threading etc. support, see docs */
EV_API_DECL void ev_set_userdata (EV_P_ void *data) EV_THROW;
EV_API_DECL void *ev_userdata (EV_P) EV_THROW;
typedef void (*ev_loop_callback)(EV_P);
EV_API_DECL void ev_set_invoke_pending_cb (EV_P_ ev_loop_callback invoke_pending_cb) EV_THROW;
/* C++ doesn't allow the use of the ev_loop_callback typedef here, so we need to spell it out */
EV_API_DECL void ev_set_loop_release_cb (EV_P_ void (*release)(EV_P) EV_THROW, void (*acquire)(EV_P) EV_THROW) EV_THROW;
EV_API_DECL unsigned int ev_pending_count (EV_P) EV_THROW; /* number of pending events, if any */
EV_API_DECL void ev_invoke_pending (EV_P); /* invoke all pending watchers */
/*
* stop/start the timer handling.
*/
EV_API_DECL void ev_suspend (EV_P) EV_THROW;
EV_API_DECL void ev_resume (EV_P) EV_THROW;
#endif
#endif
/* these may evaluate ev multiple times, and the other arguments at most once */
/* either use ev_init + ev_TYPE_set, or the ev_TYPE_init macro, below, to first initialise a watcher */
#define ev_init(ev,cb_) do { \
((ev_watcher *)(void *)(ev))->active = \
((ev_watcher *)(void *)(ev))->pending = 0; \
ev_set_priority ((ev), 0); \
ev_set_cb ((ev), cb_); \
} while (0)
#define ev_io_set(ev,fd_,events_) do { (ev)->fd = (fd_); (ev)->events = (events_) | EV__IOFDSET; } while (0)
#define ev_timer_set(ev,after_,repeat_) do { ((ev_watcher_time *)(ev))->at = (after_); (ev)->repeat = (repeat_); } while (0)
#define ev_periodic_set(ev,ofs_,ival_,rcb_) do { (ev)->offset = (ofs_); (ev)->interval = (ival_); (ev)->reschedule_cb = (rcb_); } while (0)
#define ev_signal_set(ev,signum_) do { (ev)->signum = (signum_); } while (0)
#define ev_child_set(ev,pid_,trace_) do { (ev)->pid = (pid_); (ev)->flags = !!(trace_); } while (0)
#define ev_stat_set(ev,path_,interval_) do { (ev)->path = (path_); (ev)->interval = (interval_); (ev)->wd = -2; } while (0)
#define ev_idle_set(ev) /* nop, yes, this is a serious in-joke */
#define ev_prepare_set(ev) /* nop, yes, this is a serious in-joke */
#define ev_check_set(ev) /* nop, yes, this is a serious in-joke */
#define ev_embed_set(ev,other_) do { (ev)->other = (other_); } while (0)
#define ev_fork_set(ev) /* nop, yes, this is a serious in-joke */
#define ev_cleanup_set(ev) /* nop, yes, this is a serious in-joke */
#define ev_async_set(ev) /* nop, yes, this is a serious in-joke */
#define ev_io_init(ev,cb,fd,events) do { ev_init ((ev), (cb)); ev_io_set ((ev),(fd),(events)); } while (0)
#define ev_timer_init(ev,cb,after,repeat) do { ev_init ((ev), (cb)); ev_timer_set ((ev),(after),(repeat)); } while (0)
#define ev_periodic_init(ev,cb,ofs,ival,rcb) do { ev_init ((ev), (cb)); ev_periodic_set ((ev),(ofs),(ival),(rcb)); } while (0)
#define ev_signal_init(ev,cb,signum) do { ev_init ((ev), (cb)); ev_signal_set ((ev), (signum)); } while (0)
#define ev_child_init(ev,cb,pid,trace) do { ev_init ((ev), (cb)); ev_child_set ((ev),(pid),(trace)); } while (0)
#define ev_stat_init(ev,cb,path,interval) do { ev_init ((ev), (cb)); ev_stat_set ((ev),(path),(interval)); } while (0)
#define ev_idle_init(ev,cb) do { ev_init ((ev), (cb)); ev_idle_set ((ev)); } while (0)
#define ev_prepare_init(ev,cb) do { ev_init ((ev), (cb)); ev_prepare_set ((ev)); } while (0)
#define ev_check_init(ev,cb) do { ev_init ((ev), (cb)); ev_check_set ((ev)); } while (0)
#define ev_embed_init(ev,cb,other) do { ev_init ((ev), (cb)); ev_embed_set ((ev),(other)); } while (0)
#define ev_fork_init(ev,cb) do { ev_init ((ev), (cb)); ev_fork_set ((ev)); } while (0)
#define ev_cleanup_init(ev,cb) do { ev_init ((ev), (cb)); ev_cleanup_set ((ev)); } while (0)
#define ev_async_init(ev,cb) do { ev_init ((ev), (cb)); ev_async_set ((ev)); } while (0)
#define ev_is_pending(ev) (0 + ((ev_watcher *)(void *)(ev))->pending) /* ro, true when watcher is waiting for callback invocation */
#define ev_is_active(ev) (0 + ((ev_watcher *)(void *)(ev))->active) /* ro, true when the watcher has been started */
#define ev_cb_(ev) (ev)->cb /* rw */
#define ev_cb(ev) (memmove (&ev_cb_ (ev), &((ev_watcher *)(ev))->cb, sizeof (ev_cb_ (ev))), (ev)->cb)
#if EV_MINPRI == EV_MAXPRI
# define ev_priority(ev) ((ev), EV_MINPRI)
# define ev_set_priority(ev,pri) ((ev), (pri))
#else
# define ev_priority(ev) (+(((ev_watcher *)(void *)(ev))->priority))
# define ev_set_priority(ev,pri) ( (ev_watcher *)(void *)(ev))->priority = (pri)
#endif
#define ev_periodic_at(ev) (+((ev_watcher_time *)(ev))->at)
#ifndef ev_set_cb
# define ev_set_cb(ev,cb_) (ev_cb_ (ev) = (cb_), memmove (&((ev_watcher *)(ev))->cb, &ev_cb_ (ev), sizeof (ev_cb_ (ev))))
#endif
/* stopping (enabling, adding) a watcher does nothing if it is already running */
/* stopping (disabling, deleting) a watcher does nothing unless it's already running */
#if EV_PROTOTYPES
/* feeds an event into a watcher as if the event actually occurred */
/* accepts any ev_watcher type */
EV_API_DECL void ev_feed_event (EV_P_ void *w, int revents) EV_THROW;
EV_API_DECL void ev_feed_fd_event (EV_P_ int fd, int revents) EV_THROW;
#if EV_SIGNAL_ENABLE
EV_API_DECL void ev_feed_signal (int signum) EV_THROW;
EV_API_DECL void ev_feed_signal_event (EV_P_ int signum) EV_THROW;
#endif
EV_API_DECL void ev_invoke (EV_P_ void *w, int revents);
EV_API_DECL int ev_clear_pending (EV_P_ void *w) EV_THROW;
EV_API_DECL void ev_io_start (EV_P_ ev_io *w) EV_THROW;
EV_API_DECL void ev_io_stop (EV_P_ ev_io *w) EV_THROW;
EV_API_DECL void ev_timer_start (EV_P_ ev_timer *w) EV_THROW;
EV_API_DECL void ev_timer_stop (EV_P_ ev_timer *w) EV_THROW;
/* stops if active and no repeat, restarts if active and repeating, starts if inactive and repeating */
EV_API_DECL void ev_timer_again (EV_P_ ev_timer *w) EV_THROW;
/* return remaining time */
EV_API_DECL ev_tstamp ev_timer_remaining (EV_P_ ev_timer *w) EV_THROW;
#if EV_PERIODIC_ENABLE
EV_API_DECL void ev_periodic_start (EV_P_ ev_periodic *w) EV_THROW;
EV_API_DECL void ev_periodic_stop (EV_P_ ev_periodic *w) EV_THROW;
EV_API_DECL void ev_periodic_again (EV_P_ ev_periodic *w) EV_THROW;
#endif
/* only supported in the default loop */
#if EV_SIGNAL_ENABLE
EV_API_DECL void ev_signal_start (EV_P_ ev_signal *w) EV_THROW;
EV_API_DECL void ev_signal_stop (EV_P_ ev_signal *w) EV_THROW;
#endif
/* only supported in the default loop */
# if EV_CHILD_ENABLE
EV_API_DECL void ev_child_start (EV_P_ ev_child *w) EV_THROW;
EV_API_DECL void ev_child_stop (EV_P_ ev_child *w) EV_THROW;
# endif
# if EV_STAT_ENABLE
EV_API_DECL void ev_stat_start (EV_P_ ev_stat *w) EV_THROW;
EV_API_DECL void ev_stat_stop (EV_P_ ev_stat *w) EV_THROW;
EV_API_DECL void ev_stat_stat (EV_P_ ev_stat *w) EV_THROW;
# endif
# if EV_IDLE_ENABLE
EV_API_DECL void ev_idle_start (EV_P_ ev_idle *w) EV_THROW;
EV_API_DECL void ev_idle_stop (EV_P_ ev_idle *w) EV_THROW;
# endif
#if EV_PREPARE_ENABLE
EV_API_DECL void ev_prepare_start (EV_P_ ev_prepare *w) EV_THROW;
EV_API_DECL void ev_prepare_stop (EV_P_ ev_prepare *w) EV_THROW;
#endif
#if EV_CHECK_ENABLE
EV_API_DECL void ev_check_start (EV_P_ ev_check *w) EV_THROW;
EV_API_DECL void ev_check_stop (EV_P_ ev_check *w) EV_THROW;
#endif
# if EV_FORK_ENABLE
EV_API_DECL void ev_fork_start (EV_P_ ev_fork *w) EV_THROW;
EV_API_DECL void ev_fork_stop (EV_P_ ev_fork *w) EV_THROW;
# endif
# if EV_CLEANUP_ENABLE
EV_API_DECL void ev_cleanup_start (EV_P_ ev_cleanup *w) EV_THROW;
EV_API_DECL void ev_cleanup_stop (EV_P_ ev_cleanup *w) EV_THROW;
# endif
# if EV_EMBED_ENABLE
/* only supported when loop to be embedded is in fact embeddable */
EV_API_DECL void ev_embed_start (EV_P_ ev_embed *w) EV_THROW;
EV_API_DECL void ev_embed_stop (EV_P_ ev_embed *w) EV_THROW;
EV_API_DECL void ev_embed_sweep (EV_P_ ev_embed *w) EV_THROW;
# endif
# if EV_ASYNC_ENABLE
EV_API_DECL void ev_async_start (EV_P_ ev_async *w) EV_THROW;
EV_API_DECL void ev_async_stop (EV_P_ ev_async *w) EV_THROW;
EV_API_DECL void ev_async_send (EV_P_ ev_async *w) EV_THROW;
# endif
#if EV_COMPAT3
#define EVLOOP_NONBLOCK EVRUN_NOWAIT
#define EVLOOP_ONESHOT EVRUN_ONCE
#define EVUNLOOP_CANCEL EVBREAK_CANCEL
#define EVUNLOOP_ONE EVBREAK_ONE
#define EVUNLOOP_ALL EVBREAK_ALL
#if EV_PROTOTYPES
EV_INLINE void ev_loop (EV_P_ int flags) { ev_run (EV_A_ flags); }
EV_INLINE void ev_unloop (EV_P_ int how ) { ev_break (EV_A_ how ); }
EV_INLINE void ev_default_destroy (void) { ev_loop_destroy (EV_DEFAULT); }
EV_INLINE void ev_default_fork (void) { ev_loop_fork (EV_DEFAULT); }
#if EV_FEATURE_API
EV_INLINE unsigned int ev_loop_count (EV_P) { return ev_iteration (EV_A); }
EV_INLINE unsigned int ev_loop_depth (EV_P) { return ev_depth (EV_A); }
EV_INLINE void ev_loop_verify (EV_P) { ev_verify (EV_A); }
#endif
#endif
#else
typedef struct ev_loop ev_loop;
#endif
#endif
EV_CPP(})
#endif

5570
libev/ev.pod vendored Normal file

File diff suppressed because it is too large Load Diff

285
libev/ev_epoll.c vendored Normal file
View File

@ -0,0 +1,285 @@
/*
* libev epoll fd activity backend
*
* Copyright (c) 2007,2008,2009,2010,2011 Marc Alexander Lehmann <libev@schmorp.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modifica-
* tion, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-
* CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-
* CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-
* ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License ("GPL") version 2 or any later version,
* in which case the provisions of the GPL are applicable instead of
* the above. If you wish to allow the use of your version of this file
* only under the terms of the GPL and not to allow others to use your
* version of this file under the BSD license, indicate your decision
* by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete the
* provisions above, a recipient may use your version of this file under
* either the BSD or the GPL.
*/
/*
* general notes about epoll:
*
* a) epoll silently removes fds from the fd set. as nothing tells us
* that an fd has been removed otherwise, we have to continually
* "rearm" fds that we suspect *might* have changed (same
* problem with kqueue, but much less costly there).
* b) the fact that ADD != MOD creates a lot of extra syscalls due to a)
* and seems not to have any advantage.
* c) the inability to handle fork or file descriptors (think dup)
* limits the applicability over poll, so this is not a generic
* poll replacement.
* d) epoll doesn't work the same as select with many file descriptors
* (such as files). while not critical, no other advanced interface
* seems to share this (rather non-unixy) limitation.
* e) epoll claims to be embeddable, but in practise you never get
* a ready event for the epoll fd (broken: <=2.6.26, working: >=2.6.32).
* f) epoll_ctl returning EPERM means the fd is always ready.
*
* lots of "weird code" and complication handling in this file is due
* to these design problems with epoll, as we try very hard to avoid
* epoll_ctl syscalls for common usage patterns and handle the breakage
* ensuing from receiving events for closed and otherwise long gone
* file descriptors.
*/
#include <sys/epoll.h>
#define EV_EMASK_EPERM 0x80
static void
epoll_modify (EV_P_ int fd, int oev, int nev)
{
struct epoll_event ev;
unsigned char oldmask;
/*
* we handle EPOLL_CTL_DEL by ignoring it here
* on the assumption that the fd is gone anyways
* if that is wrong, we have to handle the spurious
* event in epoll_poll.
* if the fd is added again, we try to ADD it, and, if that
* fails, we assume it still has the same eventmask.
*/
if (!nev)
return;
oldmask = anfds [fd].emask;
anfds [fd].emask = nev;
/* store the generation counter in the upper 32 bits, the fd in the lower 32 bits */
ev.data.u64 = (uint64_t)(uint32_t)fd
| ((uint64_t)(uint32_t)++anfds [fd].egen << 32);
ev.events = (nev & EV_READ ? EPOLLIN : 0)
| (nev & EV_WRITE ? EPOLLOUT : 0);
if (expect_true (!epoll_ctl (backend_fd, oev && oldmask != nev ? EPOLL_CTL_MOD : EPOLL_CTL_ADD, fd, &ev)))
return;
if (expect_true (errno == ENOENT))
{
/* if ENOENT then the fd went away, so try to do the right thing */
if (!nev)
goto dec_egen;
if (!epoll_ctl (backend_fd, EPOLL_CTL_ADD, fd, &ev))
return;
}
else if (expect_true (errno == EEXIST))
{
/* EEXIST means we ignored a previous DEL, but the fd is still active */
/* if the kernel mask is the same as the new mask, we assume it hasn't changed */
if (oldmask == nev)
goto dec_egen;
if (!epoll_ctl (backend_fd, EPOLL_CTL_MOD, fd, &ev))
return;
}
else if (expect_true (errno == EPERM))
{
/* EPERM means the fd is always ready, but epoll is too snobbish */
/* to handle it, unlike select or poll. */
anfds [fd].emask = EV_EMASK_EPERM;
/* add fd to epoll_eperms, if not already inside */
if (!(oldmask & EV_EMASK_EPERM))
{
array_needsize (int, epoll_eperms, epoll_epermmax, epoll_epermcnt + 1, EMPTY2);
epoll_eperms [epoll_epermcnt++] = fd;
}
return;
}
fd_kill (EV_A_ fd);
dec_egen:
/* we didn't successfully call epoll_ctl, so decrement the generation counter again */
--anfds [fd].egen;
}
static void
epoll_poll (EV_P_ ev_tstamp timeout)
{
int i;
int eventcnt;
if (expect_false (epoll_epermcnt))
timeout = 0.;
/* epoll wait times cannot be larger than (LONG_MAX - 999UL) / HZ msecs, which is below */
/* the default libev max wait time, however. */
EV_RELEASE_CB;
eventcnt = epoll_wait (backend_fd, epoll_events, epoll_eventmax, timeout * 1e3);
EV_ACQUIRE_CB;
if (expect_false (eventcnt < 0))
{
if (errno != EINTR)
ev_syserr ("(libev) epoll_wait");
return;
}
for (i = 0; i < eventcnt; ++i)
{
struct epoll_event *ev = epoll_events + i;
int fd = (uint32_t)ev->data.u64; /* mask out the lower 32 bits */
int want = anfds [fd].events;
int got = (ev->events & (EPOLLOUT | EPOLLERR | EPOLLHUP) ? EV_WRITE : 0)
| (ev->events & (EPOLLIN | EPOLLERR | EPOLLHUP) ? EV_READ : 0);
/*
* check for spurious notification.
* this only finds spurious notifications on egen updates
* other spurious notifications will be found by epoll_ctl, below
* we assume that fd is always in range, as we never shrink the anfds array
*/
if (expect_false ((uint32_t)anfds [fd].egen != (uint32_t)(ev->data.u64 >> 32)))
{
/* recreate kernel state */
postfork |= 2;
continue;
}
if (expect_false (got & ~want))
{
anfds [fd].emask = want;
/*
* we received an event but are not interested in it, try mod or del
* this often happens because we optimistically do not unregister fds
* when we are no longer interested in them, but also when we get spurious
* notifications for fds from another process. this is partially handled
* above with the gencounter check (== our fd is not the event fd), and
* partially here, when epoll_ctl returns an error (== a child has the fd
* but we closed it).
*/
ev->events = (want & EV_READ ? EPOLLIN : 0)
| (want & EV_WRITE ? EPOLLOUT : 0);
/* pre-2.6.9 kernels require a non-null pointer with EPOLL_CTL_DEL, */
/* which is fortunately easy to do for us. */
if (epoll_ctl (backend_fd, want ? EPOLL_CTL_MOD : EPOLL_CTL_DEL, fd, ev))
{
postfork |= 2; /* an error occurred, recreate kernel state */
continue;
}
}
fd_event (EV_A_ fd, got);
}
/* if the receive array was full, increase its size */
if (expect_false (eventcnt == epoll_eventmax))
{
ev_free (epoll_events);
epoll_eventmax = array_nextsize (sizeof (struct epoll_event), epoll_eventmax, epoll_eventmax + 1);
epoll_events = (struct epoll_event *)ev_malloc (sizeof (struct epoll_event) * epoll_eventmax);
}
/* now synthesize events for all fds where epoll fails, while select works... */
for (i = epoll_epermcnt; i--; )
{
int fd = epoll_eperms [i];
unsigned char events = anfds [fd].events & (EV_READ | EV_WRITE);
if (anfds [fd].emask & EV_EMASK_EPERM && events)
fd_event (EV_A_ fd, events);
else
{
epoll_eperms [i] = epoll_eperms [--epoll_epermcnt];
anfds [fd].emask = 0;
}
}
}
inline_size
int
epoll_init (EV_P_ int flags)
{
#if defined EPOLL_CLOEXEC && !defined __ANDROID__
backend_fd = epoll_create1 (EPOLL_CLOEXEC);
if (backend_fd < 0 && (errno == EINVAL || errno == ENOSYS))
#endif
backend_fd = epoll_create (256);
if (backend_fd < 0)
return 0;
fcntl (backend_fd, F_SETFD, FD_CLOEXEC);
backend_mintime = 1e-3; /* epoll does sometimes return early, this is just to avoid the worst */
backend_modify = epoll_modify;
backend_poll = epoll_poll;
epoll_eventmax = 64; /* initial number of events receivable per poll */
epoll_events = (struct epoll_event *)ev_malloc (sizeof (struct epoll_event) * epoll_eventmax);
return EVBACKEND_EPOLL;
}
inline_size
void
epoll_destroy (EV_P)
{
ev_free (epoll_events);
array_free (epoll_eperm, EMPTY);
}
inline_size
void
epoll_fork (EV_P)
{
close (backend_fd);
while ((backend_fd = epoll_create (256)) < 0)
ev_syserr ("(libev) epoll_create");
fcntl (backend_fd, F_SETFD, FD_CLOEXEC);
fd_rearm_all (EV_A);
}

218
libev/ev_kqueue.c vendored Normal file
View File

@ -0,0 +1,218 @@
/*
* libev kqueue backend
*
* Copyright (c) 2007,2008,2009,2010,2011,2012,2013 Marc Alexander Lehmann <libev@schmorp.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modifica-
* tion, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-
* CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-
* CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-
* ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License ("GPL") version 2 or any later version,
* in which case the provisions of the GPL are applicable instead of
* the above. If you wish to allow the use of your version of this file
* only under the terms of the GPL and not to allow others to use your
* version of this file under the BSD license, indicate your decision
* by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete the
* provisions above, a recipient may use your version of this file under
* either the BSD or the GPL.
*/
#include <sys/types.h>
#include <sys/time.h>
#include <sys/event.h>
#include <string.h>
#include <errno.h>
inline_speed
void
kqueue_change (EV_P_ int fd, int filter, int flags, int fflags)
{
++kqueue_changecnt;
array_needsize (struct kevent, kqueue_changes, kqueue_changemax, kqueue_changecnt, EMPTY2);
EV_SET (&kqueue_changes [kqueue_changecnt - 1], fd, filter, flags, fflags, 0, 0);
}
/* OS X at least needs this */
#ifndef EV_ENABLE
# define EV_ENABLE 0
#endif
#ifndef NOTE_EOF
# define NOTE_EOF 0
#endif
static void
kqueue_modify (EV_P_ int fd, int oev, int nev)
{
if (oev != nev)
{
if (oev & EV_READ)
kqueue_change (EV_A_ fd, EVFILT_READ , EV_DELETE, 0);
if (oev & EV_WRITE)
kqueue_change (EV_A_ fd, EVFILT_WRITE, EV_DELETE, 0);
}
/* to detect close/reopen reliably, we have to re-add */
/* event requests even when oev == nev */
if (nev & EV_READ)
kqueue_change (EV_A_ fd, EVFILT_READ , EV_ADD | EV_ENABLE, NOTE_EOF);
if (nev & EV_WRITE)
kqueue_change (EV_A_ fd, EVFILT_WRITE, EV_ADD | EV_ENABLE, NOTE_EOF);
}
static void
kqueue_poll (EV_P_ ev_tstamp timeout)
{
int res, i;
struct timespec ts;
/* need to resize so there is enough space for errors */
if (kqueue_changecnt > kqueue_eventmax)
{
ev_free (kqueue_events);
kqueue_eventmax = array_nextsize (sizeof (struct kevent), kqueue_eventmax, kqueue_changecnt);
kqueue_events = (struct kevent *)ev_malloc (sizeof (struct kevent) * kqueue_eventmax);
}
EV_RELEASE_CB;
EV_TS_SET (ts, timeout);
res = kevent (backend_fd, kqueue_changes, kqueue_changecnt, kqueue_events, kqueue_eventmax, &ts);
EV_ACQUIRE_CB;
kqueue_changecnt = 0;
if (expect_false (res < 0))
{
if (errno != EINTR)
ev_syserr ("(libev) kevent");
return;
}
for (i = 0; i < res; ++i)
{
int fd = kqueue_events [i].ident;
if (expect_false (kqueue_events [i].flags & EV_ERROR))
{
int err = kqueue_events [i].data;
/* we are only interested in errors for fds that we are interested in :) */
if (anfds [fd].events)
{
if (err == ENOENT) /* resubmit changes on ENOENT */
kqueue_modify (EV_A_ fd, 0, anfds [fd].events);
else if (err == EBADF) /* on EBADF, we re-check the fd */
{
if (fd_valid (fd))
kqueue_modify (EV_A_ fd, 0, anfds [fd].events);
else
fd_kill (EV_A_ fd);
}
else /* on all other errors, we error out on the fd */
fd_kill (EV_A_ fd);
}
}
else
fd_event (
EV_A_
fd,
kqueue_events [i].filter == EVFILT_READ ? EV_READ
: kqueue_events [i].filter == EVFILT_WRITE ? EV_WRITE
: 0
);
}
if (expect_false (res == kqueue_eventmax))
{
ev_free (kqueue_events);
kqueue_eventmax = array_nextsize (sizeof (struct kevent), kqueue_eventmax, kqueue_eventmax + 1);
kqueue_events = (struct kevent *)ev_malloc (sizeof (struct kevent) * kqueue_eventmax);
}
}
inline_size
int
kqueue_init (EV_P_ int flags)
{
/* initialize the kernel queue */
kqueue_fd_pid = getpid ();
if ((backend_fd = kqueue ()) < 0)
return 0;
fcntl (backend_fd, F_SETFD, FD_CLOEXEC); /* not sure if necessary, hopefully doesn't hurt */
backend_mintime = 1e-9; /* apparently, they did the right thing in freebsd */
backend_modify = kqueue_modify;
backend_poll = kqueue_poll;
kqueue_eventmax = 64; /* initial number of events receivable per poll */
kqueue_events = (struct kevent *)ev_malloc (sizeof (struct kevent) * kqueue_eventmax);
kqueue_changes = 0;
kqueue_changemax = 0;
kqueue_changecnt = 0;
return EVBACKEND_KQUEUE;
}
inline_size
void
kqueue_destroy (EV_P)
{
ev_free (kqueue_events);
ev_free (kqueue_changes);
}
inline_size
void
kqueue_fork (EV_P)
{
/* some BSD kernels don't just destroy the kqueue itself,
* but also close the fd, which isn't documented, and
* impossible to support properly.
* we remember the pid of the kqueue call and only close
* the fd if the pid is still the same.
* this leaks fds on sane kernels, but BSD interfaces are
* notoriously buggy and rarely get fixed.
*/
pid_t newpid = getpid ();
if (newpid == kqueue_fd_pid)
close (backend_fd);
kqueue_fd_pid = newpid;
while ((backend_fd = kqueue ()) < 0)
ev_syserr ("(libev) kqueue");
fcntl (backend_fd, F_SETFD, FD_CLOEXEC);
/* re-register interest in fds */
fd_rearm_all (EV_A);
}
/* sys/event.h defines EV_ERROR */
#undef EV_ERROR

151
libev/ev_poll.c vendored Normal file
View File

@ -0,0 +1,151 @@
/*
* libev poll fd activity backend
*
* Copyright (c) 2007,2008,2009,2010,2011 Marc Alexander Lehmann <libev@schmorp.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modifica-
* tion, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-
* CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-
* CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-
* ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License ("GPL") version 2 or any later version,
* in which case the provisions of the GPL are applicable instead of
* the above. If you wish to allow the use of your version of this file
* only under the terms of the GPL and not to allow others to use your
* version of this file under the BSD license, indicate your decision
* by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete the
* provisions above, a recipient may use your version of this file under
* either the BSD or the GPL.
*/
#include <poll.h>
inline_size
void
pollidx_init (int *base, int count)
{
/* consider using memset (.., -1, ...), which is practically guaranteed
* to work on all systems implementing poll */
while (count--)
*base++ = -1;
}
static void
poll_modify (EV_P_ int fd, int oev, int nev)
{
int idx;
if (oev == nev)
return;
array_needsize (int, pollidxs, pollidxmax, fd + 1, pollidx_init);
idx = pollidxs [fd];
if (idx < 0) /* need to allocate a new pollfd */
{
pollidxs [fd] = idx = pollcnt++;
array_needsize (struct pollfd, polls, pollmax, pollcnt, EMPTY2);
polls [idx].fd = fd;
}
assert (polls [idx].fd == fd);
if (nev)
polls [idx].events =
(nev & EV_READ ? POLLIN : 0)
| (nev & EV_WRITE ? POLLOUT : 0);
else /* remove pollfd */
{
pollidxs [fd] = -1;
if (expect_true (idx < --pollcnt))
{
polls [idx] = polls [pollcnt];
pollidxs [polls [idx].fd] = idx;
}
}
}
static void
poll_poll (EV_P_ ev_tstamp timeout)
{
struct pollfd *p;
int res;
EV_RELEASE_CB;
res = poll (polls, pollcnt, timeout * 1e3);
EV_ACQUIRE_CB;
if (expect_false (res < 0))
{
if (errno == EBADF)
fd_ebadf (EV_A);
else if (errno == ENOMEM && !syserr_cb)
fd_enomem (EV_A);
else if (errno != EINTR)
ev_syserr ("(libev) poll");
}
else
for (p = polls; res; ++p)
{
assert (("libev: poll() returned illegal result, broken BSD kernel?", p < polls + pollcnt));
if (expect_false (p->revents)) /* this expect is debatable */
{
--res;
if (expect_false (p->revents & POLLNVAL))
fd_kill (EV_A_ p->fd);
else
fd_event (
EV_A_
p->fd,
(p->revents & (POLLOUT | POLLERR | POLLHUP) ? EV_WRITE : 0)
| (p->revents & (POLLIN | POLLERR | POLLHUP) ? EV_READ : 0)
);
}
}
}
inline_size
int
poll_init (EV_P_ int flags)
{
backend_mintime = 1e-3;
backend_modify = poll_modify;
backend_poll = poll_poll;
pollidxs = 0; pollidxmax = 0;
polls = 0; pollmax = 0; pollcnt = 0;
return EVBACKEND_POLL;
}
inline_size
void
poll_destroy (EV_P)
{
ev_free (pollidxs);
ev_free (polls);
}

189
libev/ev_port.c vendored Normal file
View File

@ -0,0 +1,189 @@
/*
* libev solaris event port backend
*
* Copyright (c) 2007,2008,2009,2010,2011 Marc Alexander Lehmann <libev@schmorp.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modifica-
* tion, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-
* CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-
* CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-
* ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License ("GPL") version 2 or any later version,
* in which case the provisions of the GPL are applicable instead of
* the above. If you wish to allow the use of your version of this file
* only under the terms of the GPL and not to allow others to use your
* version of this file under the BSD license, indicate your decision
* by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete the
* provisions above, a recipient may use your version of this file under
* either the BSD or the GPL.
*/
/* useful reading:
*
* http://bugs.opensolaris.org/view_bug.do?bug_id=6268715 (random results)
* http://bugs.opensolaris.org/view_bug.do?bug_id=6455223 (just totally broken)
* http://bugs.opensolaris.org/view_bug.do?bug_id=6873782 (manpage ETIME)
* http://bugs.opensolaris.org/view_bug.do?bug_id=6874410 (implementation ETIME)
* http://www.mail-archive.com/networking-discuss@opensolaris.org/msg11898.html ETIME vs. nget
* http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/lib/libc/port/gen/event_port.c (libc)
* http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/uts/common/fs/portfs/port.c#1325 (kernel)
*/
#include <sys/types.h>
#include <sys/time.h>
#include <poll.h>
#include <port.h>
#include <string.h>
#include <errno.h>
inline_speed
void
port_associate_and_check (EV_P_ int fd, int ev)
{
if (0 >
port_associate (
backend_fd, PORT_SOURCE_FD, fd,
(ev & EV_READ ? POLLIN : 0)
| (ev & EV_WRITE ? POLLOUT : 0),
0
)
)
{
if (errno == EBADFD)
fd_kill (EV_A_ fd);
else
ev_syserr ("(libev) port_associate");
}
}
static void
port_modify (EV_P_ int fd, int oev, int nev)
{
/* we need to reassociate no matter what, as closes are
* once more silently being discarded.
*/
if (!nev)
{
if (oev)
port_dissociate (backend_fd, PORT_SOURCE_FD, fd);
}
else
port_associate_and_check (EV_A_ fd, nev);
}
static void
port_poll (EV_P_ ev_tstamp timeout)
{
int res, i;
struct timespec ts;
uint_t nget = 1;
/* we initialise this to something we will skip in the loop, as */
/* port_getn can return with nget unchanged, but no indication */
/* whether it was the original value or has been updated :/ */
port_events [0].portev_source = 0;
EV_RELEASE_CB;
EV_TS_SET (ts, timeout);
res = port_getn (backend_fd, port_events, port_eventmax, &nget, &ts);
EV_ACQUIRE_CB;
/* port_getn may or may not set nget on error */
/* so we rely on port_events [0].portev_source not being updated */
if (res == -1 && errno != ETIME && errno != EINTR)
ev_syserr ("(libev) port_getn (see http://bugs.opensolaris.org/view_bug.do?bug_id=6268715, try LIBEV_FLAGS=3 env variable)");
for (i = 0; i < nget; ++i)
{
if (port_events [i].portev_source == PORT_SOURCE_FD)
{
int fd = port_events [i].portev_object;
fd_event (
EV_A_
fd,
(port_events [i].portev_events & (POLLOUT | POLLERR | POLLHUP) ? EV_WRITE : 0)
| (port_events [i].portev_events & (POLLIN | POLLERR | POLLHUP) ? EV_READ : 0)
);
fd_change (EV_A_ fd, EV__IOFDSET);
}
}
if (expect_false (nget == port_eventmax))
{
ev_free (port_events);
port_eventmax = array_nextsize (sizeof (port_event_t), port_eventmax, port_eventmax + 1);
port_events = (port_event_t *)ev_malloc (sizeof (port_event_t) * port_eventmax);
}
}
inline_size
int
port_init (EV_P_ int flags)
{
/* Initialize the kernel queue */
if ((backend_fd = port_create ()) < 0)
return 0;
assert (("libev: PORT_SOURCE_FD must not be zero", PORT_SOURCE_FD));
fcntl (backend_fd, F_SETFD, FD_CLOEXEC); /* not sure if necessary, hopefully doesn't hurt */
/* if my reading of the opensolaris kernel sources are correct, then
* opensolaris does something very stupid: it checks if the time has already
* elapsed and doesn't round up if that is the case,m otherwise it DOES round
* up. Since we can't know what the case is, we need to guess by using a
* "large enough" timeout. Normally, 1e-9 would be correct.
*/
backend_mintime = 1e-3; /* needed to compensate for port_getn returning early */
backend_modify = port_modify;
backend_poll = port_poll;
port_eventmax = 64; /* initial number of events receivable per poll */
port_events = (port_event_t *)ev_malloc (sizeof (port_event_t) * port_eventmax);
return EVBACKEND_PORT;
}
inline_size
void
port_destroy (EV_P)
{
ev_free (port_events);
}
inline_size
void
port_fork (EV_P)
{
close (backend_fd);
while ((backend_fd = port_create ()) < 0)
ev_syserr ("(libev) port");
fcntl (backend_fd, F_SETFD, FD_CLOEXEC);
/* re-register interest in fds */
fd_rearm_all (EV_A);
}

316
libev/ev_select.c vendored Normal file
View File

@ -0,0 +1,316 @@
/*
* libev select fd activity backend
*
* Copyright (c) 2007,2008,2009,2010,2011 Marc Alexander Lehmann <libev@schmorp.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modifica-
* tion, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-
* CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-
* CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-
* ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License ("GPL") version 2 or any later version,
* in which case the provisions of the GPL are applicable instead of
* the above. If you wish to allow the use of your version of this file
* only under the terms of the GPL and not to allow others to use your
* version of this file under the BSD license, indicate your decision
* by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete the
* provisions above, a recipient may use your version of this file under
* either the BSD or the GPL.
*/
#ifndef _WIN32
/* for unix systems */
# include <inttypes.h>
# ifndef __hpux
/* for REAL unix systems */
# include <sys/select.h>
# endif
#endif
#ifndef EV_SELECT_USE_FD_SET
# ifdef NFDBITS
# define EV_SELECT_USE_FD_SET 0
# else
# define EV_SELECT_USE_FD_SET 1
# endif
#endif
#if EV_SELECT_IS_WINSOCKET
# undef EV_SELECT_USE_FD_SET
# define EV_SELECT_USE_FD_SET 1
# undef NFDBITS
# define NFDBITS 0
#endif
#if !EV_SELECT_USE_FD_SET
# define NFDBYTES (NFDBITS / 8)
#endif
#include <string.h>
static void
select_modify (EV_P_ int fd, int oev, int nev)
{
if (oev == nev)
return;
{
#if EV_SELECT_USE_FD_SET
#if EV_SELECT_IS_WINSOCKET
SOCKET handle = anfds [fd].handle;
#else
int handle = fd;
#endif
assert (("libev: fd >= FD_SETSIZE passed to fd_set-based select backend", fd < FD_SETSIZE));
/* FD_SET is broken on windows (it adds the fd to a set twice or more,
* which eventually leads to overflows). Need to call it only on changes.
*/
#if EV_SELECT_IS_WINSOCKET
if ((oev ^ nev) & EV_READ)
#endif
if (nev & EV_READ)
FD_SET (handle, (fd_set *)vec_ri);
else
FD_CLR (handle, (fd_set *)vec_ri);
#if EV_SELECT_IS_WINSOCKET
if ((oev ^ nev) & EV_WRITE)
#endif
if (nev & EV_WRITE)
FD_SET (handle, (fd_set *)vec_wi);
else
FD_CLR (handle, (fd_set *)vec_wi);
#else
int word = fd / NFDBITS;
fd_mask mask = 1UL << (fd % NFDBITS);
if (expect_false (vec_max <= word))
{
int new_max = word + 1;
vec_ri = ev_realloc (vec_ri, new_max * NFDBYTES);
vec_ro = ev_realloc (vec_ro, new_max * NFDBYTES); /* could free/malloc */
vec_wi = ev_realloc (vec_wi, new_max * NFDBYTES);
vec_wo = ev_realloc (vec_wo, new_max * NFDBYTES); /* could free/malloc */
#ifdef _WIN32
vec_eo = ev_realloc (vec_eo, new_max * NFDBYTES); /* could free/malloc */
#endif
for (; vec_max < new_max; ++vec_max)
((fd_mask *)vec_ri) [vec_max] =
((fd_mask *)vec_wi) [vec_max] = 0;
}
((fd_mask *)vec_ri) [word] |= mask;
if (!(nev & EV_READ))
((fd_mask *)vec_ri) [word] &= ~mask;
((fd_mask *)vec_wi) [word] |= mask;
if (!(nev & EV_WRITE))
((fd_mask *)vec_wi) [word] &= ~mask;
#endif
}
}
static void
select_poll (EV_P_ ev_tstamp timeout)
{
struct timeval tv;
int res;
int fd_setsize;
EV_RELEASE_CB;
EV_TV_SET (tv, timeout);
#if EV_SELECT_USE_FD_SET
fd_setsize = sizeof (fd_set);
#else
fd_setsize = vec_max * NFDBYTES;
#endif
memcpy (vec_ro, vec_ri, fd_setsize);
memcpy (vec_wo, vec_wi, fd_setsize);
#ifdef _WIN32
/* pass in the write set as except set.
* the idea behind this is to work around a windows bug that causes
* errors to be reported as an exception and not by setting
* the writable bit. this is so uncontrollably lame.
*/
memcpy (vec_eo, vec_wi, fd_setsize);
res = select (vec_max * NFDBITS, (fd_set *)vec_ro, (fd_set *)vec_wo, (fd_set *)vec_eo, &tv);
#elif EV_SELECT_USE_FD_SET
fd_setsize = anfdmax < FD_SETSIZE ? anfdmax : FD_SETSIZE;
res = select (fd_setsize, (fd_set *)vec_ro, (fd_set *)vec_wo, 0, &tv);
#else
res = select (vec_max * NFDBITS, (fd_set *)vec_ro, (fd_set *)vec_wo, 0, &tv);
#endif
EV_ACQUIRE_CB;
if (expect_false (res < 0))
{
#if EV_SELECT_IS_WINSOCKET
errno = WSAGetLastError ();
#endif
#ifdef WSABASEERR
/* on windows, select returns incompatible error codes, fix this */
if (errno >= WSABASEERR && errno < WSABASEERR + 1000)
if (errno == WSAENOTSOCK)
errno = EBADF;
else
errno -= WSABASEERR;
#endif
#ifdef _WIN32
/* select on windows erroneously returns EINVAL when no fd sets have been
* provided (this is documented). what microsoft doesn't tell you that this bug
* exists even when the fd sets _are_ provided, so we have to check for this bug
* here and emulate by sleeping manually.
* we also get EINVAL when the timeout is invalid, but we ignore this case here
* and assume that EINVAL always means: you have to wait manually.
*/
if (errno == EINVAL)
{
if (timeout)
{
unsigned long ms = timeout * 1e3;
Sleep (ms ? ms : 1);
}
return;
}
#endif
if (errno == EBADF)
fd_ebadf (EV_A);
else if (errno == ENOMEM && !syserr_cb)
fd_enomem (EV_A);
else if (errno != EINTR)
ev_syserr ("(libev) select");
return;
}
#if EV_SELECT_USE_FD_SET
{
int fd;
for (fd = 0; fd < anfdmax; ++fd)
if (anfds [fd].events)
{
int events = 0;
#if EV_SELECT_IS_WINSOCKET
SOCKET handle = anfds [fd].handle;
#else
int handle = fd;
#endif
if (FD_ISSET (handle, (fd_set *)vec_ro)) events |= EV_READ;
if (FD_ISSET (handle, (fd_set *)vec_wo)) events |= EV_WRITE;
#ifdef _WIN32
if (FD_ISSET (handle, (fd_set *)vec_eo)) events |= EV_WRITE;
#endif
if (expect_true (events))
fd_event (EV_A_ fd, events);
}
}
#else
{
int word, bit;
for (word = vec_max; word--; )
{
fd_mask word_r = ((fd_mask *)vec_ro) [word];
fd_mask word_w = ((fd_mask *)vec_wo) [word];
#ifdef _WIN32
word_w |= ((fd_mask *)vec_eo) [word];
#endif
if (word_r || word_w)
for (bit = NFDBITS; bit--; )
{
fd_mask mask = 1UL << bit;
int events = 0;
events |= word_r & mask ? EV_READ : 0;
events |= word_w & mask ? EV_WRITE : 0;
if (expect_true (events))
fd_event (EV_A_ word * NFDBITS + bit, events);
}
}
}
#endif
}
inline_size
int
select_init (EV_P_ int flags)
{
backend_mintime = 1e-6;
backend_modify = select_modify;
backend_poll = select_poll;
#if EV_SELECT_USE_FD_SET
vec_ri = ev_malloc (sizeof (fd_set)); FD_ZERO ((fd_set *)vec_ri);
vec_ro = ev_malloc (sizeof (fd_set));
vec_wi = ev_malloc (sizeof (fd_set)); FD_ZERO ((fd_set *)vec_wi);
vec_wo = ev_malloc (sizeof (fd_set));
#ifdef _WIN32
vec_eo = ev_malloc (sizeof (fd_set));
#endif
#else
vec_max = 0;
vec_ri = 0;
vec_ro = 0;
vec_wi = 0;
vec_wo = 0;
#ifdef _WIN32
vec_eo = 0;
#endif
#endif
return EVBACKEND_SELECT;
}
inline_size
void
select_destroy (EV_P)
{
ev_free (vec_ri);
ev_free (vec_ro);
ev_free (vec_wi);
ev_free (vec_wo);
#ifdef _WIN32
ev_free (vec_eo);
#endif
}

204
libev/ev_vars.h vendored Normal file
View File

@ -0,0 +1,204 @@
/*
* loop member variable declarations
*
* Copyright (c) 2007,2008,2009,2010,2011,2012,2013 Marc Alexander Lehmann <libev@schmorp.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modifica-
* tion, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-
* CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-
* CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-
* ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License ("GPL") version 2 or any later version,
* in which case the provisions of the GPL are applicable instead of
* the above. If you wish to allow the use of your version of this file
* only under the terms of the GPL and not to allow others to use your
* version of this file under the BSD license, indicate your decision
* by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete the
* provisions above, a recipient may use your version of this file under
* either the BSD or the GPL.
*/
#define VARx(type,name) VAR(name, type name)
VARx(ev_tstamp, now_floor) /* last time we refreshed rt_time */
VARx(ev_tstamp, mn_now) /* monotonic clock "now" */
VARx(ev_tstamp, rtmn_diff) /* difference realtime - monotonic time */
/* for reverse feeding of events */
VARx(W *, rfeeds)
VARx(int, rfeedmax)
VARx(int, rfeedcnt)
VAR (pendings, ANPENDING *pendings [NUMPRI])
VAR (pendingmax, int pendingmax [NUMPRI])
VAR (pendingcnt, int pendingcnt [NUMPRI])
VARx(int, pendingpri) /* highest priority currently pending */
VARx(ev_prepare, pending_w) /* dummy pending watcher */
VARx(ev_tstamp, io_blocktime)
VARx(ev_tstamp, timeout_blocktime)
VARx(int, backend)
VARx(int, activecnt) /* total number of active events ("refcount") */
VARx(EV_ATOMIC_T, loop_done) /* signal by ev_break */
VARx(int, backend_fd)
VARx(ev_tstamp, backend_mintime) /* assumed typical timer resolution */
VAR (backend_modify, void (*backend_modify)(EV_P_ int fd, int oev, int nev))
VAR (backend_poll , void (*backend_poll)(EV_P_ ev_tstamp timeout))
VARx(ANFD *, anfds)
VARx(int, anfdmax)
VAR (evpipe, int evpipe [2])
VARx(ev_io, pipe_w)
VARx(EV_ATOMIC_T, pipe_write_wanted)
VARx(EV_ATOMIC_T, pipe_write_skipped)
#if !defined(_WIN32) || EV_GENWRAP
VARx(pid_t, curpid)
#endif
VARx(char, postfork) /* true if we need to recreate kernel state after fork */
#if EV_USE_SELECT || EV_GENWRAP
VARx(void *, vec_ri)
VARx(void *, vec_ro)
VARx(void *, vec_wi)
VARx(void *, vec_wo)
#if defined(_WIN32) || EV_GENWRAP
VARx(void *, vec_eo)
#endif
VARx(int, vec_max)
#endif
#if EV_USE_POLL || EV_GENWRAP
VARx(struct pollfd *, polls)
VARx(int, pollmax)
VARx(int, pollcnt)
VARx(int *, pollidxs) /* maps fds into structure indices */
VARx(int, pollidxmax)
#endif
#if EV_USE_EPOLL || EV_GENWRAP
VARx(struct epoll_event *, epoll_events)
VARx(int, epoll_eventmax)
VARx(int *, epoll_eperms)
VARx(int, epoll_epermcnt)
VARx(int, epoll_epermmax)
#endif
#if EV_USE_KQUEUE || EV_GENWRAP
VARx(pid_t, kqueue_fd_pid)
VARx(struct kevent *, kqueue_changes)
VARx(int, kqueue_changemax)
VARx(int, kqueue_changecnt)
VARx(struct kevent *, kqueue_events)
VARx(int, kqueue_eventmax)
#endif
#if EV_USE_PORT || EV_GENWRAP
VARx(struct port_event *, port_events)
VARx(int, port_eventmax)
#endif
#if EV_USE_IOCP || EV_GENWRAP
VARx(HANDLE, iocp)
#endif
VARx(int *, fdchanges)
VARx(int, fdchangemax)
VARx(int, fdchangecnt)
VARx(ANHE *, timers)
VARx(int, timermax)
VARx(int, timercnt)
#if EV_PERIODIC_ENABLE || EV_GENWRAP
VARx(ANHE *, periodics)
VARx(int, periodicmax)
VARx(int, periodiccnt)
#endif
#if EV_IDLE_ENABLE || EV_GENWRAP
VAR (idles, ev_idle **idles [NUMPRI])
VAR (idlemax, int idlemax [NUMPRI])
VAR (idlecnt, int idlecnt [NUMPRI])
#endif
VARx(int, idleall) /* total number */
VARx(struct ev_prepare **, prepares)
VARx(int, preparemax)
VARx(int, preparecnt)
VARx(struct ev_check **, checks)
VARx(int, checkmax)
VARx(int, checkcnt)
#if EV_FORK_ENABLE || EV_GENWRAP
VARx(struct ev_fork **, forks)
VARx(int, forkmax)
VARx(int, forkcnt)
#endif
#if EV_CLEANUP_ENABLE || EV_GENWRAP
VARx(struct ev_cleanup **, cleanups)
VARx(int, cleanupmax)
VARx(int, cleanupcnt)
#endif
#if EV_ASYNC_ENABLE || EV_GENWRAP
VARx(EV_ATOMIC_T, async_pending)
VARx(struct ev_async **, asyncs)
VARx(int, asyncmax)
VARx(int, asynccnt)
#endif
#if EV_USE_INOTIFY || EV_GENWRAP
VARx(int, fs_fd)
VARx(ev_io, fs_w)
VARx(char, fs_2625) /* whether we are running in linux 2.6.25 or newer */
VAR (fs_hash, ANFS fs_hash [EV_INOTIFY_HASHSIZE])
#endif
VARx(EV_ATOMIC_T, sig_pending)
#if EV_USE_SIGNALFD || EV_GENWRAP
VARx(int, sigfd)
VARx(ev_io, sigfd_w)
VARx(sigset_t, sigfd_set)
#endif
VARx(unsigned int, origflags) /* original loop flags */
#if EV_FEATURE_API || EV_GENWRAP
VARx(unsigned int, loop_count) /* total number of loop iterations/blocks */
VARx(unsigned int, loop_depth) /* #ev_run enters - #ev_run leaves */
VARx(void *, userdata)
/* C++ doesn't support the ev_loop_callback typedef here. stinks. */
VAR (release_cb, void (*release_cb)(EV_P) EV_THROW)
VAR (acquire_cb, void (*acquire_cb)(EV_P) EV_THROW)
VAR (invoke_cb , ev_loop_callback invoke_cb)
#endif
#undef VARx

162
libev/ev_win32.c vendored Normal file
View File

@ -0,0 +1,162 @@
/*
* libev win32 compatibility cruft (_not_ a backend)
*
* Copyright (c) 2007,2008,2009 Marc Alexander Lehmann <libev@schmorp.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modifica-
* tion, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-
* CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-
* CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-
* ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License ("GPL") version 2 or any later version,
* in which case the provisions of the GPL are applicable instead of
* the above. If you wish to allow the use of your version of this file
* only under the terms of the GPL and not to allow others to use your
* version of this file under the BSD license, indicate your decision
* by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete the
* provisions above, a recipient may use your version of this file under
* either the BSD or the GPL.
*/
#ifdef _WIN32
/* note: the comment below could not be substantiated, but what would I care */
/* MSDN says this is required to handle SIGFPE */
/* my wild guess would be that using something floating-pointy is required */
/* for the crt to do something about it */
volatile double SIGFPE_REQ = 0.0f;
static SOCKET
ev_tcp_socket (void)
{
#if EV_USE_WSASOCKET
return WSASocket (AF_INET, SOCK_STREAM, 0, 0, 0, 0);
#else
return socket (AF_INET, SOCK_STREAM, 0);
#endif
}
/* oh, the humanity! */
static int
ev_pipe (int filedes [2])
{
struct sockaddr_in addr = { 0 };
int addr_size = sizeof (addr);
struct sockaddr_in adr2;
int adr2_size = sizeof (adr2);
SOCKET listener;
SOCKET sock [2] = { -1, -1 };
if ((listener = ev_tcp_socket ()) == INVALID_SOCKET)
return -1;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
addr.sin_port = 0;
if (bind (listener, (struct sockaddr *)&addr, addr_size))
goto fail;
if (getsockname (listener, (struct sockaddr *)&addr, &addr_size))
goto fail;
if (listen (listener, 1))
goto fail;
if ((sock [0] = ev_tcp_socket ()) == INVALID_SOCKET)
goto fail;
if (connect (sock [0], (struct sockaddr *)&addr, addr_size))
goto fail;
/* TODO: returns INVALID_SOCKET on winsock accept, not < 0. fix it */
/* when convenient, probably by just removing error checking altogether? */
if ((sock [1] = accept (listener, 0, 0)) < 0)
goto fail;
/* windows vista returns fantasy port numbers for sockets:
* example for two interconnected tcp sockets:
*
* (Socket::unpack_sockaddr_in getsockname $sock0)[0] == 53364
* (Socket::unpack_sockaddr_in getpeername $sock0)[0] == 53363
* (Socket::unpack_sockaddr_in getsockname $sock1)[0] == 53363
* (Socket::unpack_sockaddr_in getpeername $sock1)[0] == 53365
*
* wow! tridirectional sockets!
*
* this way of checking ports seems to work:
*/
if (getpeername (sock [0], (struct sockaddr *)&addr, &addr_size))
goto fail;
if (getsockname (sock [1], (struct sockaddr *)&adr2, &adr2_size))
goto fail;
errno = WSAEINVAL;
if (addr_size != adr2_size
|| addr.sin_addr.s_addr != adr2.sin_addr.s_addr /* just to be sure, I mean, it's windows */
|| addr.sin_port != adr2.sin_port)
goto fail;
closesocket (listener);
#if EV_SELECT_IS_WINSOCKET
filedes [0] = EV_WIN32_HANDLE_TO_FD (sock [0]);
filedes [1] = EV_WIN32_HANDLE_TO_FD (sock [1]);
#else
/* when select isn't winsocket, we also expect socket, connect, accept etc.
* to work on fds */
filedes [0] = sock [0];
filedes [1] = sock [1];
#endif
return 0;
fail:
closesocket (listener);
if (sock [0] != INVALID_SOCKET) closesocket (sock [0]);
if (sock [1] != INVALID_SOCKET) closesocket (sock [1]);
return -1;
}
#undef pipe
#define pipe(filedes) ev_pipe (filedes)
#define EV_HAVE_EV_TIME 1
ev_tstamp
ev_time (void)
{
FILETIME ft;
ULARGE_INTEGER ui;
GetSystemTimeAsFileTime (&ft);
ui.u.LowPart = ft.dwLowDateTime;
ui.u.HighPart = ft.dwHighDateTime;
/* msvc cannot convert ulonglong to double... yes, it is that sucky */
return (LONGLONG)(ui.QuadPart - 116444736000000000) * 1e-7;
}
#endif

200
libev/ev_wrap.h vendored Normal file
View File

@ -0,0 +1,200 @@
/* DO NOT EDIT, automatically generated by update_ev_wrap */
#ifndef EV_WRAP_H
#define EV_WRAP_H
#define acquire_cb ((loop)->acquire_cb)
#define activecnt ((loop)->activecnt)
#define anfdmax ((loop)->anfdmax)
#define anfds ((loop)->anfds)
#define async_pending ((loop)->async_pending)
#define asynccnt ((loop)->asynccnt)
#define asyncmax ((loop)->asyncmax)
#define asyncs ((loop)->asyncs)
#define backend ((loop)->backend)
#define backend_fd ((loop)->backend_fd)
#define backend_mintime ((loop)->backend_mintime)
#define backend_modify ((loop)->backend_modify)
#define backend_poll ((loop)->backend_poll)
#define checkcnt ((loop)->checkcnt)
#define checkmax ((loop)->checkmax)
#define checks ((loop)->checks)
#define cleanupcnt ((loop)->cleanupcnt)
#define cleanupmax ((loop)->cleanupmax)
#define cleanups ((loop)->cleanups)
#define curpid ((loop)->curpid)
#define epoll_epermcnt ((loop)->epoll_epermcnt)
#define epoll_epermmax ((loop)->epoll_epermmax)
#define epoll_eperms ((loop)->epoll_eperms)
#define epoll_eventmax ((loop)->epoll_eventmax)
#define epoll_events ((loop)->epoll_events)
#define evpipe ((loop)->evpipe)
#define fdchangecnt ((loop)->fdchangecnt)
#define fdchangemax ((loop)->fdchangemax)
#define fdchanges ((loop)->fdchanges)
#define forkcnt ((loop)->forkcnt)
#define forkmax ((loop)->forkmax)
#define forks ((loop)->forks)
#define fs_2625 ((loop)->fs_2625)
#define fs_fd ((loop)->fs_fd)
#define fs_hash ((loop)->fs_hash)
#define fs_w ((loop)->fs_w)
#define idleall ((loop)->idleall)
#define idlecnt ((loop)->idlecnt)
#define idlemax ((loop)->idlemax)
#define idles ((loop)->idles)
#define invoke_cb ((loop)->invoke_cb)
#define io_blocktime ((loop)->io_blocktime)
#define iocp ((loop)->iocp)
#define kqueue_changecnt ((loop)->kqueue_changecnt)
#define kqueue_changemax ((loop)->kqueue_changemax)
#define kqueue_changes ((loop)->kqueue_changes)
#define kqueue_eventmax ((loop)->kqueue_eventmax)
#define kqueue_events ((loop)->kqueue_events)
#define kqueue_fd_pid ((loop)->kqueue_fd_pid)
#define loop_count ((loop)->loop_count)
#define loop_depth ((loop)->loop_depth)
#define loop_done ((loop)->loop_done)
#define mn_now ((loop)->mn_now)
#define now_floor ((loop)->now_floor)
#define origflags ((loop)->origflags)
#define pending_w ((loop)->pending_w)
#define pendingcnt ((loop)->pendingcnt)
#define pendingmax ((loop)->pendingmax)
#define pendingpri ((loop)->pendingpri)
#define pendings ((loop)->pendings)
#define periodiccnt ((loop)->periodiccnt)
#define periodicmax ((loop)->periodicmax)
#define periodics ((loop)->periodics)
#define pipe_w ((loop)->pipe_w)
#define pipe_write_skipped ((loop)->pipe_write_skipped)
#define pipe_write_wanted ((loop)->pipe_write_wanted)
#define pollcnt ((loop)->pollcnt)
#define pollidxmax ((loop)->pollidxmax)
#define pollidxs ((loop)->pollidxs)
#define pollmax ((loop)->pollmax)
#define polls ((loop)->polls)
#define port_eventmax ((loop)->port_eventmax)
#define port_events ((loop)->port_events)
#define postfork ((loop)->postfork)
#define preparecnt ((loop)->preparecnt)
#define preparemax ((loop)->preparemax)
#define prepares ((loop)->prepares)
#define release_cb ((loop)->release_cb)
#define rfeedcnt ((loop)->rfeedcnt)
#define rfeedmax ((loop)->rfeedmax)
#define rfeeds ((loop)->rfeeds)
#define rtmn_diff ((loop)->rtmn_diff)
#define sig_pending ((loop)->sig_pending)
#define sigfd ((loop)->sigfd)
#define sigfd_set ((loop)->sigfd_set)
#define sigfd_w ((loop)->sigfd_w)
#define timeout_blocktime ((loop)->timeout_blocktime)
#define timercnt ((loop)->timercnt)
#define timermax ((loop)->timermax)
#define timers ((loop)->timers)
#define userdata ((loop)->userdata)
#define vec_eo ((loop)->vec_eo)
#define vec_max ((loop)->vec_max)
#define vec_ri ((loop)->vec_ri)
#define vec_ro ((loop)->vec_ro)
#define vec_wi ((loop)->vec_wi)
#define vec_wo ((loop)->vec_wo)
#else
#undef EV_WRAP_H
#undef acquire_cb
#undef activecnt
#undef anfdmax
#undef anfds
#undef async_pending
#undef asynccnt
#undef asyncmax
#undef asyncs
#undef backend
#undef backend_fd
#undef backend_mintime
#undef backend_modify
#undef backend_poll
#undef checkcnt
#undef checkmax
#undef checks
#undef cleanupcnt
#undef cleanupmax
#undef cleanups
#undef curpid
#undef epoll_epermcnt
#undef epoll_epermmax
#undef epoll_eperms
#undef epoll_eventmax
#undef epoll_events
#undef evpipe
#undef fdchangecnt
#undef fdchangemax
#undef fdchanges
#undef forkcnt
#undef forkmax
#undef forks
#undef fs_2625
#undef fs_fd
#undef fs_hash
#undef fs_w
#undef idleall
#undef idlecnt
#undef idlemax
#undef idles
#undef invoke_cb
#undef io_blocktime
#undef iocp
#undef kqueue_changecnt
#undef kqueue_changemax
#undef kqueue_changes
#undef kqueue_eventmax
#undef kqueue_events
#undef kqueue_fd_pid
#undef loop_count
#undef loop_depth
#undef loop_done
#undef mn_now
#undef now_floor
#undef origflags
#undef pending_w
#undef pendingcnt
#undef pendingmax
#undef pendingpri
#undef pendings
#undef periodiccnt
#undef periodicmax
#undef periodics
#undef pipe_w
#undef pipe_write_skipped
#undef pipe_write_wanted
#undef pollcnt
#undef pollidxmax
#undef pollidxs
#undef pollmax
#undef polls
#undef port_eventmax
#undef port_events
#undef postfork
#undef preparecnt
#undef preparemax
#undef prepares
#undef release_cb
#undef rfeedcnt
#undef rfeedmax
#undef rfeeds
#undef rtmn_diff
#undef sig_pending
#undef sigfd
#undef sigfd_set
#undef sigfd_w
#undef timeout_blocktime
#undef timercnt
#undef timermax
#undef timers
#undef userdata
#undef vec_eo
#undef vec_max
#undef vec_ri
#undef vec_ro
#undef vec_wi
#undef vec_wo
#endif

425
libev/event.c vendored Normal file
View File

@ -0,0 +1,425 @@
/*
* libevent compatibility layer
*
* Copyright (c) 2007,2008,2009,2010,2012 Marc Alexander Lehmann <libev@schmorp.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modifica-
* tion, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-
* CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-
* CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-
* ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License ("GPL") version 2 or any later version,
* in which case the provisions of the GPL are applicable instead of
* the above. If you wish to allow the use of your version of this file
* only under the terms of the GPL and not to allow others to use your
* version of this file under the BSD license, indicate your decision
* by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete the
* provisions above, a recipient may use your version of this file under
* either the BSD or the GPL.
*/
#include <stddef.h>
#include <stdlib.h>
#include <assert.h>
#ifdef EV_EVENT_H
# include EV_EVENT_H
#else
# include "event.h"
#endif
#if EV_MULTIPLICITY
# define dLOOPev struct ev_loop *loop = (struct ev_loop *)ev->ev_base
# define dLOOPbase struct ev_loop *loop = (struct ev_loop *)base
#else
# define dLOOPev
# define dLOOPbase
#endif
/* never accessed, will always be cast from/to ev_loop */
struct event_base
{
int dummy;
};
static struct event_base *ev_x_cur;
static ev_tstamp
ev_tv_get (struct timeval *tv)
{
if (tv)
{
ev_tstamp after = tv->tv_sec + tv->tv_usec * 1e-6;
return after ? after : 1e-6;
}
else
return -1.;
}
#define EVENT_STRINGIFY(s) # s
#define EVENT_VERSION(a,b) EVENT_STRINGIFY (a) "." EVENT_STRINGIFY (b)
const char *
event_get_version (void)
{
/* returns ABI, not API or library, version */
return EVENT_VERSION (EV_VERSION_MAJOR, EV_VERSION_MINOR);
}
const char *
event_get_method (void)
{
return "libev";
}
void *event_init (void)
{
#if EV_MULTIPLICITY
if (ev_x_cur)
ev_x_cur = (struct event_base *)ev_loop_new (EVFLAG_AUTO);
else
ev_x_cur = (struct event_base *)ev_default_loop (EVFLAG_AUTO);
#else
assert (("libev: multiple event bases not supported when not compiled with EV_MULTIPLICITY", !ev_x_cur));
ev_x_cur = (struct event_base *)(long)ev_default_loop (EVFLAG_AUTO);
#endif
return ev_x_cur;
}
const char *
event_base_get_method (const struct event_base *base)
{
return "libev";
}
struct event_base *
event_base_new (void)
{
#if EV_MULTIPLICITY
return (struct event_base *)ev_loop_new (EVFLAG_AUTO);
#else
assert (("libev: multiple event bases not supported when not compiled with EV_MULTIPLICITY"));
return NULL;
#endif
}
void event_base_free (struct event_base *base)
{
dLOOPbase;
#if EV_MULTIPLICITY
if (!ev_is_default_loop (loop))
ev_loop_destroy (loop);
#endif
}
int event_dispatch (void)
{
return event_base_dispatch (ev_x_cur);
}
#ifdef EV_STANDALONE
void event_set_log_callback (event_log_cb cb)
{
/* nop */
}
#endif
int event_loop (int flags)
{
return event_base_loop (ev_x_cur, flags);
}
int event_loopexit (struct timeval *tv)
{
return event_base_loopexit (ev_x_cur, tv);
}
event_callback_fn event_get_callback
(const struct event *ev)
{
return ev->ev_callback;
}
static void
ev_x_cb (struct event *ev, int revents)
{
revents &= EV_READ | EV_WRITE | EV_TIMER | EV_SIGNAL;
ev->ev_res = revents;
ev->ev_callback (ev->ev_fd, (short)revents, ev->ev_arg);
}
static void
ev_x_cb_sig (EV_P_ struct ev_signal *w, int revents)
{
struct event *ev = (struct event *)(((char *)w) - offsetof (struct event, iosig.sig));
if (revents & EV_ERROR)
event_del (ev);
ev_x_cb (ev, revents);
}
static void
ev_x_cb_io (EV_P_ struct ev_io *w, int revents)
{
struct event *ev = (struct event *)(((char *)w) - offsetof (struct event, iosig.io));
if ((revents & EV_ERROR) || !(ev->ev_events & EV_PERSIST))
event_del (ev);
ev_x_cb (ev, revents);
}
static void
ev_x_cb_to (EV_P_ struct ev_timer *w, int revents)
{
struct event *ev = (struct event *)(((char *)w) - offsetof (struct event, to));
event_del (ev);
ev_x_cb (ev, revents);
}
void event_set (struct event *ev, int fd, short events, void (*cb)(int, short, void *), void *arg)
{
if (events & EV_SIGNAL)
ev_init (&ev->iosig.sig, ev_x_cb_sig);
else
ev_init (&ev->iosig.io, ev_x_cb_io);
ev_init (&ev->to, ev_x_cb_to);
ev->ev_base = ev_x_cur; /* not threadsafe, but it's how libevent works */
ev->ev_fd = fd;
ev->ev_events = events;
ev->ev_pri = 0;
ev->ev_callback = cb;
ev->ev_arg = arg;
ev->ev_res = 0;
ev->ev_flags = EVLIST_INIT;
}
int event_once (int fd, short events, void (*cb)(int, short, void *), void *arg, struct timeval *tv)
{
return event_base_once (ev_x_cur, fd, events, cb, arg, tv);
}
int event_add (struct event *ev, struct timeval *tv)
{
dLOOPev;
if (ev->ev_events & EV_SIGNAL)
{
if (!ev_is_active (&ev->iosig.sig))
{
ev_signal_set (&ev->iosig.sig, ev->ev_fd);
ev_signal_start (EV_A_ &ev->iosig.sig);
ev->ev_flags |= EVLIST_SIGNAL;
}
}
else if (ev->ev_events & (EV_READ | EV_WRITE))
{
if (!ev_is_active (&ev->iosig.io))
{
ev_io_set (&ev->iosig.io, ev->ev_fd, ev->ev_events & (EV_READ | EV_WRITE));
ev_io_start (EV_A_ &ev->iosig.io);
ev->ev_flags |= EVLIST_INSERTED;
}
}
if (tv)
{
ev->to.repeat = ev_tv_get (tv);
ev_timer_again (EV_A_ &ev->to);
ev->ev_flags |= EVLIST_TIMEOUT;
}
else
{
ev_timer_stop (EV_A_ &ev->to);
ev->ev_flags &= ~EVLIST_TIMEOUT;
}
ev->ev_flags |= EVLIST_ACTIVE;
return 0;
}
int event_del (struct event *ev)
{
dLOOPev;
if (ev->ev_events & EV_SIGNAL)
ev_signal_stop (EV_A_ &ev->iosig.sig);
else if (ev->ev_events & (EV_READ | EV_WRITE))
ev_io_stop (EV_A_ &ev->iosig.io);
if (ev_is_active (&ev->to))
ev_timer_stop (EV_A_ &ev->to);
ev->ev_flags = EVLIST_INIT;
return 0;
}
void event_active (struct event *ev, int res, short ncalls)
{
dLOOPev;
if (res & EV_TIMEOUT)
ev_feed_event (EV_A_ &ev->to, res & EV_TIMEOUT);
if (res & EV_SIGNAL)
ev_feed_event (EV_A_ &ev->iosig.sig, res & EV_SIGNAL);
if (res & (EV_READ | EV_WRITE))
ev_feed_event (EV_A_ &ev->iosig.io, res & (EV_READ | EV_WRITE));
}
int event_pending (struct event *ev, short events, struct timeval *tv)
{
short revents = 0;
dLOOPev;
if (ev->ev_events & EV_SIGNAL)
{
/* sig */
if (ev_is_active (&ev->iosig.sig) || ev_is_pending (&ev->iosig.sig))
revents |= EV_SIGNAL;
}
else if (ev->ev_events & (EV_READ | EV_WRITE))
{
/* io */
if (ev_is_active (&ev->iosig.io) || ev_is_pending (&ev->iosig.io))
revents |= ev->ev_events & (EV_READ | EV_WRITE);
}
if (ev->ev_events & EV_TIMEOUT || ev_is_active (&ev->to) || ev_is_pending (&ev->to))
{
revents |= EV_TIMEOUT;
if (tv)
{
ev_tstamp at = ev_now (EV_A);
tv->tv_sec = (long)at;
tv->tv_usec = (long)((at - (ev_tstamp)tv->tv_sec) * 1e6);
}
}
return events & revents;
}
int event_priority_init (int npri)
{
return event_base_priority_init (ev_x_cur, npri);
}
int event_priority_set (struct event *ev, int pri)
{
ev->ev_pri = pri;
return 0;
}
int event_base_set (struct event_base *base, struct event *ev)
{
ev->ev_base = base;
return 0;
}
int event_base_loop (struct event_base *base, int flags)
{
dLOOPbase;
return !ev_run (EV_A_ flags);
}
int event_base_dispatch (struct event_base *base)
{
return event_base_loop (base, 0);
}
static void
ev_x_loopexit_cb (int revents, void *base)
{
dLOOPbase;
ev_break (EV_A_ EVBREAK_ONE);
}
int event_base_loopexit (struct event_base *base, struct timeval *tv)
{
ev_tstamp after = ev_tv_get (tv);
dLOOPbase;
ev_once (EV_A_ -1, 0, after >= 0. ? after : 0., ev_x_loopexit_cb, (void *)base);
return 0;
}
struct ev_x_once
{
int fd;
void (*cb)(int, short, void *);
void *arg;
};
static void
ev_x_once_cb (int revents, void *arg)
{
struct ev_x_once *once = (struct ev_x_once *)arg;
once->cb (once->fd, (short)revents, once->arg);
free (once);
}
int event_base_once (struct event_base *base, int fd, short events, void (*cb)(int, short, void *), void *arg, struct timeval *tv)
{
struct ev_x_once *once = (struct ev_x_once *)malloc (sizeof (struct ev_x_once));
dLOOPbase;
if (!once)
return -1;
once->fd = fd;
once->cb = cb;
once->arg = arg;
ev_once (EV_A_ fd, events & (EV_READ | EV_WRITE), ev_tv_get (tv), ev_x_once_cb, (void *)once);
return 0;
}
int event_base_priority_init (struct event_base *base, int npri)
{
/*dLOOPbase;*/
return 0;
}

177
libev/event.h vendored Normal file
View File

@ -0,0 +1,177 @@
/*
* libevent compatibility header, only core events supported
*
* Copyright (c) 2007,2008,2010,2012 Marc Alexander Lehmann <libev@schmorp.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modifica-
* tion, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-
* CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-
* CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-
* ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License ("GPL") version 2 or any later version,
* in which case the provisions of the GPL are applicable instead of
* the above. If you wish to allow the use of your version of this file
* only under the terms of the GPL and not to allow others to use your
* version of this file under the BSD license, indicate your decision
* by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete the
* provisions above, a recipient may use your version of this file under
* either the BSD or the GPL.
*/
#ifndef EVENT_H_
#define EVENT_H_
#ifdef EV_H
# include EV_H
#else
# include "ev.h"
#endif
#ifndef EVLOOP_NONBLOCK
# define EVLOOP_NONBLOCK EVRUN_NOWAIT
#endif
#ifndef EVLOOP_ONESHOT
# define EVLOOP_ONESHOT EVRUN_ONCE
#endif
#ifndef EV_TIMEOUT
# define EV_TIMEOUT EV_TIMER
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* we need sys/time.h for struct timeval only */
#if !defined (WIN32) || defined (__MINGW32__)
# include <time.h> /* mingw seems to need this, for whatever reason */
# include <sys/time.h>
#endif
struct event_base;
#define EVLIST_TIMEOUT 0x01
#define EVLIST_INSERTED 0x02
#define EVLIST_SIGNAL 0x04
#define EVLIST_ACTIVE 0x08
#define EVLIST_INTERNAL 0x10
#define EVLIST_INIT 0x80
typedef void (*event_callback_fn)(int, short, void *);
struct event
{
/* libev watchers we map onto */
union {
struct ev_io io;
struct ev_signal sig;
} iosig;
struct ev_timer to;
/* compatibility slots */
struct event_base *ev_base;
event_callback_fn ev_callback;
void *ev_arg;
int ev_fd;
int ev_pri;
int ev_res;
int ev_flags;
short ev_events;
};
event_callback_fn event_get_callback (const struct event *ev);
#define EV_READ EV_READ
#define EV_WRITE EV_WRITE
#define EV_PERSIST 0x10
#define EV_ET 0x20 /* nop */
#define EVENT_SIGNAL(ev) ((int) (ev)->ev_fd)
#define EVENT_FD(ev) ((int) (ev)->ev_fd)
#define event_initialized(ev) ((ev)->ev_flags & EVLIST_INIT)
#define evtimer_add(ev,tv) event_add (ev, tv)
#define evtimer_set(ev,cb,data) event_set (ev, -1, 0, cb, data)
#define evtimer_del(ev) event_del (ev)
#define evtimer_pending(ev,tv) event_pending (ev, EV_TIMEOUT, tv)
#define evtimer_initialized(ev) event_initialized (ev)
#define timeout_add(ev,tv) evtimer_add (ev, tv)
#define timeout_set(ev,cb,data) evtimer_set (ev, cb, data)
#define timeout_del(ev) evtimer_del (ev)
#define timeout_pending(ev,tv) evtimer_pending (ev, tv)
#define timeout_initialized(ev) evtimer_initialized (ev)
#define signal_add(ev,tv) event_add (ev, tv)
#define signal_set(ev,sig,cb,data) event_set (ev, sig, EV_SIGNAL | EV_PERSIST, cb, data)
#define signal_del(ev) event_del (ev)
#define signal_pending(ev,tv) event_pending (ev, EV_SIGNAL, tv)
#define signal_initialized(ev) event_initialized (ev)
const char *event_get_version (void);
const char *event_get_method (void);
void *event_init (void);
void event_base_free (struct event_base *base);
#define EVLOOP_ONCE EVLOOP_ONESHOT
int event_loop (int);
int event_loopexit (struct timeval *tv);
int event_dispatch (void);
#define _EVENT_LOG_DEBUG 0
#define _EVENT_LOG_MSG 1
#define _EVENT_LOG_WARN 2
#define _EVENT_LOG_ERR 3
typedef void (*event_log_cb)(int severity, const char *msg);
void event_set_log_callback(event_log_cb cb);
void event_set (struct event *ev, int fd, short events, void (*cb)(int, short, void *), void *arg);
int event_once (int fd, short events, void (*cb)(int, short, void *), void *arg, struct timeval *tv);
int event_add (struct event *ev, struct timeval *tv);
int event_del (struct event *ev);
void event_active (struct event *ev, int res, short ncalls); /* ncalls is being ignored */
int event_pending (struct event *ev, short, struct timeval *tv);
int event_priority_init (int npri);
int event_priority_set (struct event *ev, int pri);
struct event_base *event_base_new (void);
const char *event_base_get_method (const struct event_base *);
int event_base_set (struct event_base *base, struct event *ev);
int event_base_loop (struct event_base *base, int);
int event_base_loopexit (struct event_base *base, struct timeval *tv);
int event_base_dispatch (struct event_base *base);
int event_base_once (struct event_base *base, int fd, short events, void (*cb)(int, short, void *), void *arg, struct timeval *tv);
int event_base_priority_init (struct event_base *base, int fd);
/* next line is different in the libevent+libev version */
/*libevent-include*/
#ifdef __cplusplus
}
#endif
#endif

226
libev/event_compat.h vendored Normal file
View File

@ -0,0 +1,226 @@
/*
* Copyright (c) 2000-2004 Niels Provos <provos@citi.umich.edu>
* Copyright (c) 2008 Marc Alexander Lehmann <libev@schmorp.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef __cplusplus
extern "C" {
#endif
#ifdef _WIN32
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# undef WIN32_LEAN_AND_MEAN
typedef unsigned char u_char;
typedef unsigned short u_short;
#else
# include <sys/types.h>
# include <sys/time.h>
# include <inttypes.h>
#endif
#include <stdarg.h>
/* Fix so that ppl dont have to run with <sys/queue.h> */
#ifndef TAILQ_ENTRY
#define _EVENT_DEFINED_TQENTRY
#define TAILQ_ENTRY(type) \
struct { \
struct type *tqe_next; /* next element */ \
struct type **tqe_prev; /* address of previous next element */ \
}
#endif /* !TAILQ_ENTRY */
#ifndef RB_ENTRY
#define _EVENT_DEFINED_RBENTRY
#define RB_ENTRY(type) \
struct { \
struct type *rbe_left; /* left element */ \
struct type *rbe_right; /* right element */ \
struct type *rbe_parent; /* parent element */ \
int rbe_color; /* node color */ \
}
#endif /* !RB_ENTRY */
/*
* Key-Value pairs. Can be used for HTTP headers but also for
* query argument parsing.
*/
struct evkeyval {
TAILQ_ENTRY(evkeyval) next;
char *key;
char *value;
};
#ifdef _EVENT_DEFINED_TQENTRY
#undef TAILQ_ENTRY
struct event_list;
struct evkeyvalq;
#undef _EVENT_DEFINED_TQENTRY
#else
TAILQ_HEAD (event_list, event);
TAILQ_HEAD (evkeyvalq, evkeyval);
#endif /* _EVENT_DEFINED_TQENTRY */
#ifdef _EVENT_DEFINED_RBENTRY
#undef RB_ENTRY
#undef _EVENT_DEFINED_RBENTRY
#endif /* _EVENT_DEFINED_RBENTRY */
struct eventop {
char *name;
void *(*init)(struct event_base *);
int (*add)(void *, struct event *);
int (*del)(void *, struct event *);
int (*recalc)(struct event_base *, void *, int);
int (*dispatch)(struct event_base *, void *, struct timeval *);
void (*dealloc)(struct event_base *, void *);
};
/* These functions deal with buffering input and output */
struct evbuffer {
u_char *buffer;
u_char *orig_buffer;
size_t misalign;
size_t totallen;
size_t off;
void (*cb)(struct evbuffer *, size_t, size_t, void *);
void *cbarg;
};
/* Just for error reporting - use other constants otherwise */
#define EVBUFFER_READ 0x01
#define EVBUFFER_WRITE 0x02
#define EVBUFFER_EOF 0x10
#define EVBUFFER_ERROR 0x20
#define EVBUFFER_TIMEOUT 0x40
struct bufferevent;
typedef void (*evbuffercb)(struct bufferevent *, void *);
typedef void (*everrorcb)(struct bufferevent *, short what, void *);
struct event_watermark {
size_t low;
size_t high;
};
struct bufferevent {
struct event ev_read;
struct event ev_write;
struct evbuffer *input;
struct evbuffer *output;
struct event_watermark wm_read;
struct event_watermark wm_write;
evbuffercb readcb;
evbuffercb writecb;
everrorcb errorcb;
void *cbarg;
int timeout_read; /* in seconds */
int timeout_write; /* in seconds */
short enabled; /* events that are currently enabled */
};
struct bufferevent *bufferevent_new(int fd,
evbuffercb readcb, evbuffercb writecb, everrorcb errorcb, void *cbarg);
int bufferevent_base_set(struct event_base *base, struct bufferevent *bufev);
int bufferevent_priority_set(struct bufferevent *bufev, int pri);
void bufferevent_free(struct bufferevent *bufev);
int bufferevent_write(struct bufferevent *bufev, const void *data, size_t size);
int bufferevent_write_buffer(struct bufferevent *bufev, struct evbuffer *buf);
size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size);
int bufferevent_enable(struct bufferevent *bufev, short event);
int bufferevent_disable(struct bufferevent *bufev, short event);
void bufferevent_settimeout(struct bufferevent *bufev,
int timeout_read, int timeout_write);
#define EVBUFFER_LENGTH(x) (x)->off
#define EVBUFFER_DATA(x) (x)->buffer
#define EVBUFFER_INPUT(x) (x)->input
#define EVBUFFER_OUTPUT(x) (x)->output
struct evbuffer *evbuffer_new(void);
void evbuffer_free(struct evbuffer *);
int evbuffer_expand(struct evbuffer *, size_t);
int evbuffer_add(struct evbuffer *, const void *, size_t);
int evbuffer_remove(struct evbuffer *, void *, size_t);
char *evbuffer_readline(struct evbuffer *);
int evbuffer_add_buffer(struct evbuffer *, struct evbuffer *);
int evbuffer_add_printf(struct evbuffer *, const char *fmt, ...);
int evbuffer_add_vprintf(struct evbuffer *, const char *fmt, va_list ap);
void evbuffer_drain(struct evbuffer *, size_t);
int evbuffer_write(struct evbuffer *, int);
int evbuffer_read(struct evbuffer *, int, int);
u_char *evbuffer_find(struct evbuffer *, const u_char *, size_t);
void evbuffer_setcb(struct evbuffer *, void (*)(struct evbuffer *, size_t, size_t, void *), void *);
/*
* Marshaling tagged data - We assume that all tags are inserted in their
* numeric order - so that unknown tags will always be higher than the
* known ones - and we can just ignore the end of an event buffer.
*/
void evtag_init(void);
void evtag_marshal(struct evbuffer *evbuf, uint32_t tag, const void *data,
uint32_t len);
void encode_int(struct evbuffer *evbuf, uint32_t number);
void evtag_marshal_int(struct evbuffer *evbuf, uint32_t tag, uint32_t integer);
void evtag_marshal_string(struct evbuffer *buf, uint32_t tag,
const char *string);
void evtag_marshal_timeval(struct evbuffer *evbuf, uint32_t tag,
struct timeval *tv);
int evtag_unmarshal(struct evbuffer *src, uint32_t *ptag, struct evbuffer *dst);
int evtag_peek(struct evbuffer *evbuf, uint32_t *ptag);
int evtag_peek_length(struct evbuffer *evbuf, uint32_t *plength);
int evtag_payload_length(struct evbuffer *evbuf, uint32_t *plength);
int evtag_consume(struct evbuffer *evbuf);
int evtag_unmarshal_int(struct evbuffer *evbuf, uint32_t need_tag,
uint32_t *pinteger);
int evtag_unmarshal_fixed(struct evbuffer *src, uint32_t need_tag, void *data,
size_t len);
int evtag_unmarshal_string(struct evbuffer *evbuf, uint32_t need_tag,
char **pstring);
int evtag_unmarshal_timeval(struct evbuffer *evbuf, uint32_t need_tag,
struct timeval *ptv);
#ifdef __cplusplus
}
#endif

131
libev/import_libevent vendored Executable file
View File

@ -0,0 +1,131 @@
#!/bin/sh
LE=../libevent-1.4.3-stable
if ! [ -e evbuffer.c ]; then
echo do not run this programm unless you know what you are doing
exit 1
fi
# this program combines libev and libevent into a single package
cvs update -AdP libev
rsync -avP libev/. . --exclude CVS
rm -f configure.ac
cp $LE/evdns.h .
perl -i -pe 's%^/.libevent-include./%#include "event_compat.h"%' event.h
perl -ne '
s/\s+char buf\[64\];/\tchar buf[96];/;
if (/#include "event.h"/) {
print "#ifndef EV_STANDALONE\n$_#endif\n";
next;
}
if (/#include "misc.h"/) {
print "#ifndef EV_STANDALONE\n$_#endif\n";
next;
}
if (/#include "(unistd.h|sys\/time.h)"/) {
print "#ifndef WIN32\n$_#endif\n";
next;
}
next if /#include "log.h"/;
print;
' <$LE/evdns.c >evdns.c
cp $LE/autogen.sh .
cp $LE/epoll_sub.c .
cp $LE/evbuffer.c .
cp $LE/buffer.c .
cp $LE/evhttp.h .
cp $LE/evutil.h .
cp $LE/evutil.c .
cp $LE/event-config.h .
cp $LE/event-internal.h .
cp $LE/evrpc.h .
cp $LE/evrpc.c .
cp $LE/evrpc-internal.h .
cp $LE/http.c .
cp $LE/event_tagging.c .
cp $LE/http-internal.h .
cp $LE/strlcpy-internal.h .
cp $LE/log.c .
cp $LE/log.h .
cp $LE/strlcpy.c .
rsync -a $LE/WIN32* $LE/sample $LE/test $LE/compat . --del
#rename 's/libevent/libev/' WIN32-Prj/lib*
cp $LE/aclocal.m4 .
#cp $LE/acconfig.h .
cp $LE/config.h.in .
cp $LE/event_rpcgen.py .
cp $LE/*.3 .
#perl -i -pe 's/libevent/libev/g' sample/Makefile.am
#perl -i -pe 's/libevent/libev/g' test/Makefile.am
perl -i -pe 's/#include <event.h>$/#include "event.h"/' test/*.c
perl -i -ne '
next if /"event-internal.h"/;
s/base\d?->sig.ev_signal_added/0/;
s/base\d?->sig.ev_signal_pair\[0\]/-1/;
s/base->sig.evsignal_caught/0/;
next if /^\ttest_signal_(dealloc|pipeloss|switchbase|assert|restore)\(\)/;
next if /^\ttest_simplesignal\(\)/; # non-default-loop
next if /^\ttest_immediatesignal\(\)/; # non-default-loop
next if /test_priorities\(\d\)/;
print;
' test/regress.c
perl -ne '
s/\bmin_heap.h\b//g;
s/\bsignal.c\b//g;
s/\bevport.c\b//g;
s/\bkqueue.c\b//g;
s/\bdevpoll.c\b//g;
s/\brtsig.c\b//g;
s/\bselect.c\b//g;
s/\bpoll.c\b//g;
s/\bepoll.c\b//g;
s/\bepoll_sub.c\b//g;
s/\bevent-internal.h\b//g;
s/\bevsignal.h\b//g;
s/^(man_MANS\s*=)/$1 ev.3 /;
s/^(EXTRA_DIST\s*=)/$1 libev.m4 ev.h ev_vars.h ev_wrap.h event_compat.h ev++.h ev_epoll.c ev_select.c ev_poll.c ev_kqueue.c ev_port.c ev_win32.c ev.3 ev.pod /;
s/^(include_HEADERS\s*=)/$1 ev.h event_compat.h ev++.h /;
s/^(CORE_SRC\s*=)/$1 ev.c /;
s/^(SYS_LIBS\s*=)/$1 -lm /;
#s/libevent/libev/g;
print;
' <$LE/Makefile.am >Makefile.am
perl -ne '
#s/-Wall/-Wall -Wno-comment -Wunused-function -Wno-unused-value/;
s/-Wall//g;
#s/libevent/libev/g;
#VERSION
s/AM_INIT_AUTOMAKE\s*\(.*,(.*)\)/AM_INIT_AUTOMAKE(libevent-$1+libev,3.1)/;
s/AC_LIBOBJ\(select\)/: ;/g;
s/AC_LIBOBJ\(poll\)/: ;/g;
s/AC_LIBOBJ\(kqueue\)/: ;/g;
s/AC_LIBOBJ\(epoll\)/: ;/g;
s/AC_LIBOBJ\(devpoll\)/: ;/g;
s/AC_LIBOBJ\(evport\)/: ;/g;
s/AC_LIBOBJ\(signal\)/: ;/g;
s/AC_LIBOBJ\(rtsig\)/: ;/g;
print "m4_include([libev.m4])\n" if /^AC_OUTPUT/;
print;
' <$LE/configure.in >configure.in
aclocal-1.7
automake-1.7 --add-missing
autoconf
autoheader
libtoolize
CC="ccache gcc" ./configure --prefix=/opt/libev --disable-shared "$@"

42
libev/libev.m4 vendored Normal file
View File

@ -0,0 +1,42 @@
dnl this file is part of libev, do not make local modifications
dnl http://software.schmorp.de/pkg/libev
dnl libev support
AC_CHECK_HEADERS(sys/inotify.h sys/epoll.h sys/event.h port.h poll.h sys/select.h sys/eventfd.h sys/signalfd.h)
AC_CHECK_FUNCS(inotify_init epoll_ctl kqueue port_create poll select eventfd signalfd)
AC_CHECK_FUNCS(clock_gettime, [], [
dnl on linux, try syscall wrapper first
if test $(uname) = Linux; then
AC_MSG_CHECKING(for clock_gettime syscall)
AC_LINK_IFELSE([AC_LANG_PROGRAM(
[#include <unistd.h>
#include <sys/syscall.h>
#include <time.h>],
[struct timespec ts; int status = syscall (SYS_clock_gettime, CLOCK_REALTIME, &ts)])],
[ac_have_clock_syscall=1
AC_DEFINE(HAVE_CLOCK_SYSCALL, 1, Define to 1 to use the syscall interface for clock_gettime)
AC_MSG_RESULT(yes)],
[AC_MSG_RESULT(no)])
fi
if test -z "$LIBEV_M4_AVOID_LIBRT" && test -z "$ac_have_clock_syscall"; then
AC_CHECK_LIB(rt, clock_gettime)
unset ac_cv_func_clock_gettime
AC_CHECK_FUNCS(clock_gettime)
fi
])
AC_CHECK_FUNCS(nanosleep, [], [
if test -z "$LIBEV_M4_AVOID_LIBRT"; then
AC_CHECK_LIB(rt, nanosleep)
unset ac_cv_func_nanosleep
AC_CHECK_FUNCS(nanosleep)
fi
])
if test -z "$LIBEV_M4_AVOID_LIBM"; then
LIBM=m
fi
AC_SEARCH_LIBS(floor, $LIBM, [AC_DEFINE(HAVE_FLOOR, 1, Define to 1 if the floor function is available)])

8
libev/update_ev_c vendored Executable file
View File

@ -0,0 +1,8 @@
#!/bin/sh -e
(
sed -ne '1,\%/\* ECB.H BEGIN \*/%p' ev.c
cat ~/src/libecb/ecb.h
sed -ne '\%/\* ECB.H END \*/%,$p' ev.c
) >ev.c~ && mv ev.c~ ev.c

19
libev/update_ev_wrap vendored Executable file
View File

@ -0,0 +1,19 @@
#!/bin/sh
(
echo '#define VAR(name,decl) name'
echo '#define EV_GENWRAP 1'
cat ev_vars.h
) | cc -E -o - - | perl -ne '
while (<>) {
push @syms, $1 if /(^\w+)/;
}
print "/* DO NOT EDIT, automatically generated by update_ev_wrap */\n",
"#ifndef EV_WRAP_H\n",
"#define EV_WRAP_H\n",
(map "#define $_ ((loop)->$_)\n", sort @syms),
"#else\n",
"#undef EV_WRAP_H\n",
(map "#undef $_\n", sort @syms),
"#endif\n";
' >ev_wrap.h

7
libev/update_symbols vendored Executable file
View File

@ -0,0 +1,7 @@
#!/bin/sh
make ev.o event.o || exit
nm ev.o | perl -ne 'print "$1\n" if /\S+ [A-Z] (\S+)/' > Symbols.ev
nm event.o | perl -ne 'print "$1\n" if /\S+ [A-Z] (\S+)/' > Symbols.event

88
log.cpp Executable file → Normal file
View File

@ -1,63 +1,57 @@
#include <common.h>
#include <log.h>
int log_level=log_info;
int log_level = log_info;
int enable_log_position=0;
int enable_log_color=1;
int enable_log_position = 0;
int enable_log_color = 1;
void log0(const char* file, const char* function, int line, int level, const char* str, ...) {
if (level > log_level) return;
if (level > log_trace || level < 0) return;
void log0(const char * file,const char * function,int line,int level,const char* str, ...) {
time_t timer;
char buffer[100];
struct tm* tm_info;
if(level>log_level) return ;
if(level>log_trace||level<0) return ;
time(&timer);
tm_info = localtime(&timer);
if (enable_log_color)
printf("%s", log_color[level]);
time_t timer;
char buffer[100];
struct tm* tm_info;
strftime(buffer, 100, "%Y-%m-%d %H:%M:%S", tm_info);
printf("[%s][%s]", buffer, log_text[level]);
time(&timer);
tm_info = localtime(&timer);
if (enable_log_position) printf("[%s,func:%s,line:%d]", file, function, line);
if(enable_log_color)
printf("%s",log_color[level]);
va_list vlist;
va_start(vlist, str);
vfprintf(stdout, str, vlist);
va_end(vlist);
if (enable_log_color)
printf("%s", RESET);
strftime(buffer, 100, "%Y-%m-%d %H:%M:%S", tm_info);
printf("[%s][%s]",buffer,log_text[level]);
// printf("\n");
// if(enable_log_color)
// printf(log_color[level]);
fflush(stdout);
if(enable_log_position)printf("[%s,func:%s,line:%d]",file,function,line);
va_list vlist;
va_start(vlist, str);
vfprintf(stdout, str, vlist);
va_end(vlist);
if(enable_log_color)
printf("%s",RESET);
//printf("\n");
//if(enable_log_color)
//printf(log_color[level]);
fflush(stdout);
if(log_level==log_fatal)
{
about_to_exit=1;
}
if (log_level == log_fatal) {
about_to_exit = 1;
}
}
void log_bare(int level,const char* str, ...)
{
if(level>log_level) return ;
if(level>log_trace||level<0) return ;
if(enable_log_color)
printf("%s",log_color[level]);
va_list vlist;
va_start(vlist, str);
vfprintf(stdout, str, vlist);
va_end(vlist);
if(enable_log_color)
printf("%s",RESET);
fflush(stdout);
void log_bare(int level, const char* str, ...) {
if (level > log_level) return;
if (level > log_trace || level < 0) return;
if (enable_log_color)
printf("%s", log_color[level]);
va_list vlist;
va_start(vlist, str);
vfprintf(stdout, str, vlist);
va_end(vlist);
if (enable_log_color)
printf("%s", RESET);
fflush(stdout);
}

98
log.h Executable file → Normal file
View File

@ -2,100 +2,54 @@
#ifndef _LOG_MYLOG_H_
#define _LOG_MYLOG_H_
#include<stdio.h>
#include<string.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<getopt.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <getopt.h>
#include <unistd.h>
#include<errno.h>
#include <fcntl.h>
//#include"aes.h"
#include <sys/epoll.h>
#include <sys/wait.h>
#include<map>
#include<string>
#include<vector>
#include <sys/socket.h> //for socket ofcourse
#include <sys/types.h>
#include <stdlib.h> //for exit(0);
#include <errno.h> //For errno - the error number
#include <netinet/tcp.h> //Provides declarations for tcp header
#include <netinet/udp.h>
#include <netinet/ip.h> //Provides declarations for ip header
#include <netinet/if_ether.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <byteswap.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <linux/if_ether.h>
#include <linux/filter.h>
#include <sys/time.h>
#include <errno.h>
#include <time.h>
#include <sys/timerfd.h>
#include <set>
//#include <encrypt.h>
#include <inttypes.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <stdarg.h>
using namespace std;
#define RED "\x1B[31m"
#define GRN "\x1B[32m"
#define YEL "\x1B[33m"
#define BLU "\x1B[34m"
#define MAG "\x1B[35m"
#define CYN "\x1B[36m"
#define WHT "\x1B[37m"
#define RED "\x1B[31m"
#define GRN "\x1B[32m"
#define YEL "\x1B[33m"
#define BLU "\x1B[34m"
#define MAG "\x1B[35m"
#define CYN "\x1B[36m"
#define WHT "\x1B[37m"
#define RESET "\x1B[0m"
const int log_never = 0;
const int log_fatal = 1;
const int log_error = 2;
const int log_warn = 3;
const int log_info = 4;
const int log_debug = 5;
const int log_trace = 6;
const int log_end = 7;
const int log_never=0;
const int log_fatal=1;
const int log_error=2;
const int log_warn=3;
const int log_info=4;
const int log_debug=5;
const int log_trace=6;
const int log_end=7;
const char log_text[][20]={"NEVER","FATAL","ERROR","WARN","INFO","DEBUG","TRACE",""};
const char log_color[][20]={RED,RED,RED,YEL,GRN,MAG,""};
const char log_text[][20] = {"NEVER", "FATAL", "ERROR", "WARN", "INFO", "DEBUG", "TRACE", ""};
const char log_color[][20] = {RED, RED, RED, YEL, GRN, MAG, ""};
extern int log_level;
extern int enable_log_position;
extern int enable_log_color;
#ifdef MY_DEBUG
#define mylog(__first_argu__dummy_abcde__,...) printf(__VA_ARGS__)
#define mylog(__first_argu__dummy_abcde__, ...) printf(__VA_ARGS__)
#else
#define mylog(...) log0(__FILE__,__FUNCTION__,__LINE__,__VA_ARGS__)
#define mylog(...) log0(__FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
#endif
//#define mylog(__first_argu__dummy_abcde__,...) {;}
void log0(const char * file,const char * function,int line,int level,const char* str, ...);
void log_bare(int level,const char* str, ...);
void log0(const char* file, const char* function, int line, int level, const char* str, ...);
void log_bare(int level, const char* str, ...);
#endif

231
main.cpp
View File

@ -13,117 +13,140 @@
#include "git_version.h"
using namespace std;
static void print_help() {
char git_version_buf[100] = {0};
strncpy(git_version_buf, gitversion, 10);
static void print_help()
{
char git_version_buf[100]={0};
strncpy(git_version_buf,gitversion,10);
printf("UDPspeeder V2\n");
printf("git version: %s ", git_version_buf);
printf("build date: %s %s\n", __DATE__, __TIME__);
printf("repository: https://github.com/wangyu-/UDPspeeder\n");
printf("\n");
printf("usage:\n");
printf(" run as client: ./this_program -c -l local_listen_ip:local_port -r server_ip:server_port [options]\n");
printf(" run as server: ./this_program -s -l server_listen_ip:server_port -r remote_ip:remote_port [options]\n");
printf("\n");
printf("common options, must be same on both sides:\n");
printf(" -k,--key <string> key for simple xor encryption. if not set, xor is disabled\n");
printf("UDPspeeder V2\n");
printf("git version: %s ",git_version_buf);
printf("build date: %s %s\n",__DATE__,__TIME__);
printf("repository: https://github.com/wangyu-/UDPspeeder\n");
printf("\n");
printf("usage:\n");
printf(" run as client: ./this_program -c -l local_listen_ip:local_port -r server_ip:server_port [options]\n");
printf(" run as server: ./this_program -s -l server_listen_ip:server_port -r remote_ip:remote_port [options]\n");
printf("\n");
printf("common options, must be same on both sides:\n");
printf(" -k,--key <string> key for simple xor encryption. if not set, xor is disabled\n");
printf("main options:\n");
printf(" -f,--fec x:y forward error correction, send y redundant packets for every x packets\n");
printf(" --timeout <number> how long could a packet be held in queue before doing fec, unit: ms, default: 8ms\n");
printf(" --report <number> turn on send/recv report, and set a period for reporting, unit: s\n");
printf("main options:\n");
printf(" -f,--fec x:y forward error correction, send y redundant packets for every x packets\n");
printf(" --timeout <number> how long could a packet be held in queue before doing fec, unit: ms, default: 8ms\n");
printf(" --report <number> turn on send/recv report, and set a period for reporting, unit: s\n");
printf("advanced options:\n");
printf(" --mode <number> fec-mode,available values: 0,1; mode 0(default) costs less bandwidth,no mtu problem.\n");
printf(" mode 1 usually introduces less latency, but you have to care about mtu.\n");
printf(" --mtu <number> mtu. for mode 0, the program will split packet to segment smaller than mtu value.\n");
printf(" for mode 1, no packet will be split, the program just check if the mtu is exceed.\n");
printf(" default value: 1250. you typically shouldnt change this value.\n");
printf(" -j,--jitter <number> simulated jitter. randomly delay first packet for 0~<number> ms, default value: 0.\n");
printf(" do not use if you dont know what it means.\n");
printf(" -i,--interval <number> scatter each fec group to a interval of <number> ms, to defend burst packet loss.\n");
printf(" default value: 0. do not use if you dont know what it means.\n");
printf(" -f,--fec x1:y1,x2:y2,.. similiar to -f/--fec above,fine-grained fec parameters,may help save bandwidth.\n");
printf(" example: \"-f 1:3,2:4,10:6,20:10\". check repo for details\n");
printf(" --random-drop <number> simulate packet loss, unit: 0.01%%. default value: 0.\n");
printf(" --disable-obscure <number> disable obscure, to save a bit bandwidth and cpu\n");
printf(" --disable-checksum <number> disable checksum to save a bit bandwdith and cpu\n");
// printf(" --disable-xor <number> disable xor\n");
printf("advanced options:\n");
printf(" --mode <number> fec-mode,available values: 0,1; mode 0(default) costs less bandwidth,no mtu problem.\n");
printf(" mode 1 usually introduces less latency, but you have to care about mtu.\n");
printf(" --mtu <number> mtu. for mode 0, the program will split packet to segment smaller than mtu value.\n");
printf(" for mode 1, no packet will be split, the program just check if the mtu is exceed.\n");
printf(" default value: 1250. you typically shouldnt change this value.\n");
printf("developer options:\n");
printf(" --fifo <string> use a fifo(named pipe) for sending commands to the running program, so that you\n");
printf(" can change fec encode parameters dynamically, check readme.md in repository for\n");
printf(" supported commands.\n");
printf(" -j ,--jitter jmin:jmax similiar to -j above, but create jitter randomly between jmin and jmax\n");
printf(" -i,--interval imin:imax similiar to -i above, but scatter randomly between imin and imax\n");
printf(" -q,--queue-len <number> fec queue len, only for mode 0, fec will be performed immediately after queue is full.\n");
printf(" default value: 200. \n");
printf(" -j,--jitter <number> simulated jitter. randomly delay first packet for 0~<number> ms, default value: 0.\n");
printf(" do not use if you dont know what it means.\n");
printf(" -i,--interval <number> scatter each fec group to a interval of <number> ms, to protect burst packet loss.\n");
printf(" default value: 0. do not use if you dont know what it means.\n");
printf(" --random-drop <number> simulate packet loss, unit: 0.01%%. default value: 0.\n");
printf(" --disable-obscure <number> disable obscure, to save a bit bandwidth and cpu.\n");
// printf(" --disable-xor <number> disable xor\n");
printf(" default value: 200. \n");
printf(" --decode-buf <number> size of buffer of fec decoder,unit: packet, default: 2000\n");
// printf(" --fix-latency <number> try to stabilize latency, only for mode 0\n");
printf(" --delay-capacity <number> max number of delayed packets, 0 means unlimited, default: 0\n");
printf(" --disable-fec <number> completely disable fec, turn the program into a normal udp tunnel\n");
printf(" --sock-buf <number> buf size for socket, >=10 and <=10240, unit: kbyte, default: 1024\n");
printf(" --out-addr ip:port force all output packets of '-r' end to go through this address, port 0 for random port.\n");
#ifdef __linux__
printf(" --out-interface <string> force all output packets of '-r' end to go through this interface.\n");
#endif
printf("log and help options:\n");
printf(" --log-level <number> 0: never 1: fatal 2: error 3: warn \n");
printf(" 4: info (default) 5: debug 6: trace\n");
printf(" --log-position enable file name, function name, line number in log\n");
printf(" --disable-color disable log color\n");
printf(" -h,--help print this help message\n");
printf("developer options:\n");
printf(" --fifo <string> use a fifo(named pipe) for sending commands to the running program, so that you\n");
printf(" can change fec encode parameters dynamically, check readme.md in repository for\n");
printf(" supported commands.\n");
printf(" -j ,--jitter jmin:jmax similiar to -j above, but create jitter randomly between jmin and jmax\n");
printf(" -i,--interval imin:imax similiar to -i above, but scatter randomly between imin and imax\n");
printf(" --decode-buf <number> size of buffer of fec decoder,u nit: packet, default: 2000\n");
printf(" --fix-latency <number> try to stabilize latency, only for mode 0\n");
printf(" --delay-capacity <number> max number of delayed packets\n");
printf(" --disable-fec <number> completely disable fec, turn the program into a normal udp tunnel\n");
printf(" --sock-buf <number> buf size for socket, >=10 and <=10240, unit: kbyte, default: 1024\n");
printf("log and help options:\n");
printf(" --log-level <number> 0: never 1: fatal 2: error 3: warn \n");
printf(" 4: info (default) 5: debug 6: trace\n");
printf(" --log-position enable file name, function name, line number in log\n");
printf(" --disable-color disable log color\n");
printf(" -h,--help print this help message\n");
//printf("common options,these options must be same on both side\n");
// printf("common options,these options must be same on both side\n");
}
int main(int argc, char *argv[])
{
//working_mode=tunnel_mode;
assert(sizeof(u64_t)==8);
assert(sizeof(i64_t)==8);
assert(sizeof(u32_t)==4);
assert(sizeof(i32_t)==4);
assert(sizeof(u16_t)==2);
assert(sizeof(i16_t)==2);
dup2(1, 2); //redirect stderr to stdout
int i, j, k;
if (argc == 1)
{
print_help();
myexit( -1);
}
for (i = 0; i < argc; i++)
{
if(strcmp(argv[i],"-h")==0||strcmp(argv[i],"--help")==0)
{
print_help();
myexit(0);
}
}
process_arg(argc,argv);
delay_manager.set_capacity(delay_capacity);
local_ip_uint32=inet_addr(local_ip);
remote_ip_uint32=inet_addr(remote_ip);
sub_net_uint32=inet_addr(sub_net);
if(strlen(tun_dev)==0)
{
sprintf(tun_dev,"tun%u",get_true_random_number()%1000);
}
if(client_or_server==client_mode)
{
tunnel_client_event_loop();
}
else
{
tunnel_server_event_loop();
}
return 0;
void sigpipe_cb(struct ev_loop *l, ev_signal *w, int revents) {
mylog(log_info, "got sigpipe, ignored");
}
void sigterm_cb(struct ev_loop *l, ev_signal *w, int revents) {
mylog(log_info, "got sigterm, exit");
myexit(0);
}
void sigint_cb(struct ev_loop *l, ev_signal *w, int revents) {
mylog(log_info, "got sigint, exit");
myexit(0);
}
int main(int argc, char *argv[]) {
working_mode = tunnel_mode;
init_ws();
// unit_test();
struct ev_loop *loop = ev_default_loop(0);
#if !defined(__MINGW32__)
ev_signal signal_watcher_sigpipe;
ev_signal_init(&signal_watcher_sigpipe, sigpipe_cb, SIGPIPE);
ev_signal_start(loop, &signal_watcher_sigpipe);
#else
enable_log_color = 0;
#endif
ev_signal signal_watcher_sigterm;
ev_signal_init(&signal_watcher_sigterm, sigterm_cb, SIGTERM);
ev_signal_start(loop, &signal_watcher_sigterm);
ev_signal signal_watcher_sigint;
ev_signal_init(&signal_watcher_sigint, sigint_cb, SIGINT);
ev_signal_start(loop, &signal_watcher_sigint);
assert(sizeof(u64_t) == 8);
assert(sizeof(i64_t) == 8);
assert(sizeof(u32_t) == 4);
assert(sizeof(i32_t) == 4);
assert(sizeof(u16_t) == 2);
assert(sizeof(i16_t) == 2);
dup2(1, 2); // redirect stderr to stdout
int i, j, k;
if (argc == 1) {
print_help();
myexit(-1);
}
for (i = 0; i < argc; i++) {
if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
print_help();
myexit(0);
}
}
process_arg(argc, argv);
delay_manager.set_capacity(delay_capacity);
if (strlen(tun_dev) == 0) {
sprintf(tun_dev, "tun%u", get_fake_random_number() % 1000);
}
if (program_mode == client_mode) {
tunnel_client_event_loop();
} else {
tunnel_server_event_loop();
}
return 0;
}

118
makefile
View File

@ -1,69 +1,119 @@
cc_cross=/home/wangyu/Desktop/arm-2014.05/bin/arm-none-linux-gnueabi-g++
cc_local=g++
#cc_mips34kc=/toolchains/OpenWrt-SDK-ar71xx-for-linux-x86_64-gcc-4.8-linaro_uClibc-0.9.33.2/staging_dir/toolchain-mips_34kc_gcc-4.8-linaro_uClibc-0.9.33.2/bin/mips-openwrt-linux-g++
cc_mips24kc_be=/toolchains/lede-sdk-17.01.2-ar71xx-generic_gcc-5.4.0_musl-1.1.16.Linux-x86_64/staging_dir/toolchain-mips_24kc_gcc-5.4.0_musl-1.1.16/bin/mips-openwrt-linux-musl-g++
cc_mips24kc_le=/toolchains/lede-sdk-17.01.2-ramips-mt7621_gcc-5.4.0_musl-1.1.16.Linux-x86_64/staging_dir/toolchain-mipsel_24kc_gcc-5.4.0_musl-1.1.16/bin/mipsel-openwrt-linux-musl-g++
#cc_arm= /toolchains/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabi/bin/arm-linux-gnueabi-g++ -march=armv6 -marm
cc_arm= /toolchains/arm-2014.05/bin/arm-none-linux-gnueabi-g++
cc_arm= /toolchains/lede-sdk-17.01.2-bcm53xx_gcc-5.4.0_musl-1.1.16_eabi.Linux-x86_64/staging_dir/toolchain-arm_cortex-a9_gcc-5.4.0_musl-1.1.16_eabi/bin/arm-openwrt-linux-c++
cc_mingw_cross=i686-w64-mingw32-g++-posix
cc_mac_cross=o64-clang++ -stdlib=libc++
cc_x86=/toolchains/lede-sdk-17.01.2-x86-generic_gcc-5.4.0_musl-1.1.16.Linux-x86_64/staging_dir/toolchain-i386_pentium4_gcc-5.4.0_musl-1.1.16/bin/i486-openwrt-linux-c++
cc_amd64=/toolchains/lede-sdk-17.01.2-x86-64_gcc-5.4.0_musl-1.1.16.Linux-x86_64/staging_dir/toolchain-x86_64_gcc-5.4.0_musl-1.1.16/bin/x86_64-openwrt-linux-c++
#cc_bcm2708=/home/wangyu/raspberry/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin/arm-linux-gnueabihf-g++
FLAGS= -std=c++11 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -Wno-missing-field-initializers -ggdb
SOURCES=main.cpp log.cpp common.cpp lib/fec.c lib/rs.c packet.cpp delay_manager.cpp fd_manager.cpp connection.cpp fec_manager.cpp misc.cpp tunnel.cpp
SOURCES0=main.cpp log.cpp common.cpp lib/fec.cpp lib/rs.cpp packet.cpp delay_manager.cpp fd_manager.cpp connection.cpp fec_manager.cpp misc.cpp tunnel_client.cpp tunnel_server.cpp
SOURCES=${SOURCES0} my_ev.cpp -isystem libev
NAME=speederv2
FLAGS= -std=c++11 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -Wno-missing-field-initializers ${OPT}
TARGETS=amd64 arm mips24kc_be x86 mips24kc_le
TAR=${NAME}_binaries.tar.gz `echo ${TARGETS}|sed -r 's/([^ ]+)/speederv2_\1/g'` version.txt
TAR=${NAME}_binaries.tar.gz `echo ${TARGETS}|sed -r 's/([^ ]+)/${NAME}_\1/g'` version.txt
export STAGING_DIR=/tmp/ #just for supress warning of staging_dir not define
# targets for nativei (non-cross) compile
all:git_version
rm -f ${NAME}
${cc_local} -o ${NAME} -I. ${SOURCES} ${FLAGS} -lrt -ggdb -static -O3
${cc_local} -o ${NAME} -I. ${SOURCES} ${FLAGS} -lrt -ggdb -static -O2
freebsd:git_version
rm -f ${NAME}
${cc_local} -o ${NAME} -I. ${SOURCES} ${FLAGS} -lrt -ggdb -static -O2
mingw:git_version
rm -f ${NAME}
${cc_local} -o ${NAME} -I. ${SOURCES} ${FLAGS} -ggdb -static -O2 -lws2_32
mingw_wepoll:git_version #to compile you need a pacthed version of libev with wepoll backend
rm -f ${NAME}
${cc_local} -o ${NAME} -I. ${SOURCES0} ${FLAGS} -ggdb -static -O2 -DNO_LIBEV_EMBED -D_WIN32 -lev -lws2_32
mac:git_version
rm -f ${NAME}
${cc_local} -o ${NAME} -I. ${SOURCES} ${FLAGS} -ggdb -O2
cygwin:git_version
rm -f ${NAME}
${cc_local} -o ${NAME} -I. ${SOURCES} ${FLAGS} -lrt -ggdb -static -O2 -D_GNU_SOURCE
#targes for general cross compile
cross:git_version
${cc_cross} -o ${NAME}_cross -I. ${SOURCES} ${FLAGS} -lrt -O2
cross2:git_version
${cc_cross} -o ${NAME}_cross -I. ${SOURCES} ${FLAGS} -lrt -static -lgcc_eh -O2
cross3:git_version
${cc_cross} -o ${NAME}_cross -I. ${SOURCES} ${FLAGS} -lrt -static -O2
#targets only for debug purpose
fast: git_version
rm -f ${NAME}
${cc_local} -o ${NAME} -I. ${SOURCES} ${FLAGS} -lrt -ggdb
debug: git_version
rm -f ${NAME}
${cc_local} -o ${NAME} -I. ${SOURCES} ${FLAGS} -lrt -Wformat-nonliteral -D MY_DEBUG
${cc_local} -o ${NAME} -I. ${SOURCES} ${FLAGS} -lrt -Wformat-nonliteral -D MY_DEBUG -ggdb
debug2: git_version
rm -f ${NAME}
${cc_local} -o ${NAME} -I. ${SOURCES} ${FLAGS} -lrt -Wformat-nonliteral -ggdb
mips24kc_be: git_version
${cc_mips24kc_be} -o ${NAME}_$@ -I. ${SOURCES} ${FLAGS} -lrt -lgcc_eh -static -O3
#targets only for 'make release'
mips24kc_be_debug: git_version
${cc_mips24kc_be} -o ${NAME}_$@ -I. ${SOURCES} ${FLAGS} -lrt -lgcc_eh -static -ggdb
mips24kc_be: git_version
${cc_mips24kc_be} -o ${NAME}_$@ -I. ${SOURCES} ${FLAGS} -lrt -lgcc_eh -static -O2
mips24kc_le: git_version
${cc_mips24kc_le} -o ${NAME}_$@ -I. ${SOURCES} ${FLAGS} -lrt -lgcc_eh -static -O3
${cc_mips24kc_le} -o ${NAME}_$@ -I. ${SOURCES} ${FLAGS} -lrt -lgcc_eh -static -O2
amd64:git_version
${cc_local} -o ${NAME}_$@ -I. ${SOURCES} ${FLAGS} -lrt -static -O3
amd64_debug:git_version
${cc_local} -o ${NAME}_$@ -I. ${SOURCES} ${FLAGS} -lrt -static -ggdb
x86:git_version
${cc_local} -o ${NAME}_$@ -I. ${SOURCES} ${FLAGS} -lrt -static -O3 -m32
${cc_amd64} -o ${NAME}_$@ -I. ${SOURCES} ${FLAGS} -lrt -static -O2 -lgcc_eh -ggdb
x86:git_version #to build this you need 'g++-multilib' installed
${cc_x86} -o ${NAME}_$@ -I. ${SOURCES} ${FLAGS} -lrt -static -O2 -lgcc_eh -ggdb
arm:git_version
${cc_arm} -o ${NAME}_$@ -I. ${SOURCES} ${FLAGS} -lrt -static -O3
${cc_arm} -o ${NAME}_$@ -I. ${SOURCES} ${FLAGS} -lrt -static -O2 -lgcc_eh
arm_debug:git_version
${cc_arm} -o ${NAME}_$@ -I. ${SOURCES} ${FLAGS} -lrt -static -ggdb
cross:git_version
${cc_cross} -o ${NAME}_cross -I. ${SOURCES} ${FLAGS} -lrt -O3
cross2:git_version
${cc_cross} -o ${NAME}_cross -I. ${SOURCES} ${FLAGS} -lrt -static -lgcc_eh -O3
cross3:git_version
${cc_cross} -o ${NAME}_cross -I. ${SOURCES} ${FLAGS} -lrt -static -O3
release: ${TARGETS}
release: ${TARGETS}
cp git_version.h version.txt
tar -zcvf ${TAR}
#targets for cross compile windows targets on linux
mingw_cross:git_version #to build this and the below one you need 'mingw-w64' installed (the cross compile version on linux)
${cc_mingw_cross} -o ${NAME}.exe -I. ${SOURCES} ${FLAGS} -ggdb -static -O2 -lws2_32
mingw_cross_wepoll:git_version #to compile you need a pacthed version of libev with wepoll backend installed
${cc_mingw_cross} -o ${NAME}_wepoll.exe -I. ${SOURCES0} ${FLAGS} -ggdb -static -O2 -DNO_LIBEV_EMBED -D_WIN32 -lev -lws2_32
#targets for cross compile macos targets on linux
mac_cross:git_version #need to install 'osxcross' first.
${cc_mac_cross} -o ${NAME}_mac -I. ${SOURCES} ${FLAGS} -ggdb -O2
#release2 includes all binary in 'release' plus win and mac cross compile targets
release2: ${TARGETS} mingw_cross mingw_cross_wepoll mac_cross
cp git_version.h version.txt
tar -zcvf ${TAR} ${NAME}.exe ${NAME}_wepoll.exe ${NAME}_mac
clean:
rm -f ${TAR}
rm -f speeder speeder_cross
rm -f ${NAME} ${NAME}_cross ${NAME}.exe ${NAME}_wepoll.exe ${NAME}_mac
rm -f git_version.h
git_version:
echo "const char * const gitversion = \"$(shell git rev-parse HEAD)\";" > git_version.h
echo "const char *gitversion = \"$(shell git rev-parse HEAD)\";" > git_version.h

1603
misc.cpp

File diff suppressed because it is too large Load Diff

25
misc.h
View File

@ -14,18 +14,16 @@
#include "delay_manager.h"
#include "fec_manager.h"
extern char fifo_file[1000];
extern int mtu_warn;
extern int disable_mtu_warn;
extern int disable_fec;
extern int disable_checksum;
extern int debug_force_flush_fec;
extern int jitter_min;
extern int jitter_max;
@ -34,9 +32,14 @@ extern int output_interval_max;
extern int fix_latency;
extern u32_t local_ip_uint32,remote_ip_uint32;
extern char local_ip[100], remote_ip[100];
extern int local_port, remote_port;
// extern u32_t local_ip_uint32,remote_ip_uint32;
// extern char local_ip[100], remote_ip[100];
// extern int local_port, remote_port;
extern address_t local_addr, remote_addr;
extern address_t *out_addr;
extern char *out_interface;
extern conn_manager_t conn_manager;
extern delay_manager_t delay_manager;
@ -52,17 +55,19 @@ extern int tun_mtu;
extern int mssfix;
extern int manual_set_tun;
extern int persist_tun;
int from_normal_to_fec(conn_info_t & conn_info,char *data,int len,int & out_n,char **&out_arr,int *&out_len,my_time_t *&out_delay);
int from_fec_to_normal(conn_info_t & conn_info,char *data,int len,int & out_n,char **&out_arr,int *&out_len,my_time_t *&out_delay);
int from_normal_to_fec(conn_info_t &conn_info, char *data, int len, int &out_n, char **&out_arr, int *&out_len, my_time_t *&out_delay);
int from_fec_to_normal(conn_info_t &conn_info, char *data, int len, int &out_n, char **&out_arr, int *&out_len, my_time_t *&out_delay);
int delay_send(my_time_t delay,const dest_t &dest,char *data,int len);
int delay_send(my_time_t delay, const dest_t &dest, char *data, int len);
int print_parameter();
int handle_command(char *s);
int unit_test();
//void print_help();
// void print_help();
void process_arg(int argc, char *argv[]);

16
my_ev.cpp Normal file
View File

@ -0,0 +1,16 @@
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wextra"
#pragma GCC diagnostic ignored "-Wsign-compare"
#pragma GCC diagnostic ignored "-Wcomment"
#pragma GCC diagnostic ignored "-Wparentheses"
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
#pragma GCC diagnostic ignored "-Wunused-value"
#pragma GCC diagnostic ignored "-Wall"
#pragma GCC diagnostic ignored "-W"
#include "my_ev_common.h"
#include "ev.c"
#pragma GCC diagnostic pop

4
my_ev.h Normal file
View File

@ -0,0 +1,4 @@
#pragma once
#include "my_ev_common.h"
#include "ev.h"

15
my_ev_common.h Normal file
View File

@ -0,0 +1,15 @@
#define EV_STANDALONE 1
#define EV_COMMON \
void *data; \
unsigned long long u64;
#define EV_COMPAT3 0
//#define EV_VERIFY 2
#if defined(__MINGW32__)
#define EV_FD_TO_WIN32_HANDLE(fd) (fd)
#define EV_WIN32_HANDLE_TO_FD(handle) (handle)
#define EV_WIN32_CLOSE_FD(fd) closesocket(fd)
#define FD_SETSIZE 4096
#endif

View File

@ -5,364 +5,333 @@
* Author: root
*/
#include "common.h"
#include "log.h"
#include "packet.h"
#include "misc.h"
int iv_min=4;
int iv_max=32;//< 256;
u64_t packet_send_count=0;
u64_t dup_packet_send_count=0;
u64_t packet_recv_count=0;
u64_t dup_packet_recv_count=0;
int iv_min = 4;
int iv_max = 32; //< 256;
u64_t packet_send_count = 0;
u64_t dup_packet_send_count = 0;
u64_t packet_recv_count = 0;
u64_t dup_packet_recv_count = 0;
typedef u64_t anti_replay_seq_t;
int disable_replay_filter=0;
int disable_replay_filter = 0;
int disable_obscure=0;
int disable_xor=0;
int disable_obscure = 0;
int disable_xor = 0;
int random_drop=0;
int random_drop = 0;
char key_string[1000]= "";
char key_string[1000] = "";
//int local_listen_fd=-1;
// int local_listen_fd=-1;
void encrypt_0(char * input,int &len,char *key)
{
int i,j;
if(key[0]==0) return;
for(i=0,j=0;i<len;i++,j++)
{
if(key[j]==0)j=0;
input[i]^=key[j];
}
void encrypt_0(char *input, int &len, char *key) {
int i, j;
if (key[0] == 0) return;
for (i = 0, j = 0; i < len; i++, j++) {
if (key[j] == 0) j = 0;
input[i] ^= key[j];
}
}
void decrypt_0(char * input,int &len,char *key)
{
int i,j;
if(key[0]==0) return;
for(i=0,j=0;i<len;i++,j++)
{
if(key[j]==0)j=0;
input[i]^=key[j];
}
void decrypt_0(char *input, int &len, char *key) {
int i, j;
if (key[0] == 0) return;
for (i = 0, j = 0; i < len; i++, j++) {
if (key[j] == 0) j = 0;
input[i] ^= key[j];
}
}
int do_obscure_old(const char * input, int in_len,char *output,int &out_len)
{
//memcpy(output,input,in_len);
// out_len=in_len;
//return 0;
int do_obscure_old(const char *input, int in_len, char *output, int &out_len) {
// memcpy(output,input,in_len);
// out_len=in_len;
// return 0;
int i, j, k;
if (in_len > 65535||in_len<0)
return -1;
int iv_len=iv_min+rand()%(iv_max-iv_min);
get_true_random_chars(output,iv_len);
memcpy(output+iv_len,input,in_len);
int i, j, k;
if (in_len > 65535 || in_len < 0)
return -1;
int iv_len = iv_min + rand() % (iv_max - iv_min);
get_fake_random_chars(output, iv_len);
memcpy(output + iv_len, input, in_len);
output[iv_len+in_len]=(uint8_t)iv_len;
output[iv_len + in_len] = (uint8_t)iv_len;
output[iv_len+in_len]^=output[0];
output[iv_len+in_len]^=key_string[0];
output[iv_len + in_len] ^= output[0];
output[iv_len + in_len] ^= key_string[0];
for(i=0,j=0,k=1;i<in_len;i++,j++,k++)
{
if(j==iv_len) j=0;
if(key_string[k]==0)k=0;
output[iv_len+i]^=output[j];
output[iv_len+i]^=key_string[k];
}
for (i = 0, j = 0, k = 1; i < in_len; i++, j++, k++) {
if (j == iv_len) j = 0;
if (key_string[k] == 0) k = 0;
output[iv_len + i] ^= output[j];
output[iv_len + i] ^= key_string[k];
}
out_len=iv_len+in_len+1;
return 0;
out_len = iv_len + in_len + 1;
return 0;
}
int do_obscure(char * data,int &len)
{
assert(len>=0);
assert(len<buf_len);
int do_obscure(char *data, int &len) {
assert(len >= 0);
assert(len < buf_len);
int iv_len=random_between(iv_min,iv_max);
get_true_random_chars(data+len,iv_len);
data[iv_len+len]=(uint8_t)iv_len;
for(int i=0,j=0;i<len;i++,j++)
{
if(j==iv_len)j=0;
data[i]^=data[len+j];
}
int iv_len = random_between(iv_min, iv_max);
get_fake_random_chars(data + len, iv_len);
data[iv_len + len] = (uint8_t)iv_len;
for (int i = 0, j = 0; i < len; i++, j++) {
if (j == iv_len) j = 0;
data[i] ^= data[len + j];
}
len=len+iv_len+1;
return 0;
len = len + iv_len + 1;
return 0;
}
int de_obscure(char * data,int &len)
{
if(len<1) return -1;
int iv_len=int ((uint8_t) data[len-1]);
int de_obscure(char *data, int &len) {
if (len < 1) return -1;
int iv_len = int((uint8_t)data[len - 1]);
if(len<1+iv_len) return -1;
if (len < 1 + iv_len) return -1;
len=len-1-iv_len;
for(int i=0,j=0;i<len;i++,j++)
{
if(j==iv_len)j=0;
data[i]^=data[len+j];
}
len = len - 1 - iv_len;
for (int i = 0, j = 0; i < len; i++, j++) {
if (j == iv_len) j = 0;
data[i] ^= data[len + j];
}
return 0;
return 0;
}
int de_obscure_old(const char * input, int in_len,char *output,int &out_len)
{
//memcpy(output,input,in_len);
//out_len=in_len;
//return 0;
int de_obscure_old(const char *input, int in_len, char *output, int &out_len) {
// memcpy(output,input,in_len);
// out_len=in_len;
// return 0;
int i, j, k;
if (in_len > 65535||in_len<0)
{
mylog(log_debug,"in_len > 65535||in_len<0 , %d",in_len);
return -1;
}
int iv_len= int ((uint8_t)(input[in_len-1]^input[0]^key_string[0]) );
out_len=in_len-1-iv_len;
if(out_len<0)
{
mylog(log_debug,"%d %d\n",in_len,out_len);
return -1;
}
for(i=0,j=0,k=1;i<in_len;i++,j++,k++)
{
if(j==iv_len) j=0;
if(key_string[k]==0)k=0;
output[i]=input[iv_len+i]^input[j]^key_string[k];
}
dup_packet_recv_count++;
return 0;
int i, j, k;
if (in_len > 65535 || in_len < 0) {
mylog(log_debug, "in_len > 65535||in_len<0 , %d", in_len);
return -1;
}
int iv_len = int((uint8_t)(input[in_len - 1] ^ input[0] ^ key_string[0]));
out_len = in_len - 1 - iv_len;
if (out_len < 0) {
mylog(log_debug, "%d %d\n", in_len, out_len);
return -1;
}
for (i = 0, j = 0, k = 1; i < in_len; i++, j++, k++) {
if (j == iv_len) j = 0;
if (key_string[k] == 0) k = 0;
output[i] = input[iv_len + i] ^ input[j] ^ key_string[k];
}
dup_packet_recv_count++;
return 0;
}
/*
int sendto_fd_ip_port (int fd,u32_t ip,int port,char * buf, int len,int flags)
{
sockaddr_in tmp_sockaddr;
sockaddr_in tmp_sockaddr;
memset(&tmp_sockaddr,0,sizeof(tmp_sockaddr));
tmp_sockaddr.sin_family = AF_INET;
tmp_sockaddr.sin_addr.s_addr = ip;
tmp_sockaddr.sin_port = htons(uint16_t(port));
memset(&tmp_sockaddr,0,sizeof(tmp_sockaddr));
tmp_sockaddr.sin_family = AF_INET;
tmp_sockaddr.sin_addr.s_addr = ip;
tmp_sockaddr.sin_port = htons(uint16_t(port));
return sendto(fd, buf,
len , 0,
(struct sockaddr *) &tmp_sockaddr,
sizeof(tmp_sockaddr));
return sendto(fd, buf,
len , 0,
(struct sockaddr *) &tmp_sockaddr,
sizeof(tmp_sockaddr));
}*/
int sendto_fd_addr(int fd, address_t addr, char *buf, int len, int flags) {
return sendto(fd, buf,
len, 0,
(struct sockaddr *)&addr.inner,
addr.get_len());
}
/*
int sendto_ip_port (u32_t ip,int port,char * buf, int len,int flags)
{
return sendto_fd_ip_port(local_listen_fd,ip,port,buf,len,flags);
return sendto_fd_ip_port(local_listen_fd,ip,port,buf,len,flags);
}*/
int send_fd (int fd,char * buf, int len,int flags)
{
return send(fd,buf,len,flags);
int send_fd(int fd, char *buf, int len, int flags) {
return send(fd, buf, len, flags);
}
int my_send(const dest_t &dest,char *data,int len)
{
if(dest.cook)
{
do_cook(data,len);
}
switch(dest.type)
{
case type_fd_ip_port:
{
return sendto_fd_ip_port(dest.inner.fd,dest.inner.fd_ip_port.ip_port.ip,dest.inner.fd_ip_port.ip_port.port,data,len,0);
break;
}
case type_fd64_ip_port:
{
if(!fd_manager.exist(dest.inner.fd64)) return -1;
int fd=fd_manager.to_fd(dest.inner.fd64);
int my_send(const dest_t &dest, char *data, int len) {
if (dest.cook) {
do_cook(data, len);
}
switch (dest.type) {
case type_fd_addr: {
return sendto_fd_addr(dest.inner.fd, dest.inner.fd_addr.addr, data, len, 0);
break;
}
case type_fd64_addr: {
if (!fd_manager.exist(dest.inner.fd64)) return -1;
int fd = fd_manager.to_fd(dest.inner.fd64);
return sendto_fd_ip_port(fd,dest.inner.fd64_ip_port.ip_port.ip,dest.inner.fd64_ip_port.ip_port.port,data,len,0);
break;
}
case type_fd:
{
return send_fd(dest.inner.fd,data,len,0);
break;
}
case type_write_fd:
{
return write(dest.inner.fd,data,len);
break;
}
case type_fd64:
{
return sendto_fd_addr(fd, dest.inner.fd64_addr.addr, data, len, 0);
break;
}
case type_fd: {
return send_fd(dest.inner.fd, data, len, 0);
break;
}
case type_write_fd: {
return write(dest.inner.fd, data, len);
break;
}
case type_fd64: {
if (!fd_manager.exist(dest.inner.fd64)) return -1;
int fd = fd_manager.to_fd(dest.inner.fd64);
if(!fd_manager.exist(dest.inner.fd64)) return -1;
int fd=fd_manager.to_fd(dest.inner.fd64);
return send_fd(fd, data, len, 0);
break;
}
/*
case type_fd64_ip_port_conv:
{
if(!fd_manager.exist(dest.inner.fd64)) return -1;
int fd=fd_manager.to_fd(dest.inner.fd64);
return send_fd(fd,data,len,0);
break;
}
/*
case type_fd64_ip_port_conv:
{
if(!fd_manager.exist(dest.inner.fd64)) return -1;
int fd=fd_manager.to_fd(dest.inner.fd64);
char *new_data;
int new_len;
char *new_data;
int new_len;
put_conv(dest.conv,data,len,new_data,new_len);
return sendto_fd_ip_port(fd,dest.inner.fd64_ip_port.ip_port.ip,dest.inner.fd64_ip_port.ip_port.port,new_data,new_len,0);
break;
}*/
put_conv(dest.conv,data,len,new_data,new_len);
return sendto_fd_ip_port(fd,dest.inner.fd64_ip_port.ip_port.ip,dest.inner.fd64_ip_port.ip_port.port,new_data,new_len,0);
break;
}*/
/*
case type_fd64_conv:
{
char *new_data;
int new_len;
put_conv(dest.conv,data,len,new_data,new_len);
/*
case type_fd64_conv:
{
char *new_data;
int new_len;
put_conv(dest.conv,data,len,new_data,new_len);
if(!fd_manager.exist(dest.inner.fd64)) return -1;
int fd=fd_manager.to_fd(dest.inner.fd64);
return send_fd(fd,new_data,new_len,0);
}*/
/*
case type_fd:
{
send_fd(dest.inner.fd,data,len,0);
break;
}*/
default:
assert(0==1);
}
return 0;
if(!fd_manager.exist(dest.inner.fd64)) return -1;
int fd=fd_manager.to_fd(dest.inner.fd64);
return send_fd(fd,new_data,new_len,0);
}*/
/*
case type_fd:
{
send_fd(dest.inner.fd,data,len,0);
break;
}*/
default:
assert(0 == 1);
}
return 0;
}
/*
* this function comes from http://www.hackersdelight.org/hdcodetxt/crc.c.txt
*/
unsigned int crc32h(unsigned char *message,int len) {
assert(len>=0);
int i, crc;
unsigned int byte, c;
const unsigned int g0 = 0xEDB88320, g1 = g0>>1,
g2 = g0>>2, g3 = g0>>3, g4 = g0>>4, g5 = g0>>5,
g6 = (g0>>6)^g0, g7 = ((g0>>6)^g0)>>1;
unsigned int crc32h(unsigned char *message, int len) {
assert(len >= 0);
int i, crc;
unsigned int byte, c;
const unsigned int g0 = 0xEDB88320, g1 = g0 >> 1,
g2 = g0 >> 2, g3 = g0 >> 3, g4 = g0 >> 4, g5 = g0 >> 5,
g6 = (g0 >> 6) ^ g0, g7 = ((g0 >> 6) ^ g0) >> 1;
i = 0;
crc = 0xFFFFFFFF;
while (i!=len) { // Get next byte.
byte = message[i];
crc = crc ^ byte;
c = ((crc<<31>>31) & g7) ^ ((crc<<30>>31) & g6) ^
((crc<<29>>31) & g5) ^ ((crc<<28>>31) & g4) ^
((crc<<27>>31) & g3) ^ ((crc<<26>>31) & g2) ^
((crc<<25>>31) & g1) ^ ((crc<<24>>31) & g0);
crc = ((unsigned)crc >> 8) ^ c;
i = i + 1;
}
return ~crc;
i = 0;
crc = 0xFFFFFFFF;
while (i != len) { // Get next byte.
byte = message[i];
crc = crc ^ byte;
c = ((crc << 31 >> 31) & g7) ^ ((crc << 30 >> 31) & g6) ^
((crc << 29 >> 31) & g5) ^ ((crc << 28 >> 31) & g4) ^
((crc << 27 >> 31) & g3) ^ ((crc << 26 >> 31) & g2) ^
((crc << 25 >> 31) & g1) ^ ((crc << 24 >> 31) & g0);
crc = ((unsigned)crc >> 8) ^ c;
i = i + 1;
}
return ~crc;
}
int put_conv0(u32_t conv,const char * input,int len_in,char *&output,int &len_out)
{
assert(len_in>=0);
static char buf[buf_len];
output=buf;
u32_t n_conv=htonl(conv);
memcpy(output,&n_conv,sizeof(n_conv));
memcpy(output+sizeof(n_conv),input,len_in);
u32_t crc32=crc32h((unsigned char *)output,len_in+sizeof(crc32));
u32_t crc32_n=htonl(crc32);
len_out=len_in+(int)(sizeof(n_conv))+(int)sizeof(crc32_n);
memcpy(output+len_in+(int)(sizeof(n_conv)),&crc32_n,sizeof(crc32_n));
return 0;
int put_conv0(u32_t conv, const char *input, int len_in, char *&output, int &len_out) {
assert(len_in >= 0);
static char buf[buf_len];
output = buf;
u32_t n_conv = htonl(conv);
memcpy(output, &n_conv, sizeof(n_conv));
memcpy(output + sizeof(n_conv), input, len_in);
u32_t crc32 = crc32h((unsigned char *)output, len_in + sizeof(crc32));
u32_t crc32_n = htonl(crc32);
len_out = len_in + (int)(sizeof(n_conv)) + (int)sizeof(crc32_n);
memcpy(output + len_in + (int)(sizeof(n_conv)), &crc32_n, sizeof(crc32_n));
return 0;
}
int get_conv0(u32_t &conv,const char *input,int len_in,char *&output,int &len_out )
{
assert(len_in>=0);
u32_t n_conv;
memcpy(&n_conv,input,sizeof(n_conv));
conv=ntohl(n_conv);
output=(char *)input+sizeof(n_conv);
u32_t crc32_n;
len_out=len_in-(int)sizeof(n_conv)-(int)sizeof(crc32_n);
if(len_out<0)
{
mylog(log_debug,"len_out<0\n");
return -1;
}
memcpy(&crc32_n,input+len_in-(int)sizeof(crc32_n),sizeof(crc32_n));
u32_t crc32=ntohl(crc32_n);
if(crc32!=crc32h((unsigned char *)input,len_in-(int)sizeof(crc32_n)))
{
mylog(log_debug,"crc32 check failed\n");
return -1;
}
return 0;
int get_conv0(u32_t &conv, const char *input, int len_in, char *&output, int &len_out) {
assert(len_in >= 0);
u32_t n_conv;
memcpy(&n_conv, input, sizeof(n_conv));
conv = ntohl(n_conv);
output = (char *)input + sizeof(n_conv);
u32_t crc32_n;
len_out = len_in - (int)sizeof(n_conv) - (int)sizeof(crc32_n);
if (len_out < 0) {
mylog(log_debug, "len_out<0\n");
return -1;
}
memcpy(&crc32_n, input + len_in - (int)sizeof(crc32_n), sizeof(crc32_n));
u32_t crc32 = ntohl(crc32_n);
if (crc32 != crc32h((unsigned char *)input, len_in - (int)sizeof(crc32_n))) {
mylog(log_debug, "crc32 check failed\n");
return -1;
}
return 0;
}
int put_crc32(char * s,int &len)
{
assert(len>=0);
//if(len<0) return -1;
u32_t crc32=crc32h((unsigned char *)s,len);
write_u32(s+len,crc32);
len+=sizeof(u32_t);
int put_crc32(char *s, int &len) {
if (disable_checksum) return 0;
assert(len >= 0);
// if(len<0) return -1;
u32_t crc32 = crc32h((unsigned char *)s, len);
write_u32(s + len, crc32);
len += sizeof(u32_t);
return 0;
return 0;
}
int do_cook(char * data,int &len)
{
put_crc32(data,len);
if(!disable_obscure)do_obscure(data,len);
if(!disable_xor)encrypt_0(data,len,key_string);
return 0;
int do_cook(char *data, int &len) {
put_crc32(data, len);
if (!disable_obscure) do_obscure(data, len);
if (!disable_xor) encrypt_0(data, len, key_string);
return 0;
}
int de_cook(char * s,int &len)
{
if(!disable_xor)decrypt_0(s,len,key_string);
if(!disable_obscure)
{
int ret=de_obscure(s,len);
if(ret!=0)
{
mylog(log_debug,"de_obscure fail\n");
return ret;
}
}
int ret=rm_crc32(s,len);
if(ret!=0)
{
mylog(log_debug,"rm_crc32 fail\n");
return ret;
}
return 0;
int de_cook(char *s, int &len) {
if (!disable_xor) decrypt_0(s, len, key_string);
if (!disable_obscure) {
int ret = de_obscure(s, len);
if (ret != 0) {
mylog(log_debug, "de_obscure fail\n");
return ret;
}
}
int ret = rm_crc32(s, len);
if (ret != 0) {
mylog(log_debug, "rm_crc32 fail\n");
return ret;
}
return 0;
}
int rm_crc32(char * s,int &len)
{
assert(len>=0);
len-=sizeof(u32_t);
if(len<0) return -1;
u32_t crc32_in=read_u32(s+len);
u32_t crc32=crc32h((unsigned char *)s,len);
if(crc32!=crc32_in) return -1;
return 0;
int rm_crc32(char *s, int &len) {
if (disable_checksum) return 0;
assert(len >= 0);
len -= sizeof(u32_t);
if (len < 0) return -1;
u32_t crc32_in = read_u32(s + len);
u32_t crc32 = crc32h((unsigned char *)s, len);
if (crc32 != crc32_in) return -1;
return 0;
}
/*
int do_obs()
@ -370,31 +339,25 @@ int do_obs()
}
int de_obs()*/
int put_conv(u32_t conv,const char * input,int len_in,char *&output,int &len_out)
{
static char buf[buf_len];
output=buf;
u32_t n_conv=htonl(conv);
memcpy(output,&n_conv,sizeof(n_conv));
memcpy(output+sizeof(n_conv),input,len_in);
len_out=len_in+(int)(sizeof(n_conv));
int put_conv(u32_t conv, const char *input, int len_in, char *&output, int &len_out) {
static char buf[buf_len];
output = buf;
u32_t n_conv = htonl(conv);
memcpy(output, &n_conv, sizeof(n_conv));
memcpy(output + sizeof(n_conv), input, len_in);
len_out = len_in + (int)(sizeof(n_conv));
return 0;
return 0;
}
int get_conv(u32_t &conv,const char *input,int len_in,char *&output,int &len_out )
{
u32_t n_conv;
memcpy(&n_conv,input,sizeof(n_conv));
conv=ntohl(n_conv);
output=(char *)input+sizeof(n_conv);
len_out=len_in-(int)sizeof(n_conv);
if(len_out<0)
{
mylog(log_debug,"len_out<0\n");
return -1;
}
return 0;
int get_conv(u32_t &conv, const char *input, int len_in, char *&output, int &len_out) {
u32_t n_conv;
memcpy(&n_conv, input, sizeof(n_conv));
conv = ntohl(n_conv);
output = (char *)input + sizeof(n_conv);
len_out = len_in - (int)sizeof(n_conv);
if (len_out < 0) {
mylog(log_debug, "len_out<0\n");
return -1;
}
return 0;
}

View File

@ -12,7 +12,7 @@
#include "fd_manager.h"
extern int iv_min;
extern int iv_max;//< 256;
extern int iv_max; //< 256;
extern u64_t packet_send_count;
extern u64_t dup_packet_send_count;
@ -24,24 +24,23 @@ extern int random_drop;
extern int disable_obscure;
extern int disable_xor;
int my_send(const dest_t &dest, char *data, int len);
int my_send(const dest_t &dest,char *data,int len);
void encrypt_0(char *input, int &len, char *key);
void decrypt_0(char *input, int &len, char *key);
int add_seq(char *data, int &data_len);
int remove_seq(char *data, int &data_len);
int do_obscure(const char *input, int in_len, char *output, int &out_len);
int de_obscure(const char *input, int in_len, char *output, int &out_len);
void encrypt_0(char * input,int &len,char *key);
void decrypt_0(char * input,int &len,char *key);
int add_seq(char * data,int &data_len );
int remove_seq(char * data,int &data_len);
int do_obscure(const char * input, int in_len,char *output,int &out_len);
int de_obscure(const char * input, int in_len,char *output,int &out_len);
// int sendto_fd_u64 (int fd,u64_t u64,char * buf, int len,int flags);
int sendto_ip_port(u32_t ip, int port, char *buf, int len, int flags);
int send_fd(int fd, char *buf, int len, int flags);
//int sendto_fd_u64 (int fd,u64_t u64,char * buf, int len,int flags);
int sendto_ip_port (u32_t ip,int port,char * buf, int len,int flags);
int send_fd (int fd,char * buf, int len,int flags);
int put_conv(u32_t conv,const char * input,int len_in,char *&output,int &len_out);
int get_conv(u32_t &conv,const char *input,int len_in,char *&output,int &len_out );
int put_crc32(char * s,int &len);
int rm_crc32(char * s,int &len);
int do_cook(char * data,int &len);
int de_cook(char * s,int &len);
int put_conv(u32_t conv, const char *input, int len_in, char *&output, int &len_out);
int get_conv(u32_t &conv, const char *input, int len_in, char *&output, int &len_out);
int put_crc32(char *s, int &len);
int rm_crc32(char *s, int &len);
int do_cook(char *data, int &len);
int de_cook(char *s, int &len);
#endif /* PACKET_H_ */

View File

@ -1,705 +0,0 @@
/*
* tunnel.cpp
*
* Created on: Oct 26, 2017
* Author: root
*/
#include "tunnel.h"
int tunnel_client_event_loop()
{
//char buf[buf_len];
int i, j, k;int ret;
int yes = 1;
int epoll_fd;
int remote_fd;
fd64_t remote_fd64;
conn_info_t *conn_info_p=new conn_info_t;
conn_info_t &conn_info=*conn_info_p; //huge size of conn_info,do not allocate on stack
//conn_info.conv_manager.reserve();
//conn_info.fec_encode_manager.re_init(fec_data_num,fec_redundant_num,fec_mtu,fec_pending_num,fec_pending_time,fec_type);
int local_listen_fd;
//fd64_t local_listen_fd64;
new_listen_socket(local_listen_fd,local_ip_uint32,local_port);
//local_listen_fd64=fd_manager.create(local_listen_fd);
epoll_fd = epoll_create1(0);
assert(epoll_fd>0);
const int max_events = 4096;
struct epoll_event ev, events[max_events];
if (epoll_fd < 0) {
mylog(log_fatal,"epoll return %d\n", epoll_fd);
myexit(-1);
}
ev.events = EPOLLIN;
ev.data.u64 = local_listen_fd;
ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, local_listen_fd, &ev);
if (ret!=0) {
mylog(log_fatal,"add udp_listen_fd error\n");
myexit(-1);
}
assert(new_connected_socket(remote_fd,remote_ip_uint32,remote_port)==0);
remote_fd64=fd_manager.create(remote_fd);
mylog(log_debug,"remote_fd64=%llu\n",remote_fd64);
ev.events = EPOLLIN;
ev.data.u64 = remote_fd64;
ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, remote_fd, &ev);
if (ret!= 0) {
mylog(log_fatal,"add raw_fd error\n");
myexit(-1);
}
ev.events = EPOLLIN;
ev.data.u64 = delay_manager.get_timer_fd();
mylog(log_debug,"delay_manager.get_timer_fd()=%d\n",delay_manager.get_timer_fd());
ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, delay_manager.get_timer_fd(), &ev);
if (ret!= 0) {
mylog(log_fatal,"add delay_manager.get_timer_fd() error\n");
myexit(-1);
}
u64_t tmp_fd64=conn_info.fec_encode_manager.get_timer_fd64();
ev.events = EPOLLIN;
ev.data.u64 = tmp_fd64;
mylog(log_debug,"conn_info.fec_encode_manager.get_timer_fd64()=%llu\n",conn_info.fec_encode_manager.get_timer_fd64());
ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd_manager.to_fd(tmp_fd64), &ev);
if (ret!= 0) {
mylog(log_fatal,"add fec_encode_manager.get_timer_fd64() error\n");
myexit(-1);
}
//my_timer_t timer;
conn_info.timer.add_fd_to_epoll(epoll_fd);
conn_info.timer.set_timer_repeat_us(timer_interval*1000);
mylog(log_debug,"conn_info.timer.get_timer_fd()=%d\n",conn_info.timer.get_timer_fd());
int fifo_fd=-1;
if(fifo_file[0]!=0)
{
fifo_fd=create_fifo(fifo_file);
ev.events = EPOLLIN;
ev.data.u64 = fifo_fd;
ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fifo_fd, &ev);
if (ret!= 0) {
mylog(log_fatal,"add fifo_fd to epoll error %s\n",strerror(errno));
myexit(-1);
}
mylog(log_info,"fifo_file=%s\n",fifo_file);
}
while(1)////////////////////////
{
if(about_to_exit) myexit(0);
int nfds = epoll_wait(epoll_fd, events, max_events, 180 * 1000);
if (nfds < 0) { //allow zero
if(errno==EINTR )
{
mylog(log_info,"epoll interrupted by signal continue\n");
//myexit(0);
}
else
{
mylog(log_fatal,"epoll_wait return %d,%s\n", nfds,strerror(errno));
myexit(-1);
}
}
int idx;
for (idx = 0; idx < nfds; ++idx) {
if(events[idx].data.u64==(u64_t)conn_info.timer.get_timer_fd())
{
uint64_t value;
read(conn_info.timer.get_timer_fd(), &value, 8);
conn_info.conv_manager.clear_inactive();
mylog(log_trace,"events[idx].data.u64==(u64_t)conn_info.timer.get_timer_fd()\n");
conn_info.stat.report_as_client();
if(debug_force_flush_fec)
{
int out_n;char **out_arr;int *out_len;my_time_t *out_delay;
dest_t dest;
dest.type=type_fd64;
dest.inner.fd64=remote_fd64;
dest.cook=1;
from_normal_to_fec(conn_info,0,0,out_n,out_arr,out_len,out_delay);
for(int i=0;i<out_n;i++)
{
delay_send(out_delay[i],dest,out_arr[i],out_len[i]);
}
}
}
else if (events[idx].data.u64 == (u64_t)fifo_fd)
{
char buf[buf_len];
int len=read (fifo_fd, buf, sizeof (buf));
if(len<0)
{
mylog(log_warn,"fifo read failed len=%d,errno=%s\n",len,strerror(errno));
continue;
}
buf[len]=0;
handle_command(buf);
}
else if (events[idx].data.u64 == (u64_t)local_listen_fd||events[idx].data.u64 == conn_info.fec_encode_manager.get_timer_fd64())
{
char data[buf_len];
int data_len;
ip_port_t ip_port;
u32_t conv;
int out_n;char **out_arr;int *out_len;my_time_t *out_delay;
dest_t dest;
dest.type=type_fd64;
dest.inner.fd64=remote_fd64;
dest.cook=1;
if(events[idx].data.u64 == conn_info.fec_encode_manager.get_timer_fd64())
{
fd64_t fd64=events[idx].data.u64;
mylog(log_trace,"events[idx].data.u64 == conn_info.fec_encode_manager.get_timer_fd64()\n");
//mylog(log_info,"timer!!!\n");
uint64_t value;
if(!fd_manager.exist(fd64)) //fd64 has been closed
{
mylog(log_trace,"!fd_manager.exist(fd64)");
continue;
}
if((ret=read(fd_manager.to_fd(fd64), &value, 8))!=8)
{
mylog(log_trace,"(ret=read(fd_manager.to_fd(fd64), &value, 8))!=8,ret=%d\n",ret);
continue;
}
if(value==0)
{
mylog(log_debug,"value==0\n");
continue;
}
assert(value==1);
from_normal_to_fec(conn_info,0,0,out_n,out_arr,out_len,out_delay);
//from_normal_to_fec(conn_info,0,0,out_n,out_arr,out_len,out_delay);
}
else//events[idx].data.u64 == (u64_t)local_listen_fd
{
mylog(log_trace,"events[idx].data.u64 == (u64_t)local_listen_fd\n");
struct sockaddr_in udp_new_addr_in={0};
socklen_t udp_new_addr_len = sizeof(sockaddr_in);
if ((data_len = recvfrom(local_listen_fd, data, max_data_len, 0,
(struct sockaddr *) &udp_new_addr_in, &udp_new_addr_len)) == -1) {
mylog(log_error,"recv_from error,this shouldnt happen,err=%s,but we can try to continue\n",strerror(errno));
continue;
//mylog(log_error,"recv_from error,this shouldnt happen at client\n");
//myexit(1);
};
if(!disable_mtu_warn&&data_len>=mtu_warn)
{
mylog(log_warn,"huge packet,data len=%d (>=%d).strongly suggested to set a smaller mtu at upper level,to get rid of this warn\n ",data_len,mtu_warn);
}
mylog(log_trace,"Received packet from %s:%d,len: %d\n", inet_ntoa(udp_new_addr_in.sin_addr),
ntohs(udp_new_addr_in.sin_port),data_len);
ip_port.ip=udp_new_addr_in.sin_addr.s_addr;
ip_port.port=ntohs(udp_new_addr_in.sin_port);
u64_t u64=ip_port.to_u64();
if(!conn_info.conv_manager.is_u64_used(u64))
{
if(conn_info.conv_manager.get_size() >=max_conv_num)
{
mylog(log_warn,"ignored new udp connect bc max_conv_num exceed\n");
continue;
}
conv=conn_info.conv_manager.get_new_conv();
conn_info.conv_manager.insert_conv(conv,u64);
mylog(log_info,"new packet from %s:%d,conv_id=%x\n",inet_ntoa(udp_new_addr_in.sin_addr),ntohs(udp_new_addr_in.sin_port),conv);
}
else
{
conv=conn_info.conv_manager.find_conv_by_u64(u64);
mylog(log_trace,"conv=%d\n",conv);
}
conn_info.conv_manager.update_active_time(conv);
char * new_data;
int new_len;
put_conv(conv,data,data_len,new_data,new_len);
mylog(log_trace,"data_len=%d new_len=%d\n",data_len,new_len);
//dest.conv=conv;
from_normal_to_fec(conn_info,new_data,new_len,out_n,out_arr,out_len,out_delay);
}
mylog(log_trace,"out_n=%d\n",out_n);
for(int i=0;i<out_n;i++)
{
delay_send(out_delay[i],dest,out_arr[i],out_len[i]);
}
//my_send(dest,data,data_len);
}
else if (events[idx].data.u64 == (u64_t)delay_manager.get_timer_fd()) {
uint64_t value;
read(delay_manager.get_timer_fd(), &value, 8);
mylog(log_trace,"events[idx].data.u64 == (u64_t)delay_manager.get_timer_fd()\n");
//printf("<timerfd_triggered, %d>",delay_mp.size());
//fflush(stdout);
}
else if(events[idx].data.u64>u32_t(-1) )
{
char data[buf_len];
if(!fd_manager.exist(events[idx].data.u64)) //fd64 has been closed
{
mylog(log_trace,"!fd_manager.exist(events[idx].data.u64)");
continue;
}
assert(events[idx].data.u64==remote_fd64);
int fd=fd_manager.to_fd(remote_fd64);
int data_len =recv(fd,data,max_data_len,0);
mylog(log_trace, "received data from udp fd %d, len=%d\n", remote_fd,data_len);
if(data_len<0)
{
if(errno==ECONNREFUSED)
{
//conn_manager.clear_list.push_back(udp_fd);
mylog(log_debug, "recv failed %d ,udp_fd%d,errno:%s\n", data_len,remote_fd,strerror(errno));
}
mylog(log_warn, "recv failed %d ,udp_fd%d,errno:%s\n", data_len,remote_fd,strerror(errno));
continue;
}
if(!disable_mtu_warn&&data_len>mtu_warn)
{
mylog(log_warn,"huge packet,data len=%d (>%d).strongly suggested to set a smaller mtu at upper level,to get rid of this warn\n ",data_len,mtu_warn);
}
if(de_cook(data,data_len)!=0)
{
mylog(log_debug,"de_cook error");
continue;
}
int out_n;char **out_arr;int *out_len;my_time_t *out_delay;
from_fec_to_normal(conn_info,data,data_len,out_n,out_arr,out_len,out_delay);
mylog(log_trace,"out_n=%d\n",out_n);
for(int i=0;i<out_n;i++)
{
u32_t conv;
char *new_data;
int new_len;
if(get_conv(conv,out_arr[i],out_len[i],new_data,new_len)!=0)
{
mylog(log_debug,"get_conv(conv,out_arr[i],out_len[i],new_data,new_len)!=0");
continue;
}
if(!conn_info.conv_manager.is_conv_used(conv))
{
mylog(log_trace,"!conn_info.conv_manager.is_conv_used(conv)");
continue;
}
conn_info.conv_manager.update_active_time(conv);
u64_t u64=conn_info.conv_manager.find_u64_by_conv(conv);
dest_t dest;
dest.inner.fd_ip_port.fd=local_listen_fd;
dest.inner.fd_ip_port.ip_port.from_u64(u64);
dest.type=type_fd_ip_port;
//dest.conv=conv;
delay_send(out_delay[i],dest,new_data,new_len);
}
//mylog(log_trace,"[%s] send packet\n",dest.inner.ip_port.to_s());
}
else
{
mylog(log_fatal,"unknown fd,this should never happen\n");
myexit(-1);
}
}
delay_manager.check();
}
return 0;
}
int tunnel_server_event_loop()
{
//char buf[buf_len];
int i, j, k;int ret;
int yes = 1;
int epoll_fd;
int remote_fd;
// conn_info_t conn_info;
int local_listen_fd;
// fd64_t local_listen_fd64;
new_listen_socket(local_listen_fd,local_ip_uint32,local_port);
// local_listen_fd64=fd_manager.create(local_listen_fd);
epoll_fd = epoll_create1(0);
assert(epoll_fd>0);
const int max_events = 4096;
struct epoll_event ev, events[max_events];
if (epoll_fd < 0) {
mylog(log_fatal,"epoll return %d\n", epoll_fd);
myexit(-1);
}
ev.events = EPOLLIN;
ev.data.u64 = local_listen_fd;
ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, local_listen_fd, &ev);
if (ret!=0) {
mylog(log_fatal,"add udp_listen_fd error\n");
myexit(-1);
}
ev.events = EPOLLIN;
ev.data.u64 = delay_manager.get_timer_fd();
ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, delay_manager.get_timer_fd(), &ev);
if (ret!= 0) {
mylog(log_fatal,"add delay_manager.get_timer_fd() error\n");
myexit(-1);
}
mylog(log_debug," delay_manager.get_timer_fd() =%d\n", delay_manager.get_timer_fd());
mylog(log_info,"now listening at %s:%d\n",my_ntoa(local_ip_uint32),local_port);
my_timer_t timer;
timer.add_fd_to_epoll(epoll_fd);
timer.set_timer_repeat_us(timer_interval*1000);
mylog(log_debug," timer.get_timer_fd() =%d\n",timer.get_timer_fd());
int fifo_fd=-1;
if(fifo_file[0]!=0)
{
fifo_fd=create_fifo(fifo_file);
ev.events = EPOLLIN;
ev.data.u64 = fifo_fd;
ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fifo_fd, &ev);
if (ret!= 0) {
mylog(log_fatal,"add fifo_fd to epoll error %s\n",strerror(errno));
myexit(-1);
}
mylog(log_info,"fifo_file=%s\n",fifo_file);
}
while(1)////////////////////////
{
if(about_to_exit) myexit(0);
int nfds = epoll_wait(epoll_fd, events, max_events, 180 * 1000);
if (nfds < 0) { //allow zero
if(errno==EINTR )
{
mylog(log_info,"epoll interrupted by signal,continue\n");
//myexit(0);
}
else
{
mylog(log_fatal,"epoll_wait return %d,%s\n", nfds,strerror(errno));
myexit(-1);
}
}
int idx;
for (idx = 0; idx < nfds; ++idx)
{
/*
if ((events[idx].data.u64 ) == (u64_t)timer_fd)
{
conn_manager.clear_inactive();
u64_t dummy;
read(timer_fd, &dummy, 8);
//current_time_rough=get_current_time();
}
else */
if(events[idx].data.u64==(u64_t)timer.get_timer_fd())
{
uint64_t value;
read(timer.get_timer_fd(), &value, 8);
conn_manager.clear_inactive();
mylog(log_trace,"events[idx].data.u64==(u64_t)timer.get_timer_fd()\n");
//conn_info.conv_manager.clear_inactive();
}
else if (events[idx].data.u64 == (u64_t)fifo_fd)
{
char buf[buf_len];
int len=read (fifo_fd, buf, sizeof (buf));
if(len<0)
{
mylog(log_warn,"fifo read failed len=%d,errno=%s\n",len,strerror(errno));
continue;
}
buf[len]=0;
handle_command(buf);
}
else if (events[idx].data.u64 == (u64_t)local_listen_fd)
{
mylog(log_trace,"events[idx].data.u64 == (u64_t)local_listen_fd\n");
//int recv_len;
char data[buf_len];
int data_len;
struct sockaddr_in udp_new_addr_in={0};
socklen_t udp_new_addr_len = sizeof(sockaddr_in);
if ((data_len = recvfrom(local_listen_fd, data, max_data_len, 0,
(struct sockaddr *) &udp_new_addr_in, &udp_new_addr_len)) == -1) {
mylog(log_error,"recv_from error,this shouldnt happen,err=%s,but we can try to continue\n",strerror(errno));
continue;
//myexit(1);
};
mylog(log_trace,"Received packet from %s:%d,len: %d\n", inet_ntoa(udp_new_addr_in.sin_addr),
ntohs(udp_new_addr_in.sin_port),data_len);
if(!disable_mtu_warn&&data_len>=mtu_warn)///////////////////////delete this for type 0 in furture
{
mylog(log_warn,"huge packet,data len=%d (>=%d).strongly suggested to set a smaller mtu at upper level,to get rid of this warn\n ",data_len,mtu_warn);
}
if(de_cook(data,data_len)!=0)
{
mylog(log_debug,"de_cook error");
continue;
}
ip_port_t ip_port;
ip_port.ip=udp_new_addr_in.sin_addr.s_addr;
ip_port.port=ntohs(udp_new_addr_in.sin_port);
mylog(log_trace,"ip_port= %s\n",ip_port.to_s());
if(!conn_manager.exist(ip_port))
{
if(conn_manager.mp.size() >=max_conn_num)
{
mylog(log_warn,"new connection %s ignored bc max_conn_num exceed\n",ip_port.to_s());
continue;
}
conn_manager.insert(ip_port);
conn_info_t &conn_info=conn_manager.find(ip_port);
//conn_info.fec_encode_manager.re_init(fec_data_num,fec_redundant_num,fec_mtu,fec_pending_num,fec_pending_time,fec_type);
//conn_info.conv_manager.reserve(); //already reserved in constructor
u64_t fec_fd64=conn_info.fec_encode_manager.get_timer_fd64();
mylog(log_debug,"fec_fd64=%llu\n",fec_fd64);
ev.events = EPOLLIN;
ev.data.u64 = fec_fd64;
ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd_manager.to_fd(fec_fd64), &ev);
fd_manager.get_info(fec_fd64).ip_port=ip_port;
conn_info.timer.add_fd64_to_epoll(epoll_fd);
conn_info.timer.set_timer_repeat_us(timer_interval*1000);
mylog(log_debug,"conn_info.timer.get_timer_fd64()=%llu\n",conn_info.timer.get_timer_fd64());
u64_t timer_fd64=conn_info.timer.get_timer_fd64();
fd_manager.get_info(timer_fd64).ip_port=ip_port;
mylog(log_info,"new connection from %s\n",ip_port.to_s());
}
conn_info_t &conn_info=conn_manager.find(ip_port);
conn_info.update_active_time();
int out_n;char **out_arr;int *out_len;my_time_t *out_delay;
from_fec_to_normal(conn_info,data,data_len,out_n,out_arr,out_len,out_delay);
mylog(log_trace,"out_n= %d\n",out_n);
for(int i=0;i<out_n;i++)
{
u32_t conv;
char *new_data;
int new_len;
if(get_conv(conv,out_arr[i],out_len[i],new_data,new_len)!=0)
{
mylog(log_debug,"get_conv failed");
continue;
}
/*
id_t tmp_conv_id;
memcpy(&tmp_conv_id,&data_[0],sizeof(tmp_conv_id));
tmp_conv_id=ntohl(tmp_conv_id);*/
if (!conn_info.conv_manager.is_conv_used(conv))
{
if(conn_info.conv_manager.get_size() >=max_conv_num)
{
mylog(log_warn,"ignored new udp connect bc max_conv_num exceed\n");
continue;
}
int new_udp_fd;
ret=new_connected_socket(new_udp_fd,remote_ip_uint32,remote_port);
if (ret != 0) {
mylog(log_warn, "[%s]new_connected_socket failed\n",ip_port.to_s());
continue;
}
fd64_t fd64 = fd_manager.create(new_udp_fd);
ev.events = EPOLLIN;
ev.data.u64 = fd64;
ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, new_udp_fd, &ev);
conn_info.conv_manager.insert_conv(conv, fd64);
fd_manager.get_info(fd64).ip_port=ip_port;
mylog(log_info,"[%s]new conv %x,fd %d created,fd64=%llu\n",ip_port.to_s(),conv,new_udp_fd,fd64);
//assert(!conn_manager.exist_fd64(fd64));
//conn_manager.insert_fd64(fd64,ip_port);
}
conn_info.conv_manager.update_active_time(conv);
fd64_t fd64= conn_info.conv_manager.find_u64_by_conv(conv);
//int fd=fd_manager.fd64_to_fd(fd64);
dest_t dest;
dest.type=type_fd64;
dest.inner.fd64=fd64;
//dest.conv=conv;
delay_send(out_delay[i],dest,new_data,new_len);
}
}
else if (events[idx].data.u64 == (u64_t)delay_manager.get_timer_fd()) {
uint64_t value;
read(delay_manager.get_timer_fd(), &value, 8);
mylog(log_trace,"events[idx].data.u64 == (u64_t)delay_manager.get_timer_fd()\n");
}
else if (events[idx].data.u64 >u32_t(-1))
{
char data[buf_len];
int data_len;
u32_t conv;
fd64_t fd64=events[idx].data.u64;
mylog(log_trace,"events[idx].data.u64 >u32_t(-1),%llu\n",(u64_t)events[idx].data.u64);
if(!fd_manager.exist(fd64)) //fd64 has been closed
{
mylog(log_trace,"!fd_manager.exist(fd64)\n");
continue;
}
assert(fd_manager.exist_info(fd64));
ip_port_t ip_port=fd_manager.get_info(fd64).ip_port;
assert(conn_manager.exist(ip_port));
conn_info_t &conn_info=conn_manager.find(ip_port);
//conn_info.update_active_time(); //cant put it here
int out_n=-2;char **out_arr;int *out_len;my_time_t *out_delay;
dest_t dest;
dest.inner.fd_ip_port.fd=local_listen_fd;
dest.inner.fd_ip_port.ip_port=ip_port;
dest.type=type_fd_ip_port;
dest.cook=1;
if(fd64==conn_info.fec_encode_manager.get_timer_fd64())
{
//mylog(log_infol,"timer!!!\n");
uint64_t value;
if((ret=read(fd_manager.to_fd(fd64), &value, 8))!=8)
{
mylog(log_trace,"fd_manager.to_fd(fd64), &value, 8)!=8 ,%d\n",ret);
continue;
}
if(value==0)
{
mylog(log_trace,"value==0\n");
continue;
}
assert(value==1);
from_normal_to_fec(conn_info,0,0,out_n,out_arr,out_len,out_delay);
}
else if(fd64==conn_info.timer.get_timer_fd64())
{
uint64_t value;
read(conn_info.timer.get_timer_fd(), &value, 8);
conn_info.conv_manager.clear_inactive();
if(debug_force_flush_fec)
{
from_normal_to_fec(conn_info,0,0,out_n,out_arr,out_len,out_delay);
}
conn_info.stat.report_as_server(ip_port);
continue;
}
else
{
assert(conn_info.conv_manager.is_u64_used(fd64));
conv=conn_info.conv_manager.find_conv_by_u64(fd64);
conn_info.conv_manager.update_active_time(conv);
conn_info.update_active_time();
int fd=fd_manager.to_fd(fd64);
data_len=recv(fd,data,max_data_len,0);
mylog(log_trace,"received a packet from udp_fd,len:%d,conv=%d\n",data_len,conv);
if(data_len<0)
{
mylog(log_debug,"udp fd,recv_len<0 continue,%s\n",strerror(errno));
continue;
}
if(!disable_mtu_warn&&data_len>=mtu_warn)
{
mylog(log_warn,"huge packet,data len=%d (>=%d).strongly suggested to set a smaller mtu at upper level,to get rid of this warn\n ",data_len,mtu_warn);
}
char * new_data;
int new_len;
put_conv(conv,data,data_len,new_data,new_len);
from_normal_to_fec(conn_info,new_data,new_len,out_n,out_arr,out_len,out_delay);
}
mylog(log_trace,"out_n=%d\n",out_n);
for(int i=0;i<out_n;i++)
{
delay_send(out_delay[i],dest,out_arr[i],out_len[i]);
}
//mylog(log_trace,"[%s] send packet\n",ip_port.to_s());
}
else
{
mylog(log_fatal,"unknown fd,this should never happen\n");
myexit(-1);
}
}
delay_manager.check();
}
return 0;
}

View File

@ -8,7 +8,6 @@
#ifndef TUNNEL_H_
#define TUNNEL_H_
#include "misc.h"
int tunnel_client_event_loop();

422
tunnel_client.cpp Normal file
View File

@ -0,0 +1,422 @@
#include "tunnel.h"
void data_from_local_or_fec_timeout(conn_info_t &conn_info, int is_time_out) {
fd64_t &remote_fd64 = conn_info.remote_fd64;
int &local_listen_fd = conn_info.local_listen_fd;
char data[buf_len];
int data_len;
address_t addr;
u32_t conv;
int out_n;
char **out_arr;
int *out_len;
my_time_t *out_delay;
dest_t dest;
dest.type = type_fd64;
dest.inner.fd64 = remote_fd64;
dest.cook = 1;
if (is_time_out) {
// fd64_t fd64=events[idx].data.u64;
mylog(log_trace, "events[idx].data.u64 == conn_info.fec_encode_manager.get_timer_fd64()\n");
// uint64_t value;
// if(!fd_manager.exist(fd64)) //fd64 has been closed
//{
// mylog(log_trace,"!fd_manager.exist(fd64)");
// continue;
// }
// if((ret=read(fd_manager.to_fd(fd64), &value, 8))!=8)
//{
// mylog(log_trace,"(ret=read(fd_manager.to_fd(fd64), &value, 8))!=8,ret=%d\n",ret);
// continue;
// }
// if(value==0)
//{
// mylog(log_debug,"value==0\n");
// continue;
// }
// assert(value==1);
from_normal_to_fec(conn_info, 0, 0, out_n, out_arr, out_len, out_delay);
} else // events[idx].data.u64 == (u64_t)local_listen_fd
{
mylog(log_trace, "events[idx].data.u64 == (u64_t)local_listen_fd\n");
address_t::storage_t udp_new_addr_in = {0};
socklen_t udp_new_addr_len = sizeof(address_t::storage_t);
if ((data_len = recvfrom(local_listen_fd, data, max_data_len + 1, 0,
(struct sockaddr *)&udp_new_addr_in, &udp_new_addr_len)) == -1) {
mylog(log_debug, "recv_from error,this shouldnt happen,err=%s,but we can try to continue\n", get_sock_error());
return;
};
if (data_len == max_data_len + 1) {
mylog(log_warn, "huge packet from upper level, data_len > %d, packet truncated, dropped\n", max_data_len);
return;
}
if (!disable_mtu_warn && data_len >= mtu_warn) {
mylog(log_warn, "huge packet,data len=%d (>=%d).strongly suggested to set a smaller mtu at upper level,to get rid of this warn\n ", data_len, mtu_warn);
}
addr.from_sockaddr((struct sockaddr *)&udp_new_addr_in, udp_new_addr_len);
mylog(log_trace, "Received packet from %s, len: %d\n", addr.get_str(), data_len);
// u64_t u64=ip_port.to_u64();
if (!conn_info.conv_manager.c.is_data_used(addr)) {
if (conn_info.conv_manager.c.get_size() >= max_conv_num) {
mylog(log_warn, "ignored new udp connect bc max_conv_num exceed\n");
return;
}
conv = conn_info.conv_manager.c.get_new_conv();
conn_info.conv_manager.c.insert_conv(conv, addr);
mylog(log_info, "new packet from %s,conv_id=%x\n", addr.get_str(), conv);
} else {
conv = conn_info.conv_manager.c.find_conv_by_data(addr);
mylog(log_trace, "conv=%d\n", conv);
}
conn_info.conv_manager.c.update_active_time(conv);
char *new_data;
int new_len;
put_conv(conv, data, data_len, new_data, new_len);
mylog(log_trace, "data_len=%d new_len=%d\n", data_len, new_len);
from_normal_to_fec(conn_info, new_data, new_len, out_n, out_arr, out_len, out_delay);
}
mylog(log_trace, "out_n=%d\n", out_n);
for (int i = 0; i < out_n; i++) {
delay_send(out_delay[i], dest, out_arr[i], out_len[i]);
}
}
static void local_listen_cb(struct ev_loop *loop, struct ev_io *watcher, int revents) {
assert(!(revents & EV_ERROR));
conn_info_t &conn_info = *((conn_info_t *)watcher->data);
data_from_local_or_fec_timeout(conn_info, 0);
}
static void remote_cb(struct ev_loop *loop, struct ev_io *watcher, int revents) {
assert(!(revents & EV_ERROR));
conn_info_t &conn_info = *((conn_info_t *)watcher->data);
char data[buf_len];
if (!fd_manager.exist(watcher->u64)) // fd64 has been closed
{
mylog(log_trace, "!fd_manager.exist(events[idx].data.u64)");
return;
}
fd64_t &remote_fd64 = conn_info.remote_fd64;
int &remote_fd = conn_info.remote_fd;
assert(watcher->u64 == remote_fd64);
int fd = fd_manager.to_fd(remote_fd64);
int data_len = recv(fd, data, max_data_len + 1, 0);
if (data_len == max_data_len + 1) {
mylog(log_warn, "huge packet, data_len > %d, packet truncated, dropped\n", max_data_len);
return;
}
mylog(log_trace, "received data from udp fd %d, len=%d\n", remote_fd, data_len);
if (data_len < 0) {
if (get_sock_errno() == ECONNREFUSED) {
mylog(log_debug, "recv failed %d ,udp_fd%d,errno:%s\n", data_len, remote_fd, get_sock_error());
}
mylog(log_warn, "recv failed %d ,udp_fd%d,errno:%s\n", data_len, remote_fd, get_sock_error());
return;
}
if (!disable_mtu_warn && data_len > mtu_warn) {
mylog(log_warn, "huge packet,data len=%d (>%d).strongly suggested to set a smaller mtu at upper level,to get rid of this warn\n ", data_len, mtu_warn);
}
if (de_cook(data, data_len) != 0) {
mylog(log_debug, "de_cook error");
return;
}
int out_n;
char **out_arr;
int *out_len;
my_time_t *out_delay;
from_fec_to_normal(conn_info, data, data_len, out_n, out_arr, out_len, out_delay);
mylog(log_trace, "out_n=%d\n", out_n);
for (int i = 0; i < out_n; i++) {
u32_t conv;
char *new_data;
int new_len;
if (get_conv(conv, out_arr[i], out_len[i], new_data, new_len) != 0) {
mylog(log_debug, "get_conv(conv,out_arr[i],out_len[i],new_data,new_len)!=0");
continue;
}
if (!conn_info.conv_manager.c.is_conv_used(conv)) {
mylog(log_trace, "!conn_info.conv_manager.is_conv_used(conv)");
continue;
}
conn_info.conv_manager.c.update_active_time(conv);
address_t addr = conn_info.conv_manager.c.find_data_by_conv(conv);
dest_t dest;
dest.inner.fd_addr.fd = conn_info.local_listen_fd;
dest.inner.fd_addr.addr = addr;
dest.type = type_fd_addr;
delay_send(out_delay[i], dest, new_data, new_len);
}
}
static void fifo_cb(struct ev_loop *loop, struct ev_io *watcher, int revents) {
assert(!(revents & EV_ERROR));
int fifo_fd = watcher->fd;
char buf[buf_len];
int len = read(fifo_fd, buf, sizeof(buf));
if (len < 0) {
mylog(log_warn, "fifo read failed len=%d,errno=%s\n", len, get_sock_error());
return;
}
buf[len] = 0;
handle_command(buf);
}
static void delay_manager_cb(struct ev_loop *loop, struct ev_timer *watcher, int revents) {
assert(!(revents & EV_ERROR));
// uint64_t value;
// read(delay_manager.get_timer_fd(), &value, 8);
// mylog(log_trace,"events[idx].data.u64 == (u64_t)delay_manager.get_timer_fd()\n");
// do nothing
}
static void fec_encode_cb(struct ev_loop *loop, struct ev_timer *watcher, int revents) {
assert(!(revents & EV_ERROR));
conn_info_t &conn_info = *((conn_info_t *)watcher->data);
data_from_local_or_fec_timeout(conn_info, 1);
}
static void conn_timer_cb(struct ev_loop *loop, struct ev_timer *watcher, int revents) {
assert(!(revents & EV_ERROR));
uint64_t value;
conn_info_t &conn_info = *((conn_info_t *)watcher->data);
// read(conn_info.timer.get_timer_fd(), &value, 8);
conn_info.conv_manager.c.clear_inactive();
mylog(log_trace, "events[idx].data.u64==(u64_t)conn_info.timer.get_timer_fd()\n");
conn_info.stat.report_as_client();
if (debug_force_flush_fec) {
int out_n;
char **out_arr;
int *out_len;
my_time_t *out_delay;
dest_t dest;
dest.type = type_fd64;
dest.inner.fd64 = conn_info.remote_fd64;
dest.cook = 1;
from_normal_to_fec(conn_info, 0, 0, out_n, out_arr, out_len, out_delay);
for (int i = 0; i < out_n; i++) {
delay_send(out_delay[i], dest, out_arr[i], out_len[i]);
}
}
}
static void prepare_cb(struct ev_loop *loop, struct ev_prepare *watcher, int revents) {
assert(!(revents & EV_ERROR));
delay_manager.check();
}
int tunnel_client_event_loop() {
int i, j, k;
int ret;
int yes = 1;
// int epoll_fd;
conn_info_t *conn_info_p = new conn_info_t;
conn_info_t &conn_info = *conn_info_p; // huge size of conn_info,do not allocate on stack
int &local_listen_fd = conn_info.local_listen_fd;
new_listen_socket2(local_listen_fd, local_addr);
// epoll_fd = epoll_create1(0);
// assert(epoll_fd>0);
// const int max_events = 4096;
// struct epoll_event ev, events[max_events];
// if (epoll_fd < 0) {
// mylog(log_fatal,"epoll return %d\n", epoll_fd);
// myexit(-1);
// }
struct ev_loop *loop = ev_default_loop(0);
assert(loop != NULL);
conn_info.loop = loop;
// ev.events = EPOLLIN;
// ev.data.u64 = local_listen_fd;
// ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, local_listen_fd, &ev);
// if (ret!=0) {
// mylog(log_fatal,"add udp_listen_fd error\n");
// myexit(-1);
// }
struct ev_io local_listen_watcher;
local_listen_watcher.data = &conn_info;
ev_io_init(&local_listen_watcher, local_listen_cb, local_listen_fd, EV_READ);
ev_io_start(loop, &local_listen_watcher);
int &remote_fd = conn_info.remote_fd;
fd64_t &remote_fd64 = conn_info.remote_fd64;
assert(new_connected_socket2(remote_fd, remote_addr, out_addr, out_interface) == 0);
remote_fd64 = fd_manager.create(remote_fd);
mylog(log_debug, "remote_fd64=%llu\n", remote_fd64);
// ev.events = EPOLLIN;
// ev.data.u64 = remote_fd64;
// ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, remote_fd, &ev);
// if (ret!= 0) {
// mylog(log_fatal,"add raw_fd error\n");
// myexit(-1);
// }
struct ev_io remote_watcher;
remote_watcher.data = &conn_info;
remote_watcher.u64 = remote_fd64;
ev_io_init(&remote_watcher, remote_cb, remote_fd, EV_READ);
ev_io_start(loop, &remote_watcher);
// ev.events = EPOLLIN;
// ev.data.u64 = delay_manager.get_timer_fd();
// mylog(log_debug,"delay_manager.get_timer_fd()=%d\n",delay_manager.get_timer_fd());
// ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, delay_manager.get_timer_fd(), &ev);
// if (ret!= 0) {
// mylog(log_fatal,"add delay_manager.get_timer_fd() error\n");
// myexit(-1);
// }
delay_manager.set_loop_and_cb(loop, delay_manager_cb);
conn_info.fec_encode_manager.set_data(&conn_info);
conn_info.fec_encode_manager.set_loop_and_cb(loop, fec_encode_cb);
// u64_t tmp_fd64=conn_info.fec_encode_manager.get_timer_fd64();
// ev.events = EPOLLIN;
// ev.data.u64 = tmp_fd64;
// mylog(log_debug,"conn_info.fec_encode_manager.get_timer_fd64()=%llu\n",conn_info.fec_encode_manager.get_timer_fd64());
// ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd_manager.to_fd(tmp_fd64), &ev);
// if (ret!= 0) {
// mylog(log_fatal,"add fec_encode_manager.get_timer_fd64() error\n");
// myexit(-1);
// }
conn_info.timer.data = &conn_info;
ev_init(&conn_info.timer, conn_timer_cb);
ev_timer_set(&conn_info.timer, 0, timer_interval / 1000.0);
ev_timer_start(loop, &conn_info.timer);
// conn_info.timer.add_fd_to_epoll(epoll_fd);
// conn_info.timer.set_timer_repeat_us(timer_interval*1000);
// mylog(log_debug,"conn_info.timer.get_timer_fd()=%d\n",conn_info.timer.get_timer_fd());
struct ev_io fifo_watcher;
int fifo_fd = -1;
if (fifo_file[0] != 0) {
fifo_fd = create_fifo(fifo_file);
// ev.events = EPOLLIN;
// ev.data.u64 = fifo_fd;
// ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fifo_fd, &ev);
// if (ret!= 0) {
// mylog(log_fatal,"add fifo_fd to epoll error %s\n",strerror(errno));
// myexit(-1);
// }
mylog(log_info, "fifo_file=%s\n", fifo_file);
ev_io_init(&fifo_watcher, fifo_cb, fifo_fd, EV_READ);
ev_io_start(loop, &fifo_watcher);
}
ev_prepare prepare_watcher;
ev_init(&prepare_watcher, prepare_cb);
ev_prepare_start(loop, &prepare_watcher);
mylog(log_info, "now listening at %s\n", local_addr.get_str());
ev_run(loop, 0);
mylog(log_warn, "ev_run returned\n");
myexit(0);
/*
while(1)////////////////////////
{
if(about_to_exit) myexit(0);
int nfds = epoll_wait(epoll_fd, events, max_events, 180 * 1000);
if (nfds < 0) { //allow zero
if(errno==EINTR )
{
mylog(log_info,"epoll interrupted by signal continue\n");
}
else
{
mylog(log_fatal,"epoll_wait return %d,%s\n", nfds,strerror(errno));
myexit(-1);
}
}
int idx;
for (idx = 0; idx < nfds; ++idx) {
if(events[idx].data.u64==(u64_t)conn_info.timer.get_timer_fd())
{
}
else if (events[idx].data.u64 == (u64_t)fifo_fd)
{
}
else if (events[idx].data.u64 == (u64_t)local_listen_fd||events[idx].data.u64 == conn_info.fec_encode_manager.get_timer_fd64())
{
}
else if (events[idx].data.u64 == (u64_t)delay_manager.get_timer_fd()) {
}
else if(events[idx].data.u64>u32_t(-1) )
{
}
else
{
mylog(log_fatal,"unknown fd,this should never happen\n");
myexit(-1);
}
}
//delay_manager.check();
}*/
return 0;
}

464
tunnel_server.cpp Normal file
View File

@ -0,0 +1,464 @@
/*
* tunnel.cpp
*
* Created on: Oct 26, 2017
* Author: root
*/
#include "tunnel.h"
static void conn_timer_cb(struct ev_loop *loop, struct ev_timer *watcher, int revents);
static void fec_encode_cb(struct ev_loop *loop, struct ev_timer *watcher, int revents);
static void remote_cb(struct ev_loop *loop, struct ev_io *watcher, int revents);
enum tmp_mode_t { is_from_remote = 0,
is_fec_timeout,
is_conn_timer };
void data_from_remote_or_fec_timeout_or_conn_timer(conn_info_t &conn_info, fd64_t fd64, tmp_mode_t mode) {
int ret;
char data[buf_len];
int data_len;
u32_t conv;
// fd64_t fd64=events[idx].data.u64;
// mylog(log_trace,"events[idx].data.u64 >u32_t(-1),%llu\n",(u64_t)events[idx].data.u64);
// assert(fd_manager.exist_info(fd64));
// ip_port_t ip_port=fd_manager.get_info(fd64).ip_port;
// conn_info_t &conn_info=conn_manager.find(ip_port);
address_t &addr = conn_info.addr;
assert(conn_manager.exist(addr));
int &local_listen_fd = conn_info.local_listen_fd;
int out_n = -2;
char **out_arr;
int *out_len;
my_time_t *out_delay;
dest_t dest;
dest.inner.fd_addr.fd = local_listen_fd;
dest.inner.fd_addr.addr = addr;
dest.type = type_fd_addr;
dest.cook = 1;
if (mode == is_fec_timeout) {
assert(fd64 == 0);
// uint64_t value;
// if((ret=read(fd_manager.to_fd(fd64), &value, 8))!=8)
//{
// mylog(log_trace,"fd_manager.to_fd(fd64), &value, 8)!=8 ,%d\n",ret);
// continue;
// }
// if(value==0)
//{
// mylog(log_trace,"value==0\n");
// continue;
// }
// assert(value==1);
from_normal_to_fec(conn_info, 0, 0, out_n, out_arr, out_len, out_delay);
} else if (mode == is_conn_timer) {
assert(fd64 == 0);
// uint64_t value;
// read(conn_info.timer.get_timer_fd(), &value, 8);
conn_info.conv_manager.s.clear_inactive();
if (debug_force_flush_fec) {
from_normal_to_fec(conn_info, 0, 0, out_n, out_arr, out_len, out_delay);
}
conn_info.stat.report_as_server(addr);
return;
} else if (mode == is_from_remote) {
if (!fd_manager.exist(fd64)) // fd64 has been closed
{
mylog(log_warn, "!fd_manager.exist(fd64)\n");
return;
}
// fd64_t &fd64 =conn_info.remote_fd64;
assert(conn_info.conv_manager.s.is_data_used(fd64));
conv = conn_info.conv_manager.s.find_conv_by_data(fd64);
conn_info.conv_manager.s.update_active_time(conv);
conn_info.update_active_time();
int fd = fd_manager.to_fd(fd64);
data_len = recv(fd, data, max_data_len + 1, 0);
if (data_len == max_data_len + 1) {
mylog(log_warn, "huge packet from upper level, data_len > %d, packet truncated, dropped\n", max_data_len);
return;
}
mylog(log_trace, "received a packet from udp_fd,len:%d,conv=%d\n", data_len, conv);
if (data_len < 0) {
mylog(log_debug, "udp fd,recv_len<0 continue,%s\n", get_sock_error());
return;
}
if (!disable_mtu_warn && data_len >= mtu_warn) {
mylog(log_warn, "huge packet,data len=%d (>=%d).strongly suggested to set a smaller mtu at upper level,to get rid of this warn\n ", data_len, mtu_warn);
}
char *new_data;
int new_len;
put_conv(conv, data, data_len, new_data, new_len);
from_normal_to_fec(conn_info, new_data, new_len, out_n, out_arr, out_len, out_delay);
} else {
assert(0 == 1);
}
mylog(log_trace, "out_n=%d\n", out_n);
for (int i = 0; i < out_n; i++) {
delay_send(out_delay[i], dest, out_arr[i], out_len[i]);
}
}
static void local_listen_cb(struct ev_loop *loop, struct ev_io *watcher, int revents) {
assert(!(revents & EV_ERROR));
int local_listen_fd = watcher->fd;
int ret;
mylog(log_trace, "events[idx].data.u64 == (u64_t)local_listen_fd\n");
char data[buf_len];
int data_len;
address_t::storage_t udp_new_addr_in = {0};
socklen_t udp_new_addr_len = sizeof(address_t::storage_t);
if ((data_len = recvfrom(local_listen_fd, data, max_data_len + 1, 0,
(struct sockaddr *)&udp_new_addr_in, &udp_new_addr_len)) == -1) {
mylog(log_error, "recv_from error,this shouldnt happen,err=%s,but we can try to continue\n", get_sock_error());
return;
};
if (data_len == max_data_len + 1) {
mylog(log_warn, "huge packet, data_len > %d, packet truncated, dropped\n", max_data_len);
return;
}
address_t addr;
addr.from_sockaddr((struct sockaddr *)&udp_new_addr_in, udp_new_addr_len);
mylog(log_trace, "Received packet from %s,len: %d\n", addr.get_str(), data_len);
if (!disable_mtu_warn && data_len >= mtu_warn) ///////////////////////delete this for type 0 in furture
{
mylog(log_warn, "huge packet,data len=%d (>=%d).strongly suggested to set a smaller mtu at upper level,to get rid of this warn\n ", data_len, mtu_warn);
}
if (de_cook(data, data_len) != 0) {
mylog(log_debug, "de_cook error");
return;
}
if (!conn_manager.exist(addr)) {
if (conn_manager.mp.size() >= max_conn_num) {
mylog(log_warn, "new connection %s ignored bc max_conn_num exceed\n", addr.get_str());
return;
}
// conn_manager.insert(addr);
conn_info_t &conn_info = conn_manager.find_insert(addr);
conn_info.addr = addr;
conn_info.loop = ev_default_loop(0);
conn_info.local_listen_fd = local_listen_fd;
// u64_t fec_fd64=conn_info.fec_encode_manager.get_timer_fd64();
// mylog(log_debug,"fec_fd64=%llu\n",fec_fd64);
// ev.events = EPOLLIN;
// ev.data.u64 = fec_fd64;
// ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd_manager.to_fd(fec_fd64), &ev);
// fd_manager.get_info(fec_fd64).ip_port=ip_port;
conn_info.timer.data = &conn_info;
ev_init(&conn_info.timer, conn_timer_cb);
ev_timer_set(&conn_info.timer, 0, timer_interval / 1000.0);
ev_timer_start(loop, &conn_info.timer);
// conn_info.timer.add_fd64_to_epoll(epoll_fd);
// conn_info.timer.set_timer_repeat_us(timer_interval*1000);
// mylog(log_debug,"conn_info.timer.get_timer_fd64()=%llu\n",conn_info.timer.get_timer_fd64());
// u64_t timer_fd64=conn_info.timer.get_timer_fd64();
// fd_manager.get_info(timer_fd64).ip_port=ip_port;
conn_info.fec_encode_manager.set_data(&conn_info);
conn_info.fec_encode_manager.set_loop_and_cb(loop, fec_encode_cb);
mylog(log_info, "new connection from %s\n", addr.get_str());
}
conn_info_t &conn_info = conn_manager.find_insert(addr);
conn_info.update_active_time();
int out_n;
char **out_arr;
int *out_len;
my_time_t *out_delay;
from_fec_to_normal(conn_info, data, data_len, out_n, out_arr, out_len, out_delay);
mylog(log_trace, "out_n= %d\n", out_n);
for (int i = 0; i < out_n; i++) {
u32_t conv;
char *new_data;
int new_len;
if (get_conv(conv, out_arr[i], out_len[i], new_data, new_len) != 0) {
mylog(log_debug, "get_conv failed");
continue;
}
if (!conn_info.conv_manager.s.is_conv_used(conv)) {
if (conn_info.conv_manager.s.get_size() >= max_conv_num) {
mylog(log_warn, "ignored new udp connect bc max_conv_num exceed\n");
continue;
}
int new_udp_fd;
ret = new_connected_socket2(new_udp_fd, remote_addr, out_addr, out_interface);
if (ret != 0) {
mylog(log_warn, "[%s]new_connected_socket failed\n", addr.get_str());
continue;
}
fd64_t fd64 = fd_manager.create(new_udp_fd);
// ev.events = EPOLLIN;
// ev.data.u64 = fd64;
// ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, new_udp_fd, &ev);
conn_info.conv_manager.s.insert_conv(conv, fd64);
fd_manager.get_info(fd64).addr = addr;
ev_io &io_watcher = fd_manager.get_info(fd64).io_watcher;
io_watcher.u64 = fd64;
io_watcher.data = &conn_info;
ev_init(&io_watcher, remote_cb);
ev_io_set(&io_watcher, new_udp_fd, EV_READ);
ev_io_start(conn_info.loop, &io_watcher);
mylog(log_info, "[%s]new conv %x,fd %d created,fd64=%llu\n", addr.get_str(), conv, new_udp_fd, fd64);
}
conn_info.conv_manager.s.update_active_time(conv);
fd64_t fd64 = conn_info.conv_manager.s.find_data_by_conv(conv);
dest_t dest;
dest.type = type_fd64;
dest.inner.fd64 = fd64;
delay_send(out_delay[i], dest, new_data, new_len);
}
}
static void remote_cb(struct ev_loop *loop, struct ev_io *watcher, int revents) {
assert(!(revents & EV_ERROR));
conn_info_t &conn_info = *((conn_info_t *)watcher->data);
fd64_t fd64 = watcher->u64;
data_from_remote_or_fec_timeout_or_conn_timer(conn_info, fd64, is_from_remote);
}
static void fifo_cb(struct ev_loop *loop, struct ev_io *watcher, int revents) {
assert(!(revents & EV_ERROR));
int fifo_fd = watcher->fd;
char buf[buf_len];
int len = read(fifo_fd, buf, sizeof(buf));
if (len < 0) {
mylog(log_warn, "fifo read failed len=%d,errno=%s\n", len, get_sock_error());
return;
}
buf[len] = 0;
handle_command(buf);
}
static void delay_manager_cb(struct ev_loop *loop, struct ev_timer *watcher, int revents) {
assert(!(revents & EV_ERROR));
// uint64_t value;
// read(delay_manager.get_timer_fd(), &value, 8);
// mylog(log_trace,"events[idx].data.u64 == (u64_t)delay_manager.get_timer_fd()\n");
// do nothing
}
static void fec_encode_cb(struct ev_loop *loop, struct ev_timer *watcher, int revents) {
assert(!(revents & EV_ERROR));
conn_info_t &conn_info = *((conn_info_t *)watcher->data);
data_from_remote_or_fec_timeout_or_conn_timer(conn_info, 0, is_fec_timeout);
}
static void conn_timer_cb(struct ev_loop *loop, struct ev_timer *watcher, int revents) {
assert(!(revents & EV_ERROR));
conn_info_t &conn_info = *((conn_info_t *)watcher->data);
data_from_remote_or_fec_timeout_or_conn_timer(conn_info, 0, is_conn_timer);
}
static void prepare_cb(struct ev_loop *loop, struct ev_prepare *watcher, int revents) {
assert(!(revents & EV_ERROR));
delay_manager.check();
}
static void global_timer_cb(struct ev_loop *loop, struct ev_timer *watcher, int revents) {
assert(!(revents & EV_ERROR));
// uint64_t value;
// read(timer.get_timer_fd(), &value, 8);
conn_manager.clear_inactive();
mylog(log_trace, "events[idx].data.u64==(u64_t)timer.get_timer_fd()\n");
}
int tunnel_server_event_loop() {
int i, j, k;
int ret;
int yes = 1;
// int epoll_fd;
// int remote_fd;
int local_listen_fd;
new_listen_socket2(local_listen_fd, local_addr);
// epoll_fd = epoll_create1(0);
// assert(epoll_fd>0);
// const int max_events = 4096;
// struct epoll_event ev, events[max_events];
// if (epoll_fd < 0) {
// mylog(log_fatal,"epoll return %d\n", epoll_fd);
// myexit(-1);
// }
struct ev_loop *loop = ev_default_loop(0);
assert(loop != NULL);
// ev.events = EPOLLIN;
// ev.data.u64 = local_listen_fd;
// ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, local_listen_fd, &ev);
// if (ret!=0) {
// mylog(log_fatal,"add udp_listen_fd error\n");
// myexit(-1);
// }
struct ev_io local_listen_watcher;
ev_io_init(&local_listen_watcher, local_listen_cb, local_listen_fd, EV_READ);
ev_io_start(loop, &local_listen_watcher);
// ev.events = EPOLLIN;
// ev.data.u64 = delay_manager.get_timer_fd();
// ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, delay_manager.get_timer_fd(), &ev);
// if (ret!= 0) {
// mylog(log_fatal,"add delay_manager.get_timer_fd() error\n");
// myexit(-1);
// }
delay_manager.set_loop_and_cb(loop, delay_manager_cb);
// mylog(log_debug," delay_manager.get_timer_fd() =%d\n", delay_manager.get_timer_fd());
mylog(log_info, "now listening at %s\n", local_addr.get_str());
// my_timer_t timer;
// timer.add_fd_to_epoll(epoll_fd);
// timer.set_timer_repeat_us(timer_interval*1000);
ev_timer global_timer;
ev_init(&global_timer, global_timer_cb);
ev_timer_set(&global_timer, 0, timer_interval / 1000.0);
ev_timer_start(loop, &global_timer);
// mylog(log_debug," timer.get_timer_fd() =%d\n",timer.get_timer_fd());
struct ev_io fifo_watcher;
int fifo_fd = -1;
if (fifo_file[0] != 0) {
fifo_fd = create_fifo(fifo_file);
// ev.events = EPOLLIN;
// ev.data.u64 = fifo_fd;
// ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fifo_fd, &ev);
// if (ret!= 0) {
// mylog(log_fatal,"add fifo_fd to epoll error %s\n",strerror(errno));
// myexit(-1);
//}
ev_io_init(&fifo_watcher, fifo_cb, fifo_fd, EV_READ);
ev_io_start(loop, &fifo_watcher);
mylog(log_info, "fifo_file=%s\n", fifo_file);
}
ev_prepare prepare_watcher;
ev_init(&prepare_watcher, prepare_cb);
ev_prepare_start(loop, &prepare_watcher);
ev_run(loop, 0);
mylog(log_warn, "ev_run returned\n");
myexit(0);
/*
while(1)////////////////////////
{
if(about_to_exit) myexit(0);
int nfds = epoll_wait(epoll_fd, events, max_events, 180 * 1000);
if (nfds < 0) { //allow zero
if(errno==EINTR )
{
mylog(log_info,"epoll interrupted by signal,continue\n");
}
else
{
mylog(log_fatal,"epoll_wait return %d,%s\n", nfds,strerror(errno));
myexit(-1);
}
}
int idx;
for (idx = 0; idx < nfds; ++idx)
{
if(events[idx].data.u64==(u64_t)timer.get_timer_fd())
{
}
else if (events[idx].data.u64 == (u64_t)fifo_fd)
{
}
else if (events[idx].data.u64 == (u64_t)local_listen_fd)
{
}
else if (events[idx].data.u64 == (u64_t)delay_manager.get_timer_fd()) {
}
else if (events[idx].data.u64 >u32_t(-1))
{
}
else
{
mylog(log_fatal,"unknown fd,this should never happen\n");
myexit(-1);
}
}
}*/
return 0;
}