aboutsummaryrefslogtreecommitdiff
path: root/src/shared/net/connection.hh
diff options
context:
space:
mode:
Diffstat (limited to 'src/shared/net/connection.hh')
-rw-r--r--src/shared/net/connection.hh211
1 files changed, 38 insertions, 173 deletions
diff --git a/src/shared/net/connection.hh b/src/shared/net/connection.hh
index 79146af..70ed6e4 100644
--- a/src/shared/net/connection.hh
+++ b/src/shared/net/connection.hh
@@ -2,208 +2,73 @@
#define SHARED_NET_CONNECTION_HH_
#include <algorithm>
+#include <ctype.h>
#include <optional>
#include <string>
+#include <type_traits>
+#include <list>
#include "shared/net/net.hh"
+#include "shared/net/packet.hh"
#include "shared/net/proto.hh"
namespace shared {
namespace net {
+using socket_t = int;
+
class connection {
+public:
+ using upacket_t = std::shared_ptr<upacket>;
+ using rpacket_t = std::shared_ptr<rpacket>;
+
private:
+ static constexpr std::size_t MAX_PACKET_SIZE = 65536;
+
struct sockets {
- int rsock;
- int usock; // connected in constructor, no need to use sendto or revfrom
+ socket_t rsock;
+ socket_t usock; // connected in constructor
std::string address;
std::string port;
-
- sockets(const int r, const int u, const std::string& a,
- const std::string& p)
- : rsock(r), usock(u), address(a), port(p) {}
};
std::optional<sockets> socks;
std::optional<std::string> bad_reason;
+ std::list<upacket_t> upackets;
+ std::list<rpacket_t> rpackets;
private:
- struct packet_header {
- std::uint32_t size;
- };
-
- // Data has a non-serialised header, and variable length serialised
- // protobuf content.
- static std::string packet_to_data(const proto::packet& packet) {
-
- std::string data;
- packet.SerializeToString(&data);
- shared::compress_string(data);
-
- packet_header header{.size = htonl(static_cast<std::uint32_t>(
- std::size(data) + sizeof(packet_header)))};
-
- return std::string{reinterpret_cast<char*>(&header),
- reinterpret_cast<char*>(&header) + sizeof(header)} +
- std::move(data);
- }
-
- void send_sock_packet(const int sock, const proto::packet& packet) {
- if (!this->good()) {
- return;
- }
-
- const std::string data = packet_to_data(packet);
-
- if (send(sock, std::data(data), std::size(data), 0) == -1) {
- this->bad_reason = shared::net::get_errno_error();
- }
- }
+ bool maybe_send(const packet& packet, const socket_t& sock) noexcept;
public:
- connection(const int rsock) {
- using namespace shared::net;
-
- const std::string peer_address = get_socket_peer_address(rsock);
- const std::string peer_port = get_socket_peer_port(rsock);
-
- // Open up a connected usock based on the state of the connected rsock.
- const int usock = [&peer_address, &peer_port, &rsock]() -> int {
- constexpr addrinfo hints = {.ai_flags = AI_PASSIVE,
- .ai_family = AF_INET,
- .ai_socktype = SOCK_DGRAM};
- const std::string host_address = get_socket_host_address(rsock);
- const std::string host_port = get_socket_host_port(rsock);
- const auto host_info =
- get_addr_info(host_address, host_port, &hints);
- const int usock = make_socket(host_info.get());
- bind_socket(usock, host_info.get());
-
- const auto peer_info =
- get_addr_info(peer_address, peer_port, &hints);
- connect_socket(usock, peer_info.get());
-
- return usock;
- }();
-
- nonblock_socket(usock);
- nonblock_socket(rsock);
-
- this->socks.emplace(rsock, usock, peer_address, peer_port);
- }
-
- // We do not want to copy this object!
- // We use std::nullopt to determine if this object has been moved and if we
- // should close its sockets.
+ connection(const socket_t& rsock); // requires connected rsocket
connection(const connection&) = delete;
- connection(connection&& other) noexcept {
- std::swap(this->socks, other.socks);
- std::swap(this->bad_reason, other.bad_reason);
- other.socks.reset();
- }
- connection& operator=(connection&& other) noexcept {
- std::swap(this->socks, other.socks);
- std::swap(this->bad_reason, other.bad_reason);
- other.socks.reset();
- return *this;
- }
- ~connection() noexcept {
- if (this->socks.has_value()) {
- shared::net::close_socket(socks->rsock);
- shared::net::close_socket(socks->usock);
- }
- }
+ connection(connection&& other) noexcept;
+ connection& operator=(connection&& other) noexcept;
+ ~connection() noexcept;
- // Getters.
- bool good() const noexcept { return !bad_reason.has_value(); }
- std::string get_bad_reason() const noexcept {
- return this->bad_reason.value();
+public:
+ bool good() const noexcept {
+ return !bad_reason.has_value() && this->socks.has_value();
}
+ std::string get_bad_reason() const noexcept { return *this->bad_reason; }
std::string get_address() const noexcept { return this->socks->address; }
public:
- // Send does nothing if good() returns false!
- // Returns whether or not we were able to send our packet.
- void rsend_packet(const proto::packet& packet) noexcept {
- return send_sock_packet(this->socks->rsock, packet);
- }
- void usend_packet(const proto::packet& packet) noexcept {
- return send_sock_packet(this->socks->usock, packet);
- }
- std::optional<proto::packet> rrecv_packet() noexcept {
- const int sock = this->socks->rsock;
-
- // Get the size of the backlog, early out of there's nothing there yet.
- const auto backlog_size = shared::net::get_backlog_size(sock);
- if (backlog_size < sizeof(packet_header)) {
- return std::nullopt;
- }
-
- // Read for the packet headers and get the claimed size. Early out if
- // our stream isn't big enough for that yet.
- packet_header header = {};
- recv(sock, &header, sizeof(header), MSG_PEEK);
- const std::uint32_t read_packet_size = ntohl(header.size);
- if (backlog_size < read_packet_size) {
- return std::nullopt;
- }
-
- // Read the actual packet now, based on our claimed size.
- std::string data;
- data.reserve(read_packet_size);
- if (read(sock, std::data(data), read_packet_size) == -1) {
- this->bad_reason = shared::net::get_errno_error();
- return std::nullopt;
- }
-
- data = std::string{
- reinterpret_cast<char*>(std::data(data)) + sizeof(packet_header),
- reinterpret_cast<char*>(std::data(data)) + read_packet_size};
- shared::decompress_string(data);
+ // When an identical packet is sent to multiple people, the functions using
+ // rpacket_t should be used to avoid unnecessary compression.
+ void rsend_packet(const rpacket_t& packet) noexcept;
+ void usend_packet(const upacket_t& packet) noexcept;
+ void rsend_packet(rpacket&& packet) noexcept;
+ void usend_packet(upacket&& packet) noexcept;
- // Parse the packet, ignoring the header.
- proto::packet packet;
- if (!packet.ParseFromString(data)) {
- return std::nullopt;
- }
-
- return packet;
- }
- std::optional<proto::packet> urecv_packet() noexcept {
- const int sock = this->socks->usock;
-
- restart:
- const auto packet_size = shared::net::get_backlog_size(sock);
-
- if (packet_size == 0) {
- return std::nullopt;
- }
-
- std::string data;
- data.reserve(packet_size);
- if (recv(sock, std::data(data), packet_size, 0) == -1) {
- this->bad_reason = shared::net::get_errno_error();
- return std::nullopt;
- }
-
- data = std::string{
- reinterpret_cast<char*>(std::data(data)) + sizeof(packet_header),
- reinterpret_cast<char*>(std::data(data)) + packet_size};
- shared::decompress_string(data);
-
- proto::packet packet;
- if (!packet.ParseFromString(data)) {
- goto restart;
- }
+public:
+ std::optional<proto::packet> rrecv_packet() noexcept;
+ std::optional<proto::packet> urecv_packet() noexcept;
+ std::optional<proto::packet> recv_packet() noexcept; // from either
- return packet;
- }
- // Gets packets from r/u streams, doesn't care which.
- std::optional<proto::packet> recv_packet() noexcept {
- if (const auto ret = urecv_packet(); ret.has_value()) {
- return ret;
- }
- return rrecv_packet();
- }
+public:
+ void poll(); // call to send potentially queued packets
+ void close(); // call to close sockets
};
} // namespace net