Skip to content

Commit ed9fa5a

Browse files
Merge pull request #19 from NikolasK-source/feature-ipv6-support
Feature ipv6 support
2 parents 666170d + 6b0e3ad commit ed9fa5a

File tree

3 files changed

+95
-31
lines changed

3 files changed

+95
-31
lines changed

src/Modbus_TCP_Client.cpp

Lines changed: 71 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,15 @@ namespace TCP {
2323

2424
static constexpr int MAX_REGS = 0x10000;
2525

26-
Client::Client(const std::string &ip, unsigned short port, modbus_mapping_t *mapping, std::size_t tcp_timeout) {
26+
Client::Client(const std::string &host,
27+
const std::string &service,
28+
modbus_mapping_t *mapping,
29+
std::size_t tcp_timeout) {
30+
const char *host_str = "::";
31+
if (!(host.empty() || host == "any")) host_str = host.c_str();
32+
2733
// create modbus object
28-
modbus = modbus_new_tcp(ip.c_str(), static_cast<int>(port));
34+
modbus = modbus_new_tcp_pi(host_str, service.c_str());
2935
if (modbus == nullptr) {
3036
const std::string error_msg = modbus_strerror(errno);
3137
throw std::runtime_error("failed to create modbus instance: " + error_msg);
@@ -62,9 +68,15 @@ Client::Client(const std::string &ip, unsigned short port, modbus_mapping_t *map
6268
#endif
6369
}
6470

65-
Client::Client(const std::string &ip, unsigned short port, modbus_mapping_t **mappings, std::size_t tcp_timeout) {
71+
Client::Client(const std::string &host,
72+
const std::string &service,
73+
modbus_mapping_t **mappings,
74+
std::size_t tcp_timeout) {
75+
const char *host_str = "::";
76+
if (!(host.empty() || host == "any")) host_str = host.c_str();
77+
6678
// create modbus object
67-
modbus = modbus_new_tcp(ip.c_str(), static_cast<int>(port));
79+
modbus = modbus_new_tcp_pi(host_str, service.c_str());
6880
if (modbus == nullptr) {
6981
const std::string error_msg = modbus_strerror(errno);
7082
throw std::runtime_error("failed to create modbus instance: " + error_msg);
@@ -100,10 +112,14 @@ Client::Client(const std::string &ip, unsigned short port, modbus_mapping_t **ma
100112

101113
void Client::listen() {
102114
// create tcp socket
103-
socket = modbus_tcp_listen(modbus, 1);
115+
socket = modbus_tcp_pi_listen(modbus, 1);
104116
if (socket == -1) {
105-
const std::string error_msg = modbus_strerror(errno);
106-
throw std::runtime_error("failed to create tcp socket: " + error_msg);
117+
if (errno == ECONNREFUSED) {
118+
throw std::runtime_error("failed to create tcp socket: unknown or invalid service");
119+
} else {
120+
const std::string error_msg = modbus_strerror(errno);
121+
throw std::runtime_error("failed to create tcp socket: " + error_msg);
122+
}
107123
}
108124

109125
// set socket options
@@ -147,7 +163,6 @@ void Client::set_tcp_timeout(std::size_t tcp_timeout) {
147163
}
148164
#endif
149165

150-
151166
Client::~Client() {
152167
if (modbus != nullptr) {
153168
modbus_close(modbus);
@@ -164,27 +179,68 @@ void Client::set_debug(bool debug) {
164179
}
165180
}
166181

182+
/**
183+
* @brief convert socket address to string
184+
* @param sa socket address
185+
* @return sa as string
186+
*/
187+
static std::string sockaddr_to_str(const sockaddr_storage &sa) {
188+
char buffer[INET6_ADDRSTRLEN + 1];
189+
if (sa.ss_family == AF_INET) {
190+
auto peer_in = reinterpret_cast<const struct sockaddr_in *>(&sa);
191+
inet_ntop(sa.ss_family, &peer_in->sin_addr, buffer, sizeof(buffer));
192+
std::ostringstream sstr;
193+
return buffer;
194+
} else if (sa.ss_family == AF_INET6) {
195+
auto peer_in6 = reinterpret_cast<const struct sockaddr_in6 *>(&sa);
196+
inet_ntop(sa.ss_family, &peer_in6->sin6_addr, buffer, sizeof(buffer));
197+
std::ostringstream sstr;
198+
sstr << '[' << buffer << ']';
199+
return sstr.str();
200+
} else {
201+
return "UNKNOWN";
202+
}
203+
}
204+
205+
std::string Client::get_listen_addr() {
206+
struct sockaddr_storage sock_addr;
207+
socklen_t len = sizeof(sock_addr);
208+
int tmp = getsockname(socket, reinterpret_cast<struct sockaddr *>(&sock_addr), &len);
209+
210+
if (tmp < 0) {
211+
const std::string error_msg = modbus_strerror(errno);
212+
throw std::runtime_error("getsockname failed: " + error_msg);
213+
}
214+
215+
std::ostringstream sstr;
216+
sstr << sockaddr_to_str(sock_addr);
217+
// the port entries have the same offset and size in sockaddr_in and sockaddr_in6
218+
sstr << ':' << htons(reinterpret_cast<const struct sockaddr_in *>(&sock_addr)->sin_port);
219+
220+
return sstr.str();
221+
}
222+
167223
std::string Client::connect_client() {
168-
int tmp = modbus_tcp_accept(modbus, &socket);
224+
int tmp = modbus_tcp_pi_accept(modbus, &socket);
169225
if (tmp < 0) {
170226
const std::string error_msg = modbus_strerror(errno);
171227
throw std::runtime_error("modbus_tcp_accept failed: " + error_msg);
172228
}
173229

174-
struct sockaddr_in peer_addr;
175-
socklen_t len = sizeof(peer_addr);
230+
struct sockaddr_storage peer_addr;
231+
socklen_t len = sizeof(peer_addr);
176232
tmp = getpeername(modbus_get_socket(modbus), reinterpret_cast<struct sockaddr *>(&peer_addr), &len);
177233

178234
if (tmp < 0) {
179235
const std::string error_msg = modbus_strerror(errno);
180236
throw std::runtime_error("getpeername failed: " + error_msg);
181237
}
182238

183-
char buffer[INET_ADDRSTRLEN];
184-
inet_ntop(peer_addr.sin_family, &peer_addr.sin_addr, buffer, sizeof(buffer));
185-
186239
std::ostringstream sstr;
187-
sstr << buffer << ':' << htons(peer_addr.sin_port);
240+
241+
sstr << sockaddr_to_str(peer_addr);
242+
// the port entries have the same offset and size in sockaddr_in and sockaddr_in6
243+
sstr << ':' << htons(reinterpret_cast<const struct sockaddr_in *>(&peer_addr)->sin_port);
188244

189245
return sstr.str();
190246
}

src/Modbus_TCP_Client.hpp

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,27 +26,27 @@ class Client {
2626
public:
2727
/*! \brief create modbus client (TCP server)
2828
*
29-
* @param ip ip to listen for incoming connections (default 0.0.0.0 (any))
30-
* @param port port to listen for incoming connections (default 502)
29+
* @param host host to listen for incoming connections (default 0.0.0.0 (any))
30+
* @param service service/port to listen for incoming connections (default 502)
3131
* @param mapping modbus mapping object for all client ids
3232
* nullptr: an mapping object with maximum size is generated
3333
* @param tcp_timeout tcp timeout (currently only available on linux systems)
3434
*/
35-
explicit Client(const std::string &ip = "0.0.0.0",
36-
short unsigned int port = 502,
35+
explicit Client(const std::string &host = "any",
36+
const std::string &service = "502",
3737
modbus_mapping_t *mapping = nullptr,
3838
std::size_t tcp_timeout = 5);
3939

4040
/**
4141
* @brief create modbus client (TCP server) with dedicated mappings per client id
4242
*
43-
* @param ip ip to listen for incoming connections
44-
* @param port port to listen for incoming connections
43+
* @param host host to listen for incoming connections
44+
* @param service service/port to listen for incoming connections
4545
* @param mappings modbus mappings (one for each possible id)
4646
* @param tcp_timeout tcp timeout (currently only available on linux systems)
4747
*/
48-
Client(const std::string &ip,
49-
short unsigned int port,
48+
Client(const std::string &host,
49+
const std::string &service,
5050
modbus_mapping_t *mappings[MAX_CLIENT_IDS],
5151
std::size_t tcp_timeout = 5);
5252

@@ -61,6 +61,12 @@ class Client {
6161
*/
6262
void set_debug(bool debug);
6363

64+
/** \brief get the address the tcp server is listening on
65+
*
66+
* @return server listening address
67+
*/
68+
std::string get_listen_addr();
69+
6470
/*! \brief wait for client to connect
6571
*
6672
* @return ip of the connected client

src/main.cpp

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,12 @@ int main(int argc, char **argv) {
9999

100100
// all command line arguments
101101
// clang-format off
102-
options.add_options()("i,ip",
103-
"ip to listen for incoming connections",
104-
cxxopts::value<std::string>()->default_value("0.0.0.0"))
105-
("p,port",
106-
"port to listen for incoming connections",
107-
cxxopts::value<std::uint16_t>()->default_value("502"))
102+
options.add_options()("i,host",
103+
"host to listen for incoming connections",
104+
cxxopts::value<std::string>()->default_value("any"))
105+
("p,service",
106+
"service or port to listen for incoming connections",
107+
cxxopts::value<std::string>()->default_value("502"))
108108
("n,name-prefix",
109109
"shared memory name prefix",
110110
cxxopts::value<std::string>()->default_value("modbus_"))
@@ -311,8 +311,8 @@ int main(int argc, char **argv) {
311311
// create modbus client
312312
std::unique_ptr<Modbus::TCP::Client> client;
313313
try {
314-
client = std::make_unique<Modbus::TCP::Client>(args["ip"].as<std::string>(),
315-
args["port"].as<uint16_t>(),
314+
client = std::make_unique<Modbus::TCP::Client>(args["host"].as<std::string>(),
315+
args["service"].as<std::string>(),
316316
mb_mappings.data(),
317317
#ifdef OS_LINUX
318318
args["tcp-timeout"].as<std::size_t>());
@@ -336,6 +336,8 @@ int main(int argc, char **argv) {
336336
return EX_SOFTWARE;
337337
}
338338

339+
std::cerr << "Listening on " << client->get_listen_addr() << " for connections." << std::endl;
340+
339341
// connection loop
340342
do {
341343
// connect client

0 commit comments

Comments
 (0)