JDierkse 5 anni fa
parent
commit
da31a95dce

+ 3 - 0
.gitmodules

@@ -0,0 +1,3 @@
+[submodule "Makefiles"]
+	path = Makefiles
+	url = https://gogs.dierkse.nl/JDierkse/Makefiles.git

+ 1 - 0
Libraries/Logging

@@ -0,0 +1 @@
+../../Logging

+ 0 - 1
Makefiles

@@ -1 +0,0 @@
-../../Makefiles

+ 1 - 0
Makefiles

@@ -0,0 +1 @@
+Subproject commit d82c8a204714538d0e9414808388e8c79f20db26

+ 12 - 0
Network/BroadcastClientImpl.cpp

@@ -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 - 0
Network/BroadcastClientImpl.h

@@ -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 - 0
Network/BroadcastServerImpl.cpp

@@ -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 - 0
Network/BroadcastServerImpl.h

@@ -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 - 0
Network/IcmpClientImpl.cpp

@@ -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 - 0
Network/IcmpClientImpl.h

@@ -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 - 0
Network/IcmpHeader.h

@@ -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 - 0
Network/Ipv4Header.h

@@ -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 - 0
Network/MulticastClientImpl.cpp

@@ -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 - 0
Network/MulticastClientImpl.h

@@ -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 - 0
Network/UdpClientImpl.cpp

@@ -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 - 0
Network/UdpClientImpl.h

@@ -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