This commit is contained in:
2020-02-06 16:35:08 +01:00
parent bd4a20b643
commit da31a95dce
15 changed files with 606 additions and 1 deletions
+3
View File
@@ -0,0 +1,3 @@
[submodule "Makefiles"]
path = Makefiles
url = https://gogs.dierkse.nl/JDierkse/Makefiles.git
+1
View File
@@ -0,0 +1 @@
../../Logging
-1
View File
@@ -1 +0,0 @@
../../Makefiles
Submodule
+1
Submodule Makefiles added at d82c8a2047
+12
View File
@@ -0,0 +1,12 @@
#include "BroadcastClientImpl.h"
namespace Network {
BroadcastClientImpl::BroadcastClientImpl(const std::string& listenAddress, int broadcastPort, std::function<void(const std::string&)> callback) :
UdpClientImpl(listenAddress, "255.255.255.255", broadcastPort, callback)
{
Receive();
}
} // namespace Network
+19
View File
@@ -0,0 +1,19 @@
#ifndef NETWORK_BROADCASTCLIENTIMPL_H
#define NETWORK_BROADCASTCLIENTIMPL_H
#include <functional>
#include <string>
#include "UdpClientImpl.h"
namespace Network {
class BroadcastClientImpl : public UdpClientImpl
{
public:
BroadcastClientImpl(const std::string& listenAddress, int broadcastPort, std::function<void(const std::string&)> callback);
};
} // namespace Network
#endif // NETWORK_BROADCASTCLIENTIMPL_H
+11
View File
@@ -0,0 +1,11 @@
#include "BroadcastServerImpl.h"
namespace Network {
BroadcastServerImpl::BroadcastServerImpl(const std::string& listenAddress, int broadcastPort, std::function<void(const std::string&)> callback) :
UdpClientImpl(listenAddress, "255.255.255.255", broadcastPort, callback)
{
}
} // namespace Network
+19
View File
@@ -0,0 +1,19 @@
#ifndef NETWORK_BROADCASTSERVER_IMPL_H
#define NETWORK_BROADCASTSERVER_IMPL_H
#include <functional>
#include <string>
#include "UdpClientImpl.h"
namespace Network {
class BroadcastServerImpl : public UdpClientImpl
{
public:
BroadcastServerImpl(const std::string& listenAddress, int broadcastPort, std::function<void(const std::string&)> callback);
};
} // namespace Network
#endif // NETWORK_BROADCASTSERVERIMPL_H
+85
View File
@@ -0,0 +1,85 @@
#include <chrono>
#include "IcmpClientImpl.h"
namespace Network {
IcmpClientImpl::IcmpClientImpl() :
m_socket(m_ioContext, asio::ip::icmp::v4()),
m_done(false)
{
StartReceive();
m_thread = std::thread([&] { m_ioContext.run(); });
#ifdef __linux__
pthread_setname_np(m_thread.native_handle(), "IcmpClientImpl");
#endif
}
IcmpClientImpl::~IcmpClientImpl()
{
m_ioContext.stop();
m_thread.join();
}
bool IcmpClientImpl::Ping(const std::string& targetAddress)
{
m_done = false;
SendEchoRequest(asio::ip::icmp::endpoint(asio::ip::address::from_string(targetAddress), 0), std::string());
std::unique_lock<std::mutex> lock(m_mutex);
if (m_condition.wait_for(lock, std::chrono::seconds(3), [&] { return m_done; }))
return true;
return false;
}
void IcmpClientImpl::SendEchoRequest(const asio::ip::icmp::endpoint& targetEndpoint, const std::string& data)
{
Internal::IcmpHeader echoRequest;
echoRequest.type(Internal::IcmpHeader::echo_request);
echoRequest.code(0);
echoRequest.identifier(GetIdentifier());
echoRequest.sequence_number(0);
Internal::ComputeChecksum(echoRequest, data.begin(), data.end());
asio::streambuf requestBuffer;
std::ostream os(&requestBuffer);
os << echoRequest << data;
m_socket.send_to(requestBuffer.data(), targetEndpoint);
}
void IcmpClientImpl::StartReceive()
{
m_replyBuffer.consume(m_replyBuffer.size());
m_socket.async_receive(m_replyBuffer.prepare(65536), std::bind(&IcmpClientImpl::HandleReceive, this, std::placeholders::_1, std::placeholders::_2));
}
void IcmpClientImpl::HandleReceive(const asio::error_code& error, std::size_t length)
{
m_replyBuffer.commit(length);
std::istream is(&m_replyBuffer);
Internal::Ipv4Header ipv4Header;
Internal::IcmpHeader icmpHeader;
is >> ipv4Header >> icmpHeader;
if (is && icmpHeader.type() == Internal::IcmpHeader::echo_reply
&& icmpHeader.identifier() == GetIdentifier()
&& icmpHeader.sequence_number() == 0)
{
m_done = true;
m_condition.notify_all();
}
StartReceive();
}
unsigned short IcmpClientImpl::GetIdentifier()
{
return static_cast<unsigned short>(::getpid());
}
} // namespace Network
+46
View File
@@ -0,0 +1,46 @@
#ifndef NETWORK_ICMPCLIENTIMPL_H
#define NETWORK_ICMPCLIENTIMPL_H
#include <condition_variable>
#include <functional>
#include <mutex>
#include <string>
#include <thread>
#include <vector>
#include "asio.h"
#include "Ipv4Header.h"
#include "IcmpHeader.h"
namespace Network {
class IcmpClientImpl
{
public:
IcmpClientImpl();
~IcmpClientImpl();
public:
bool Ping(const std::string& targetAddress);
private:
void SendEchoRequest(const asio::ip::icmp::endpoint& targetEndpoint, const std::string& data);
void StartReceive();
void HandleReceive(const asio::error_code& error, std::size_t length);
unsigned short GetIdentifier();
private:
std::thread m_thread;
std::mutex m_mutex;
std::condition_variable m_condition;
asio::io_context m_ioContext;
asio::ip::icmp::socket m_socket;
asio::streambuf m_replyBuffer;
bool m_done;
};
} // namespace Network
#endif // NETWORK_ICMPCLIENTIMPL_H
+87
View File
@@ -0,0 +1,87 @@
#ifndef ICMPHEADER_H
#define ICMPHEADER_H
#include <istream>
#include "asio.h"
namespace Network {
namespace Internal {
class IcmpHeader
{
public:
enum
{
echo_reply = 0,
destination_unreachable = 3,
source_quench = 4,
redirect = 5,
echo_request = 8,
time_exceeded = 11,
parameter_problem = 12,
timestamp_request = 13,
timestamp_reply = 14,
info_request = 15,
info_reply = 16,
address_request = 17,
address_reply = 18
};
IcmpHeader() { std::fill(rep_, rep_ + sizeof(rep_), 0); }
unsigned char type() const { return rep_[0]; }
unsigned char code() const { return rep_[1]; }
unsigned short checksum() const { return decode(2, 3); }
unsigned short identifier() const { return decode(4, 5); }
unsigned short sequence_number() const { return decode(6, 7); }
void type(unsigned char n) { rep_[0] = n; }
void code(unsigned char n) { rep_[1] = n; }
void checksum(unsigned short n) { encode(2, 3, n); }
void identifier(unsigned short n) { encode(4, 5, n); }
void sequence_number(unsigned short n) { encode(6, 7, n); }
friend std::istream& operator>>(std::istream& is, IcmpHeader& header)
{ return is.read(reinterpret_cast<char*>(header.rep_), 8); }
friend std::ostream& operator<<(std::ostream& os, const IcmpHeader& header)
{ return os.write(reinterpret_cast<const char*>(header.rep_), 8); }
private:
unsigned short decode(int a, int b) const
{ return (rep_[a] << 8) + rep_[b]; }
void encode(int a, int b, unsigned short n)
{
rep_[a] = static_cast<unsigned char>(n >> 8);
rep_[b] = static_cast<unsigned char>(n & 0xFF);
}
unsigned char rep_[8];
};
template <typename Iterator>
void ComputeChecksum(IcmpHeader& header,
Iterator body_begin, Iterator body_end)
{
unsigned int sum = (header.type() << 8) + header.code()
+ header.identifier() + header.sequence_number();
Iterator body_iter = body_begin;
while (body_iter != body_end)
{
sum += (static_cast<unsigned char>(*body_iter++) << 8);
if (body_iter != body_end)
sum += static_cast<unsigned char>(*body_iter++);
}
sum = (sum >> 16) + (sum & 0xFFFF);
sum += (sum >> 16);
header.checksum(static_cast<unsigned short>(~sum));
}
} // namespace Internal
} // namespace Network
#endif // ICMPHEADER_H
+63
View File
@@ -0,0 +1,63 @@
#ifndef IPV4HEADER_H
#define IPV4HEADER_H
#include <istream>
#include "asio.h"
namespace Network {
namespace Internal {
class Ipv4Header
{
public:
Ipv4Header() { std::fill(rep_, rep_ + sizeof(rep_), 0); }
unsigned char version() const { return (rep_[0] >> 4) & 0xF; }
unsigned short header_length() const { return (rep_[0] & 0xF) * 4; }
unsigned char type_of_service() const { return rep_[1]; }
unsigned short total_length() const { return decode(2, 3); }
unsigned short identification() const { return decode(4, 5); }
bool dont_fragment() const { return (rep_[6] & 0x40) != 0; }
bool more_fragments() const { return (rep_[6] & 0x20) != 0; }
unsigned short fragment_offset() const { return decode(6, 7) & 0x1FFF; }
unsigned int time_to_live() const { return rep_[8]; }
unsigned char protocol() const { return rep_[9]; }
unsigned short header_checksum() const { return decode(10, 11); }
asio::ip::address_v4 source_address() const
{
asio::ip::address_v4::bytes_type bytes = { { rep_[12], rep_[13], rep_[14], rep_[15] } };
return asio::ip::address_v4(bytes);
}
asio::ip::address_v4 destination_address() const
{
asio::ip::address_v4::bytes_type bytes = { { rep_[16], rep_[17], rep_[18], rep_[19] } };
return asio::ip::address_v4(bytes);
}
friend std::istream& operator>>(std::istream& is, Ipv4Header& header)
{
is.read(reinterpret_cast<char*>(header.rep_), 20);
if (header.version() != 4)
is.setstate(std::ios::failbit);
std::streamsize options_length = header.header_length() - 20;
if (options_length < 0 || options_length > 40)
is.setstate(std::ios::failbit);
else
is.read(reinterpret_cast<char*>(header.rep_) + 20, options_length);
return is;
}
private:
unsigned short decode(int a, int b) const
{ return (rep_[a] << 8) + rep_[b]; }
unsigned char rep_[60];
};
} // namespace Internal
} // namespace Network
#endif // IPV4HEADER_H
+13
View File
@@ -0,0 +1,13 @@
#include "MulticastClientImpl.h"
namespace Network {
MulticastClientImpl::MulticastClientImpl(const std::string& listenAddress, const std::string& multicastAddress, int multicastPort, std::function<void(const std::string&)> callback) :
UdpClientImpl(listenAddress, multicastAddress, multicastPort, callback)
{
EnableMulticast();
Receive();
}
} // namespace Network
+19
View File
@@ -0,0 +1,19 @@
#ifndef NETWORK_MULTICASTCLIENTIMPL_H
#define NETWORK_MULTICASTCLIENTIMPL_H
#include <functional>
#include <string>
#include "UdpClientImpl.h"
namespace Network {
class MulticastClientImpl : public UdpClientImpl
{
public:
MulticastClientImpl(const std::string& listenAddress, const std::string& multicastAddress, int multicastPort, std::function<void(const std::string&)> callback);
};
} // namespace Network
#endif // NETWORK_MULTICASTCLIENTIMPL_H
+171
View File
@@ -0,0 +1,171 @@
#include "Logging.h"
#include "UdpClientImpl.h"
namespace Network {
UdpClientImpl::UdpClientImpl(const std::string& listenAddress, const std::string& targetAddress, int targetPort, std::function<void(const std::string&)> callback) :
m_bufferLength(1024),
m_deadline(m_ioServiceAnswer),
m_listenAddress(asio::ip::address::from_string(listenAddress)),
m_targetAddress(asio::ip::address::from_string(targetAddress)),
m_targetPort(targetPort),
m_receiveSocket(m_ioService),
m_answerSocket(m_ioServiceAnswer),
m_targetEndpoint(m_targetAddress, m_targetPort),
m_callback(callback)
{
m_thread = std::thread([&] { m_ioService.run(); });
#ifdef __linux__
pthread_setname_np(m_thread.native_handle(), "UdpClientImpl");
#endif
m_socketReceiveData.reserve(m_bufferLength);
m_socketAnswerData.reserve(m_bufferLength);
asio::ip::udp::endpoint listenEndpoint(m_targetAddress, m_targetPort);
m_receiveSocket.open(listenEndpoint.protocol());
m_receiveSocket.set_option(asio::ip::udp::socket::reuse_address(true));
m_receiveSocket.bind(listenEndpoint);
}
UdpClientImpl::~UdpClientImpl()
{
m_ioService.stop();
m_thread.join();
}
void UdpClientImpl::Receive()
{
ReceiveSocket();
m_deadline.expires_at(asio::system_timer::time_point(std::chrono::system_clock::duration::max()));
CheckDeadline();
}
std::string UdpClientImpl::Send(const std::string& data, int sourcePort)
{
asio::ip::udp::socket sendSocket(m_ioService);
sendSocket.open(asio::ip::udp::v4());
sendSocket.set_option(asio::ip::udp::socket::reuse_address(true));
if (sourcePort != 0)
{
asio::ip::udp::endpoint sendEndpoint(m_listenAddress, sourcePort);
sendSocket.bind(sendEndpoint);
}
try
{
sendSocket.connect(m_targetEndpoint);
asio::ip::udp::endpoint listenEndpoint(m_listenAddress, sendSocket.local_endpoint().port());
m_answerSocket.open(listenEndpoint.protocol());
m_answerSocket.set_option(asio::ip::udp::socket::reuse_address(true));
m_answerSocket.bind(listenEndpoint);
sendSocket.send(asio::buffer(data));
asio::error_code error = asio::error::would_block;
std::size_t length = 0;
m_deadline.expires_from_now(std::chrono::seconds(1));
m_answerSocket.async_receive(asio::buffer(m_socketAnswerData.data(), m_bufferLength), std::bind(&UdpClientImpl::HandleAnswer, this, std::placeholders::_1, std::placeholders::_2, &error, &length));
do m_ioServiceAnswer.run_one();
while (error == asio::error::would_block);
std::string answer;
if (error != asio::error::operation_aborted)
answer = std::string(m_socketAnswerData.begin(), m_socketAnswerData.begin() + length);
else
answer = "Error";
m_answerSocket.close();
return answer;
}
catch (const asio::system_error& ex)
{
std::stringstream ss;
ss << "UdpClientImpl::Send() - Error: " << ex.what() << std::endl;
Logging::Log(Logging::Severity::Error, ss.str());
return "Error";
}
}
void UdpClientImpl::Broadcast(const std::string& data, int sourcePort)
{
asio::ip::udp::socket sendSocket(m_ioService);
sendSocket.open(asio::ip::udp::v4());
sendSocket.set_option(asio::ip::udp::socket::reuse_address(true));
sendSocket.set_option(asio::socket_base::broadcast(true));
if (sourcePort != 0)
{
asio::ip::udp::endpoint sendEndpoint(m_listenAddress, sourcePort);
sendSocket.bind(sendEndpoint);
}
asio::ip::udp::endpoint targetEndpoint(asio::ip::address_v4::broadcast(), m_targetPort);
try
{
sendSocket.connect(targetEndpoint);
sendSocket.send(asio::buffer(data));
}
catch (const asio::system_error& ex)
{
std::stringstream ss;
ss << "UdpClientImpl::Broadcast() - Error: " << ex.what() << std::endl;
Logging::Log(Logging::Severity::Error, ss.str());
}
sendSocket.close();
}
void UdpClientImpl::EnableMulticast()
{
m_receiveSocket.set_option(asio::ip::multicast::join_group(m_targetAddress.to_v4(), m_listenAddress.to_v4()));
}
void UdpClientImpl::CheckDeadline()
{
if (m_deadline.expires_at() <= std::chrono::system_clock::now())
{
m_answerSocket.cancel();
m_deadline.expires_at(asio::system_timer::time_point(std::chrono::system_clock::duration::max()));
}
m_deadline.async_wait(std::bind(&UdpClientImpl::CheckDeadline, this));
}
void UdpClientImpl::HandleAnswer(const asio::error_code& error, std::size_t length, asio::error_code* out_error, std::size_t* out_length) const
{
*out_error = error;
*out_length = length;
}
void UdpClientImpl::ReceiveSocket()
{
m_receiveSocket.async_receive_from(asio::buffer(m_socketReceiveData.data(), m_bufferLength), m_sourceEndpoint, std::bind(&UdpClientImpl::HandleReceiveSocket, this, std::placeholders::_1, std::placeholders::_2));
}
void UdpClientImpl::HandleReceiveSocket(const asio::error_code& error, size_t bytes)
{
if (error)
return;
if (m_callback)
{
std::vector<char>::iterator begin = m_socketReceiveData.begin();
m_callback(std::string(begin, begin + bytes));
}
ReceiveSocket();
}
} // namespace Network
+56
View File
@@ -0,0 +1,56 @@
#ifndef NETWORK_UDPCLIENTIMPL_H
#define NETWORK_UDPCLIENTIMPL_H
#include <functional>
#include <string>
#include <thread>
#include <vector>
#include "asio.h"
namespace Network {
class UdpClientImpl
{
public:
UdpClientImpl(const std::string& listenAddress, const std::string& targetAddress, int targetPort, std::function<void(const std::string&)> callback);
~UdpClientImpl();
public:
void Receive();
std::string Send(const std::string& data, int sourcePort = 0);
void Broadcast(const std::string& data, int sourcePort = 0);
protected:
void EnableMulticast();
private:
void CheckDeadline();
void HandleAnswer(const asio::error_code& error, size_t bytes, asio::error_code* out_error, std::size_t* out_length) const;
void ReceiveSocket();
void HandleReceiveSocket(const asio::error_code& error, size_t bytes);
private:
std::thread m_thread;
std::string m_url;
int m_bufferLength;
asio::io_service m_ioService;
asio::io_service m_ioServiceAnswer;
asio::system_timer m_deadline;
asio::ip::address m_listenAddress;
asio::ip::address m_targetAddress;
int m_targetPort;
asio::ip::udp::socket m_receiveSocket;
asio::ip::udp::socket m_answerSocket;
asio::ip::udp::endpoint m_targetEndpoint;
asio::ip::udp::endpoint m_sourceEndpoint;
std::vector<char> m_socketReceiveData;
std::vector<char> m_socketAnswerData;
std::function<void(const std::string&)> m_callback;
};
} // namespace Network
#endif // NETWORK_UDPCLIENTIMPL_H