|
|
|
|
|
#ifndef DLIB_BRIDGe_Hh_ |
|
#define DLIB_BRIDGe_Hh_ |
|
|
|
#include <iostream> |
|
#include <memory> |
|
#include <string> |
|
|
|
#include "bridge_abstract.h" |
|
#include "../pipe.h" |
|
#include "../threads.h" |
|
#include "../serialize.h" |
|
#include "../sockets.h" |
|
#include "../sockstreambuf.h" |
|
#include "../logger.h" |
|
#include "../algs.h" |
|
|
|
|
|
namespace dlib |
|
{ |
|
|
|
|
|
|
|
struct connect_to_ip_and_port |
|
{ |
|
connect_to_ip_and_port ( |
|
const std::string& ip_, |
|
unsigned short port_ |
|
): ip(ip_), port(port_) |
|
{ |
|
|
|
DLIB_ASSERT(is_ip_address(ip) && port != 0, |
|
"\t connect_to_ip_and_port()" |
|
<< "\n\t Invalid inputs were given to this function" |
|
<< "\n\t ip: " << ip |
|
<< "\n\t port: " << port |
|
<< "\n\t this: " << this |
|
); |
|
} |
|
|
|
private: |
|
friend class bridge; |
|
const std::string ip; |
|
const unsigned short port; |
|
}; |
|
|
|
inline connect_to_ip_and_port connect_to ( |
|
const network_address& addr |
|
) |
|
{ |
|
|
|
DLIB_ASSERT(addr.port != 0, |
|
"\t connect_to_ip_and_port()" |
|
<< "\n\t The TCP port to connect to can't be 0." |
|
<< "\n\t addr.port: " << addr.port |
|
); |
|
|
|
if (is_ip_address(addr.host_address)) |
|
{ |
|
return connect_to_ip_and_port(addr.host_address, addr.port); |
|
} |
|
else |
|
{ |
|
std::string ip; |
|
if(hostname_to_ip(addr.host_address,ip)) |
|
throw socket_error(ERESOLVE,"unable to resolve '" + addr.host_address + "' in connect_to()"); |
|
|
|
return connect_to_ip_and_port(ip, addr.port); |
|
} |
|
} |
|
|
|
struct listen_on_port |
|
{ |
|
listen_on_port( |
|
unsigned short port_ |
|
) : port(port_) |
|
{ |
|
|
|
DLIB_ASSERT( port != 0, |
|
"\t listen_on_port()" |
|
<< "\n\t Invalid inputs were given to this function" |
|
<< "\n\t port: " << port |
|
<< "\n\t this: " << this |
|
); |
|
} |
|
|
|
private: |
|
friend class bridge; |
|
const unsigned short port; |
|
}; |
|
|
|
template <typename pipe_type> |
|
struct bridge_transmit_decoration |
|
{ |
|
bridge_transmit_decoration ( |
|
pipe_type& p_ |
|
) : p(p_) {} |
|
|
|
private: |
|
friend class bridge; |
|
pipe_type& p; |
|
}; |
|
|
|
template <typename pipe_type> |
|
bridge_transmit_decoration<pipe_type> transmit ( pipe_type& p) { return bridge_transmit_decoration<pipe_type>(p); } |
|
|
|
template <typename pipe_type> |
|
struct bridge_receive_decoration |
|
{ |
|
bridge_receive_decoration ( |
|
pipe_type& p_ |
|
) : p(p_) {} |
|
|
|
private: |
|
friend class bridge; |
|
pipe_type& p; |
|
}; |
|
|
|
template <typename pipe_type> |
|
bridge_receive_decoration<pipe_type> receive ( pipe_type& p) { return bridge_receive_decoration<pipe_type>(p); } |
|
|
|
|
|
|
|
struct bridge_status |
|
{ |
|
bridge_status() : is_connected(false), foreign_port(0){} |
|
|
|
bool is_connected; |
|
unsigned short foreign_port; |
|
std::string foreign_ip; |
|
}; |
|
|
|
inline void serialize ( const bridge_status& , std::ostream& ) |
|
{ |
|
throw serialization_error("It is illegal to serialize bridge_status objects."); |
|
} |
|
|
|
inline void deserialize ( bridge_status& , std::istream& ) |
|
{ |
|
throw serialization_error("It is illegal to serialize bridge_status objects."); |
|
} |
|
|
|
|
|
|
|
namespace impl_brns |
|
{ |
|
class impl_bridge_base |
|
{ |
|
public: |
|
|
|
virtual ~impl_bridge_base() {} |
|
|
|
virtual bridge_status get_bridge_status ( |
|
) const = 0; |
|
}; |
|
|
|
template < |
|
typename transmit_pipe_type, |
|
typename receive_pipe_type |
|
> |
|
class impl_bridge : public impl_bridge_base, private noncopyable, private multithreaded_object |
|
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public: |
|
|
|
impl_bridge ( |
|
unsigned short listen_port, |
|
transmit_pipe_type* transmit_pipe_, |
|
receive_pipe_type* receive_pipe_ |
|
) : |
|
s(m), |
|
receive_thread_active(false), |
|
transmit_thread_active(false), |
|
port(0), |
|
transmit_pipe(transmit_pipe_), |
|
receive_pipe(receive_pipe_), |
|
dlog("dlib.bridge"), |
|
keepalive_code(0), |
|
message_code(1) |
|
{ |
|
int status = create_listener(list, listen_port); |
|
if (status == PORTINUSE) |
|
{ |
|
std::ostringstream sout; |
|
sout << "Error, the port " << listen_port << " is already in use."; |
|
throw socket_error(EPORT_IN_USE, sout.str()); |
|
} |
|
else if (status == OTHER_ERROR) |
|
{ |
|
throw socket_error("Unable to create listening socket for an unknown reason."); |
|
} |
|
|
|
register_thread(*this, &impl_bridge::transmit_thread); |
|
register_thread(*this, &impl_bridge::receive_thread); |
|
register_thread(*this, &impl_bridge::connect_thread); |
|
|
|
start(); |
|
} |
|
|
|
impl_bridge ( |
|
const std::string ip_, |
|
unsigned short port_, |
|
transmit_pipe_type* transmit_pipe_, |
|
receive_pipe_type* receive_pipe_ |
|
) : |
|
s(m), |
|
receive_thread_active(false), |
|
transmit_thread_active(false), |
|
port(port_), |
|
ip(ip_), |
|
transmit_pipe(transmit_pipe_), |
|
receive_pipe(receive_pipe_), |
|
dlog("dlib.bridge"), |
|
keepalive_code(0), |
|
message_code(1) |
|
{ |
|
register_thread(*this, &impl_bridge::transmit_thread); |
|
register_thread(*this, &impl_bridge::receive_thread); |
|
register_thread(*this, &impl_bridge::connect_thread); |
|
|
|
start(); |
|
} |
|
|
|
~impl_bridge() |
|
{ |
|
|
|
stop(); |
|
|
|
|
|
|
|
bool transmit_enabled = true; |
|
bool receive_enabled = true; |
|
|
|
|
|
if (transmit_pipe) |
|
{ |
|
transmit_enabled = transmit_pipe->is_dequeue_enabled(); |
|
transmit_pipe->disable_dequeue(); |
|
} |
|
if (receive_pipe) |
|
{ |
|
receive_enabled = receive_pipe->is_enqueue_enabled(); |
|
receive_pipe->disable_enqueue(); |
|
} |
|
|
|
{ |
|
auto_mutex lock(m); |
|
s.broadcast(); |
|
|
|
|
|
if (con) |
|
con->shutdown(); |
|
} |
|
|
|
|
|
wait(); |
|
|
|
if (transmit_pipe && transmit_enabled) |
|
transmit_pipe->enable_dequeue(); |
|
if (receive_pipe && receive_enabled) |
|
receive_pipe->enable_enqueue(); |
|
} |
|
|
|
bridge_status get_bridge_status ( |
|
) const |
|
{ |
|
auto_mutex lock(current_bs_mutex); |
|
return current_bs; |
|
} |
|
|
|
private: |
|
|
|
|
|
template <typename pipe_type> |
|
typename enable_if<is_convertible<bridge_status, typename pipe_type::type> >::type enqueue_bridge_status ( |
|
pipe_type* p, |
|
const bridge_status& status |
|
) |
|
{ |
|
if (p) |
|
{ |
|
typename pipe_type::type temp(status); |
|
p->enqueue(temp); |
|
} |
|
} |
|
|
|
template <typename pipe_type> |
|
typename disable_if<is_convertible<bridge_status, typename pipe_type::type> >::type enqueue_bridge_status ( |
|
pipe_type* , |
|
const bridge_status& |
|
) |
|
{ |
|
} |
|
|
|
void connect_thread ( |
|
) |
|
{ |
|
while (!should_stop()) |
|
{ |
|
auto_mutex lock(m); |
|
int status = OTHER_ERROR; |
|
if (list) |
|
{ |
|
do |
|
{ |
|
status = list->accept(con, 1000); |
|
} while (status == TIMEOUT && !should_stop()); |
|
} |
|
else |
|
{ |
|
status = create_connection(con, port, ip); |
|
} |
|
|
|
if (should_stop()) |
|
break; |
|
|
|
if (status != 0) |
|
{ |
|
|
|
s.wait_or_timeout(2000); |
|
continue; |
|
} |
|
|
|
dlog << LINFO << "Established new connection to " << con->get_foreign_ip() << ":" << con->get_foreign_port() << "."; |
|
|
|
bridge_status temp_bs; |
|
{ auto_mutex lock(current_bs_mutex); |
|
current_bs.is_connected = true; |
|
current_bs.foreign_port = con->get_foreign_port(); |
|
current_bs.foreign_ip = con->get_foreign_ip(); |
|
temp_bs = current_bs; |
|
} |
|
enqueue_bridge_status(receive_pipe, temp_bs); |
|
|
|
|
|
receive_thread_active = true; |
|
transmit_thread_active = true; |
|
|
|
s.broadcast(); |
|
|
|
|
|
|
|
while (receive_thread_active || transmit_thread_active) |
|
s.wait(); |
|
|
|
|
|
dlog << LINFO << "Closed connection to " << con->get_foreign_ip() << ":" << con->get_foreign_port() << "."; |
|
{ auto_mutex lock(current_bs_mutex); |
|
current_bs.is_connected = false; |
|
current_bs.foreign_port = con->get_foreign_port(); |
|
current_bs.foreign_ip = con->get_foreign_ip(); |
|
temp_bs = current_bs; |
|
} |
|
enqueue_bridge_status(receive_pipe, temp_bs); |
|
} |
|
|
|
} |
|
|
|
|
|
void receive_thread ( |
|
) |
|
{ |
|
while (true) |
|
{ |
|
|
|
{ auto_mutex lock(m); |
|
while (!receive_thread_active && !should_stop()) |
|
{ |
|
s.wait(); |
|
} |
|
|
|
if (should_stop()) |
|
break; |
|
} |
|
|
|
|
|
|
|
try |
|
{ |
|
if (receive_pipe) |
|
{ |
|
sockstreambuf buf(con); |
|
std::istream in(&buf); |
|
typename receive_pipe_type::type item; |
|
|
|
|
|
assign_zero_if_built_in_scalar_type(item); |
|
|
|
while (in.peek() != EOF) |
|
{ |
|
unsigned char code; |
|
in.read((char*)&code, sizeof(code)); |
|
if (code == message_code) |
|
{ |
|
deserialize(item, in); |
|
receive_pipe->enqueue(item); |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
|
|
|
|
char buf[1000]; |
|
while (con->read(buf, sizeof(buf)) > 0) ; |
|
} |
|
} |
|
catch (std::bad_alloc& ) |
|
{ |
|
dlog << LERROR << "std::bad_alloc thrown while deserializing message from " |
|
<< con->get_foreign_ip() << ":" << con->get_foreign_port(); |
|
} |
|
catch (dlib::serialization_error& e) |
|
{ |
|
dlog << LERROR << "dlib::serialization_error thrown while deserializing message from " |
|
<< con->get_foreign_ip() << ":" << con->get_foreign_port() |
|
<< ".\nThe exception error message is: \n" << e.what(); |
|
} |
|
catch (std::exception& e) |
|
{ |
|
dlog << LERROR << "std::exception thrown while deserializing message from " |
|
<< con->get_foreign_ip() << ":" << con->get_foreign_port() |
|
<< ".\nThe exception error message is: \n" << e.what(); |
|
} |
|
|
|
|
|
|
|
|
|
con->shutdown(); |
|
auto_mutex lock(m); |
|
receive_thread_active = false; |
|
s.broadcast(); |
|
} |
|
|
|
auto_mutex lock(m); |
|
receive_thread_active = false; |
|
s.broadcast(); |
|
} |
|
|
|
void transmit_thread ( |
|
) |
|
{ |
|
while (true) |
|
{ |
|
|
|
{ auto_mutex lock(m); |
|
while (!transmit_thread_active && !should_stop()) |
|
{ |
|
s.wait(); |
|
} |
|
|
|
if (should_stop()) |
|
break; |
|
} |
|
|
|
|
|
|
|
try |
|
{ |
|
sockstreambuf buf(con); |
|
std::ostream out(&buf); |
|
typename transmit_pipe_type::type item; |
|
|
|
|
|
assign_zero_if_built_in_scalar_type(item); |
|
|
|
|
|
while (out) |
|
{ |
|
bool dequeue_timed_out = false; |
|
if (transmit_pipe ) |
|
{ |
|
if (transmit_pipe->dequeue_or_timeout(item,1000)) |
|
{ |
|
out.write((char*)&message_code, sizeof(message_code)); |
|
serialize(item, out); |
|
if (transmit_pipe->size() == 0) |
|
out.flush(); |
|
|
|
continue; |
|
} |
|
|
|
dequeue_timed_out = (transmit_pipe->is_enabled() && transmit_pipe->is_dequeue_enabled()); |
|
} |
|
|
|
|
|
|
|
|
|
if (!dequeue_timed_out) |
|
{ |
|
auto_mutex lock(m); |
|
if (should_stop()) |
|
break; |
|
|
|
s.wait_or_timeout(1000); |
|
} |
|
|
|
|
|
out.write((char*)&keepalive_code, sizeof(keepalive_code)); |
|
out.flush(); |
|
} |
|
} |
|
catch (std::bad_alloc& ) |
|
{ |
|
dlog << LERROR << "std::bad_alloc thrown while serializing message to " |
|
<< con->get_foreign_ip() << ":" << con->get_foreign_port(); |
|
} |
|
catch (dlib::serialization_error& e) |
|
{ |
|
dlog << LERROR << "dlib::serialization_error thrown while serializing message to " |
|
<< con->get_foreign_ip() << ":" << con->get_foreign_port() |
|
<< ".\nThe exception error message is: \n" << e.what(); |
|
} |
|
catch (std::exception& e) |
|
{ |
|
dlog << LERROR << "std::exception thrown while serializing message to " |
|
<< con->get_foreign_ip() << ":" << con->get_foreign_port() |
|
<< ".\nThe exception error message is: \n" << e.what(); |
|
} |
|
|
|
|
|
|
|
|
|
con->shutdown(); |
|
auto_mutex lock(m); |
|
transmit_thread_active = false; |
|
s.broadcast(); |
|
} |
|
|
|
auto_mutex lock(m); |
|
transmit_thread_active = false; |
|
s.broadcast(); |
|
} |
|
|
|
mutex m; |
|
signaler s; |
|
bool receive_thread_active; |
|
bool transmit_thread_active; |
|
std::unique_ptr<connection> con; |
|
std::unique_ptr<listener> list; |
|
const unsigned short port; |
|
const std::string ip; |
|
transmit_pipe_type* const transmit_pipe; |
|
receive_pipe_type* const receive_pipe; |
|
logger dlog; |
|
const unsigned char keepalive_code; |
|
const unsigned char message_code; |
|
|
|
mutex current_bs_mutex; |
|
bridge_status current_bs; |
|
}; |
|
} |
|
|
|
|
|
|
|
|
|
class bridge : noncopyable |
|
{ |
|
public: |
|
|
|
bridge () {} |
|
|
|
template < typename T, typename U, typename V > |
|
bridge ( |
|
T network_parameters, |
|
U pipe1, |
|
V pipe2 |
|
) { reconfigure(network_parameters,pipe1,pipe2); } |
|
|
|
template < typename T, typename U> |
|
bridge ( |
|
T network_parameters, |
|
U pipe |
|
) { reconfigure(network_parameters,pipe); } |
|
|
|
|
|
void clear ( |
|
) |
|
{ |
|
pimpl.reset(); |
|
} |
|
|
|
template < typename T, typename R > |
|
void reconfigure ( |
|
listen_on_port network_parameters, |
|
bridge_transmit_decoration<T> transmit_pipe, |
|
bridge_receive_decoration<R> receive_pipe |
|
) { pimpl.reset(); pimpl.reset(new impl_brns::impl_bridge<T,R>(network_parameters.port, &transmit_pipe.p, &receive_pipe.p)); } |
|
|
|
template < typename T, typename R > |
|
void reconfigure ( |
|
listen_on_port network_parameters, |
|
bridge_receive_decoration<R> receive_pipe, |
|
bridge_transmit_decoration<T> transmit_pipe |
|
) { pimpl.reset(); pimpl.reset(new impl_brns::impl_bridge<T,R>(network_parameters.port, &transmit_pipe.p, &receive_pipe.p)); } |
|
|
|
template < typename T > |
|
void reconfigure ( |
|
listen_on_port network_parameters, |
|
bridge_transmit_decoration<T> transmit_pipe |
|
) { pimpl.reset(); pimpl.reset(new impl_brns::impl_bridge<T,T>(network_parameters.port, &transmit_pipe.p, 0)); } |
|
|
|
template < typename R > |
|
void reconfigure ( |
|
listen_on_port network_parameters, |
|
bridge_receive_decoration<R> receive_pipe |
|
) { pimpl.reset(); pimpl.reset(new impl_brns::impl_bridge<R,R>(network_parameters.port, 0, &receive_pipe.p)); } |
|
|
|
|
|
|
|
|
|
template < typename T, typename R > |
|
void reconfigure ( |
|
connect_to_ip_and_port network_parameters, |
|
bridge_transmit_decoration<T> transmit_pipe, |
|
bridge_receive_decoration<R> receive_pipe |
|
) { pimpl.reset(); pimpl.reset(new impl_brns::impl_bridge<T,R>(network_parameters.ip, network_parameters.port, &transmit_pipe.p, &receive_pipe.p)); } |
|
|
|
template < typename T, typename R > |
|
void reconfigure ( |
|
connect_to_ip_and_port network_parameters, |
|
bridge_receive_decoration<R> receive_pipe, |
|
bridge_transmit_decoration<T> transmit_pipe |
|
) { pimpl.reset(); pimpl.reset(new impl_brns::impl_bridge<T,R>(network_parameters.ip, network_parameters.port, &transmit_pipe.p, &receive_pipe.p)); } |
|
|
|
template < typename R > |
|
void reconfigure ( |
|
connect_to_ip_and_port network_parameters, |
|
bridge_receive_decoration<R> receive_pipe |
|
) { pimpl.reset(); pimpl.reset(new impl_brns::impl_bridge<R,R>(network_parameters.ip, network_parameters.port, 0, &receive_pipe.p)); } |
|
|
|
template < typename T > |
|
void reconfigure ( |
|
connect_to_ip_and_port network_parameters, |
|
bridge_transmit_decoration<T> transmit_pipe |
|
) { pimpl.reset(); pimpl.reset(new impl_brns::impl_bridge<T,T>(network_parameters.ip, network_parameters.port, &transmit_pipe.p, 0)); } |
|
|
|
|
|
bridge_status get_bridge_status ( |
|
) const |
|
{ |
|
if (pimpl) |
|
return pimpl->get_bridge_status(); |
|
else |
|
return bridge_status(); |
|
} |
|
|
|
private: |
|
|
|
std::unique_ptr<impl_brns::impl_bridge_base> pimpl; |
|
}; |
|
|
|
|
|
|
|
} |
|
|
|
#endif |
|
|
|
|