-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.cpp
172 lines (143 loc) · 5.98 KB
/
main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
#include <iostream>
#include <sstream>
#include <algorithm>
#include <docopt.h>
#include <uvw/loop.hpp>
#include <uvw/udp.hpp>
#include <uvw/timer.hpp>
#include <date.h>
#include <tz.h>
enum class Mode { Responder, Requester };
std::ostream& operator<< (std::ostream&, Mode);
struct ProgramOptions
{
Mode mode;
std::string bind_ip;
unsigned short bind_port;
std::string response;
std::string remote_ip;
unsigned short remote_port;
std::string request;
unsigned short repeat;
};
std::ostream& operator<< (std::ostream&, const ProgramOptions&);
const ProgramOptions parse_program_options(int, char* []);
std::string get_time_pretty();
int main(int argc, char* argv[])
{
const auto options = parse_program_options(argc, argv);
std::cout << std::endl << options << std::endl;
auto loop = uvw::Loop::getDefault();
auto socket = loop->resource<uvw::UDPHandle>();
auto timer = loop->resource<uvw::TimerHandle>();
auto on_error = [](const auto& err, const auto&)
{
std::cout << "libuv error! name: " << err.name() << " what: " << err.what() << std::endl;
std::abort();
};
socket->on<uvw::ErrorEvent>(on_error);
timer->on<uvw::ErrorEvent>(on_error);
auto on_timer_requester = [socket, &options](const auto&, const auto&)
{
std::cout << get_time_pretty() << " Send request <" << options.request << ">" << std::endl;
auto data = std::make_unique<char[]>( options.request.size() );
std::copy_n( options.request.c_str(), options.request.size(), data.get() );
socket->send( options.remote_ip, options.remote_port, std::move(data), static_cast<unsigned int>(options.request.size()) );
};
auto on_data_requester = [](const auto& event, const auto&)
{
std::cout << get_time_pretty() << " Received from " << event.sender.ip << ":" << event.sender.port << ", data: " << std::string{event.data.get(), event.length} << std::endl;
};
auto on_data_responder = [&options](const auto& event, auto& soc)
{
std::cout << get_time_pretty() << " Received from " << event.sender.ip << ":" << event.sender.port << ", data: " << std::string{event.data.get(), event.length} << std::endl;
std::cout << get_time_pretty() << " Send respond <" << options.response << ">" << std::endl;
auto data = std::make_unique<char[]>( options.response.size() );
std::copy_n( options.response.c_str(), options.response.size(), data.get() );
soc.send( event.sender.ip, event.sender.port, std::move(data), static_cast<unsigned int>(options.response.size()) );
};
socket->bind(options.bind_ip, options.bind_port);
switch (options.mode)
{
case Mode::Requester:
socket->on<uvw::UDPDataEvent>(on_data_requester);
socket->recv();
timer->on<uvw::TimerEvent>(on_timer_requester);
timer->start(std::chrono::milliseconds{42}, std::chrono::seconds{options.repeat});
break;
case Mode::Responder:
socket->on<uvw::UDPDataEvent>(on_data_responder);
socket->recv();
break;
}
loop->run();
socket->clear();
socket->close();
timer->clear();
timer->close();
return 0;
}
static const std::string usage =
R"(Ping-Pong
Usage:
ping_pong responder [--bind_ip <bind ip>] --bind_port <bind port> [--response <pong>]
ping_pong requester [--bind_ip <bind ip>] --bind_port <bind port> --remote_ip <remote ip> --remote_port <remote port> [--request <ping>] [--repeat <seconds>]
ping_pong (-h | --help)
Options:
-h --help Show this message
--bind_ip <bind ip> Address for pending data [default: 0.0.0.0]
--bind_port <bind port> Number port for pending data
--response <pong> Response message [default: pong]
--remote_ip <remote ip> Address for send request
--remote_port <remote port> Number port for send request
--request <ping> Request message [default: ping]
--repeat <seconds> Repeat time, in seconds [default: 5]
)";
const ProgramOptions parse_program_options(int argc, char* argv[])
{
auto opt = docopt::docopt(usage, {argv + 1, argv + argc} );
Mode mode = ( opt["responder"].asBool() ) ? Mode::Responder : Mode::Requester;
long bind_port = opt["--bind_port"].asLong();
std::string response = (mode == Mode::Responder) ? opt["--response"].asString() : "";
std::string remote_ip = (opt["--remote_ip"]) ? opt["--remote_ip"].asString() : "";
long remote_port = (opt["--remote_port"]) ? opt["--remote_port"].asLong() : 0;
std::string request = (mode == Mode::Requester) ? opt["--request"].asString() : "";
long repeat = (mode == Mode::Requester) ? opt["--repeat"].asLong() : 0;
using ushort = unsigned short;
return ProgramOptions{mode,
opt["--bind_ip"].asString(), static_cast<ushort>(bind_port), response,
std::move(remote_ip), static_cast<ushort>(remote_port), request, static_cast<ushort>(repeat)
};
}
std::ostream& operator<< (std::ostream& os, Mode m)
{
switch (m)
{
case Mode::Responder:
os << "responder"; break;
case Mode::Requester:
os << "requester"; break;
}
return os;
}
std::ostream& operator<< (std::ostream& os, const ProgramOptions& po)
{
os << "Mode: " << po.mode << std::endl;
os << "Bind IP: " << po.bind_ip << std::endl;
os << "Bind port: " << po.bind_port << std::endl;
os << "Response message: " << po.response << std::endl;
os << "Remote IP: " << po.remote_ip << std::endl;
os << "Remote port: " << po.remote_port << std::endl;
os << "Request message: " << po.request << std::endl;
os << "Repeat time: " << po.repeat << std::endl;
return os;
}
std::string get_time_pretty()
{
using namespace date;
using namespace std::chrono;
auto now = make_zoned( current_zone(), system_clock::now() );
std::ostringstream ss;
ss << "[ " << now << " ]";
return ss.str();
}