src/Network/Session.hh
author terom
Wed, 17 Dec 2008 00:40:22 +0000
changeset 381 9b35bc329d23
parent 286 2a8f20a53ff2
child 400 d64bf28c4340
permissions -rw-r--r--
separate sockaddr stuff out of NetworkAddress... now called NetworkEndpoint
#ifndef NETWORK_SESSION_HH
#define NETWORK_SESSION_HH

#include <map>
#include <stdint.h>

// forward-declare
class NetworkSession;

/**
 * A NetworkSession puts each packet onto a specific channel, which can the be used to run multiple different modules
 * on top of a single session.
 *
 * NetworkChannelID zero is reserved for internal NetworkSession use
 */
typedef uint16_t NetworkChannelID;

/**
 * Size of a NetworkSession's packet header:
 *  uint16      channel_id
 */
const size_t NETWORK_SESSION_HEADER_SIZE = sizeof(uint16_t);

#include "TCP.hh"
#include "UDP.hh"
#include "Node.hh"

/**
 * A NetworkSession provides TCP/UDP Server and Client functionality, representing remote NetworkSessions with
 * NetworkNodes. A NetworkSession can then communicate with its NetworkNodes using TCP or UDP NetworkPackets.
 */
class NetworkSession {
    friend class NetworkNode;

    private:
        /**
         * The application's magic ID
         */
        uint64_t magic;

        /**
         * Our TCP server, if we're in listen() mode
         */
        NetworkTCPServer *tcp_srv;

        /**
         * Our UDP server, if we're in listen() mode
         */
        NetworkUDP *udp_srv;

        /**
         * Our UDP client, if we're in connect() mode
         */
        NetworkUDP *udp_client;
        
        CL_SlotContainer slots;
        
        /**
         * A map of NetworkAddress -> NetworkNode, manipulated when TCP connections are established/broken down,
         * and used to map UDP packets to their NetworkNode
         */
        std::map<NetworkAddress, NetworkNode*> nodes;

        /**
         * A map of NetworkChannelID -> signal, used to signal our users when we receieve packets
         */
        std::map<NetworkChannelID, CL_Signal_v2<NetworkPacketInput&, NetworkNode *> > _map_sig_chan_message;
    
    public:
        /**
         * Construct an idle NetworkSession using the given application magic, which should be unique to tell different
         * applications apart from each other.
         *
         * @param magic unique application magic
         */
        NetworkSession (uint64_t magic);
        
        /**
         * Have the NetworkSession enter server mode, listening on the given address using both TCP and UDP
         *
         * @param addr local address to listen on
         */
        void listen (const NetworkEndpoint &addr);

        /**
         * Have the NetworkSession enter client mode, establishing a TCP connection to the server, and then allocating
         * an UDP socket on the same local address as the TCP connection.
         *
         * @param addr remote address to connect to
         */
        NetworkNode* connect (const NetworkEndpoint &addr);
    
    protected:
        /**
         * Used to build a new NetworkNode by connect/on_tcp_client. Can be used to override what kind of NetworkNodes
         * get created. Type tells what kind of node this is.
         *
         * @param tcp the TCP transport for this node
         * @param udp the UDP socket to use for this node
         * @param addr the remote address
         * @param type the type of node
         * @see NetworkNodeType
         */
        virtual NetworkNode *build_node (NetworkTCPTransport *tcp, NetworkUDP *udp, const NetworkAddress &addr, enum NetworkNodeType type);
        
        /**
         * A NetworkNode's TCP connection has failed. Removes the node from our nodes-map (using node->getRemoteAddress)
         *
         * @param node the node that has disconnected
         */
        void handle_disconnect (NetworkNode *node);

        /**
         * We have received a NetworkPacket from the given node (either TCP or UDP, we don't know)
         *
         * @param pkt the NetworkPacket that we received
         * @param node the node that sent it
         */
        void handle_message (NetworkPacketInput &pkt, NetworkNode *node);

    private:
        /**
         * Our tcp_srv has accept()'d a new client.
         *
         * Create a NetworkNode using build_node and udp_srv, add it to our node-map, and trigger sig_node_connected
         */
        void on_tcp_client (NetworkTCPTransport *client);

        /**
         * Our udp_srv has recv()'d a NetworkPacket.
         *
         * Map it to a NetworkNode using our node-map and call handle_message
         */
        void on_udp_packet (NetworkPacketInput &pkt, const NetworkAddress &addr);
        
        /**
         * New-client signal
         */
        CL_Signal_v1<NetworkNode*> _sig_node_connected;

    public:
        /**
         * Send the given NetworkPacket to all our nodes using the given NetworkChannelID, using TCP if reliable, UDP otherwise.
         *
         * @param channel_id the NetworkChannelID to use
         * @param pkt the NetworkPacket to send
         * @param reliable Whether to use TCP or UDP
         */
        void send_all (NetworkChannelID channel_id, const NetworkPacketBuffer &pkt, bool reliable = true);

        /**
         * Like send_all, but do not send the packet to the specified node. If node is NULL, this behaves like
         * send_all.
         *
         * @see send_all
         */
        void send_all_except (NetworkChannelID channel_id, const NetworkPacketBuffer &pkt, const NetworkNode *node, bool reliable = true);

        /**
         * A new node has connected to us
         */
        CL_Signal_v1<NetworkNode*>& sig_node_connected (void) { return _sig_node_connected; }

        /**
         * We have received a NetworkPacket from a NetworkNode on the given NetworkChannelID
         */
        CL_Signal_v2<NetworkPacketInput&, NetworkNode *>& sig_chan_message (NetworkChannelID cid) { return _map_sig_chan_message[cid]; }
};

#endif /* NETWORK_SESSION_HH */