2015-07-13 22:46:41 -07:00
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
/* Copyright (c) FIRST 2015. All Rights Reserved. */
|
|
|
|
|
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
|
|
|
|
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
|
|
|
|
/* the project. */
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
#include "NetworkConnection.h"
|
|
|
|
|
|
|
|
|
|
#include "tcpsockets/TCPStream.h"
|
|
|
|
|
#include "raw_socket_istream.h"
|
|
|
|
|
#include "WireDecoder.h"
|
|
|
|
|
#include "WireEncoder.h"
|
|
|
|
|
|
2015-07-17 07:21:07 -07:00
|
|
|
using namespace nt;
|
2015-07-13 22:46:41 -07:00
|
|
|
|
|
|
|
|
NetworkConnection::NetworkConnection(std::unique_ptr<TCPStream> stream,
|
|
|
|
|
Message::GetEntryTypeFunc get_entry_type)
|
|
|
|
|
: m_stream(std::move(stream)),
|
2015-07-20 20:52:26 -07:00
|
|
|
m_get_entry_type(get_entry_type) {
|
|
|
|
|
m_active = false;
|
|
|
|
|
m_proto_rev = 0x0300;
|
2015-07-29 20:31:59 -07:00
|
|
|
m_state = static_cast<int>(kCreated);
|
2015-07-20 20:52:26 -07:00
|
|
|
}
|
2015-07-13 22:46:41 -07:00
|
|
|
|
|
|
|
|
NetworkConnection::~NetworkConnection() { Stop(); }
|
|
|
|
|
|
|
|
|
|
void NetworkConnection::Start() {
|
2015-07-14 23:15:30 -07:00
|
|
|
if (m_active) return;
|
2015-07-13 22:46:41 -07:00
|
|
|
m_active = true;
|
2015-07-29 20:31:59 -07:00
|
|
|
m_state = static_cast<int>(kInit);
|
|
|
|
|
// clear queues
|
|
|
|
|
while (!m_incoming.empty()) m_incoming.pop();
|
|
|
|
|
while (!m_outgoing.empty()) m_outgoing.pop();
|
|
|
|
|
// start threads
|
2015-07-13 22:46:41 -07:00
|
|
|
m_write_thread = std::thread(&NetworkConnection::WriteThreadMain, this);
|
|
|
|
|
m_read_thread = std::thread(&NetworkConnection::ReadThreadMain, this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void NetworkConnection::Stop() {
|
2015-07-29 20:31:59 -07:00
|
|
|
m_state = static_cast<int>(kDead);
|
2015-07-13 22:46:41 -07:00
|
|
|
m_active = false;
|
2015-07-17 22:28:47 -07:00
|
|
|
// closing the stream so the read thread terminates
|
|
|
|
|
if (m_stream) m_stream->close();
|
2015-07-29 20:31:59 -07:00
|
|
|
// send an empty outgoing message set so the write thread terminates
|
|
|
|
|
m_outgoing.push(Outgoing());
|
|
|
|
|
// wait for threads to terminate
|
2015-07-13 22:46:41 -07:00
|
|
|
if (m_write_thread.joinable()) m_write_thread.join();
|
|
|
|
|
if (m_read_thread.joinable()) m_read_thread.join();
|
2015-07-29 20:31:59 -07:00
|
|
|
// clear queues
|
|
|
|
|
while (!m_incoming.empty()) m_incoming.pop();
|
|
|
|
|
while (!m_outgoing.empty()) m_outgoing.pop();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string NetworkConnection::remote_id() const {
|
|
|
|
|
std::lock_guard<std::mutex> lock(m_remote_id_mutex);
|
|
|
|
|
return m_remote_id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void NetworkConnection::set_remote_id(StringRef remote_id) {
|
|
|
|
|
std::lock_guard<std::mutex> lock(m_remote_id_mutex);
|
|
|
|
|
m_remote_id = remote_id;
|
2015-07-13 22:46:41 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void NetworkConnection::ReadThreadMain() {
|
|
|
|
|
raw_socket_istream is(*m_stream);
|
|
|
|
|
WireDecoder decoder(is, m_proto_rev);
|
|
|
|
|
|
|
|
|
|
while (m_active) {
|
|
|
|
|
if (!m_stream)
|
|
|
|
|
break;
|
|
|
|
|
decoder.set_proto_rev(m_proto_rev);
|
|
|
|
|
decoder.Reset();
|
2015-07-15 21:20:18 -07:00
|
|
|
auto msg = Message::Read(decoder, m_get_entry_type);
|
|
|
|
|
if (!msg) {
|
|
|
|
|
// terminate connection on bad message
|
2015-07-17 22:28:47 -07:00
|
|
|
if (m_stream) m_stream->close();
|
2015-07-15 21:20:18 -07:00
|
|
|
break;
|
|
|
|
|
}
|
2015-07-25 10:45:01 -07:00
|
|
|
m_incoming.emplace(std::move(msg));
|
2015-07-13 22:46:41 -07:00
|
|
|
}
|
2015-07-25 10:45:01 -07:00
|
|
|
m_incoming.emplace(nullptr); // notify anyone waiting that we disconnected
|
2015-07-29 20:31:59 -07:00
|
|
|
m_state = static_cast<int>(kDead);
|
2015-07-13 22:46:41 -07:00
|
|
|
m_active = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void NetworkConnection::WriteThreadMain() {
|
|
|
|
|
WireEncoder encoder(m_proto_rev);
|
|
|
|
|
|
|
|
|
|
while (m_active) {
|
|
|
|
|
auto msgs = m_outgoing.pop();
|
2015-07-29 20:31:59 -07:00
|
|
|
if (msgs.empty()) break;
|
2015-07-13 22:46:41 -07:00
|
|
|
encoder.set_proto_rev(m_proto_rev);
|
|
|
|
|
encoder.Reset();
|
|
|
|
|
for (auto& msg : msgs) msg->Write(encoder);
|
|
|
|
|
TCPStream::Error err;
|
|
|
|
|
if (!m_stream) break;
|
|
|
|
|
if (m_stream->send(encoder.data(), encoder.size(), &err) == 0) break;
|
|
|
|
|
}
|
2015-07-29 20:31:59 -07:00
|
|
|
m_state = static_cast<int>(kDead);
|
2015-07-13 22:46:41 -07:00
|
|
|
m_active = false;
|
|
|
|
|
}
|