/* * This file is part of OpenTTD. * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . */ /** * @file tcp.h Basic functions to receive and send TCP packets. */ #ifndef NETWORK_CORE_TCP_H #define NETWORK_CORE_TCP_H #include "address.h" #include "packet.h" #include "../../core/ring_buffer.hpp" #include #include #include #include #include #include /** The states of sending the packets. */ enum SendPacketsState { SPS_CLOSED, ///< The connection got closed. SPS_NONE_SENT, ///< The buffer is still full, so no (parts of) packets could be sent. SPS_PARTLY_SENT, ///< The packets are partly sent; there are more packets to be sent in the queue. SPS_ALL_SENT, ///< All packets in the queue are sent. }; /** Base socket handler for all TCP sockets */ class NetworkTCPSocketHandler : public NetworkSocketHandler { private: ring_buffer> packet_queue; ///< Packets that are awaiting delivery std::unique_ptr packet_recv; ///< Partially received packet public: SOCKET sock; ///< The socket currently connected to bool writable; ///< Can we write to this socket? /** * Whether this socket is currently bound to a socket. * @return true when the socket is bound, false otherwise */ bool IsConnected() const { return this->sock != INVALID_SOCKET; } virtual NetworkRecvStatus CloseConnection(bool error = true); void CloseSocket(); void SendPacket(std::unique_ptr packet); void SendPrependPacket(std::unique_ptr packet, int queue_after_packet_type); void ShrinkToFitSendQueue(); SendPacketsState SendPackets(bool closing_down = false); virtual std::unique_ptr ReceivePacket(); virtual void LogSentPacket(const Packet &pkt); bool CanSendReceive(); /** * Whether there is something pending in the send queue. * @return true when something is pending in the send queue. */ bool HasSendQueue() { return !this->packet_queue.empty(); } NetworkTCPSocketHandler(SOCKET s = INVALID_SOCKET); ~NetworkTCPSocketHandler(); }; /** * "Helper" class for creating TCP connections in a non-blocking manner */ class TCPConnecter { private: /** * The current status of the connecter. * * We track the status like this to ensure everything is executed from the * game-thread, and not at another random time where we might not have the * lock on the game-state. */ enum class Status { Init, ///< TCPConnecter is created but resolving hasn't started. Resolving, ///< The hostname is being resolved (threaded). Failure, ///< Resolving failed. Connecting, ///< We are currently connecting. Connected, ///< The connection is established. }; std::thread resolve_thread; ///< Thread used during resolving. std::atomic status = Status::Init; ///< The current status of the connecter. std::atomic killed = false; ///< Whether this connecter is marked as killed. addrinfo *ai = nullptr; ///< getaddrinfo() allocated linked-list of resolved addresses. std::vector addresses; ///< Addresses we can connect to. std::map sock_to_address; ///< Mapping of a socket to the real address it is connecting to. USed for DEBUG statements. size_t current_address = 0; ///< Current index in addresses we are trying. std::vector sockets; ///< Pending connect() attempts. std::chrono::steady_clock::time_point last_attempt; ///< Time we last tried to connect. std::string connection_string; ///< Current address we are connecting to (before resolving). NetworkAddress bind_address; ///< Address we're binding to, if any. int family = AF_UNSPEC; ///< Family we are using to connect with. static std::vector> connecters; ///< List of connections that are currently being created. void Resolve(); void OnResolved(addrinfo *ai); bool TryNextAddress(); void Connect(addrinfo *address); virtual bool CheckActivity(); /* We do not want any other derived classes from this class being able to * access these private members, but it is okay for TCPServerConnecter. */ friend class TCPServerConnecter; static void ResolveThunk(TCPConnecter *connecter); public: TCPConnecter() {}; TCPConnecter(const std::string &connection_string, uint16_t default_port, const NetworkAddress &bind_address = {}, int family = AF_UNSPEC); virtual ~TCPConnecter(); /** * Callback when the connection succeeded. * @param s the socket that we opened */ virtual void OnConnect([[maybe_unused]] SOCKET s) {} /** * Callback for when the connection attempt failed. */ virtual void OnFailure() {} void Kill(); static void CheckCallbacks(); static void KillAll(); /** * Create the connecter, and initiate connecting by putting it in the collection of TCP connections to make. * @tparam T The type of connecter to create. * @param args The arguments to the constructor of T. * @return Shared pointer to the connecter. */ template static std::shared_ptr Create(Args&& ... args) { return TCPConnecter::connecters.emplace_back(std::make_shared(std::forward(args)...)); } }; class TCPServerConnecter : public TCPConnecter { private: SOCKET socket = INVALID_SOCKET; ///< The socket when a connection is established. bool CheckActivity() override; public: ServerAddress server_address; ///< Address we are connecting to. TCPServerConnecter(const std::string &connection_string, uint16_t default_port); void SetConnected(SOCKET sock); void SetFailure(); }; #endif /* NETWORK_CORE_TCP_H */