nuclear@0: /************************************************************************************ nuclear@0: nuclear@0: PublicHeader: n/a nuclear@0: Filename : OVR_Session.h nuclear@0: Content : One network session that provides connection/disconnection events. nuclear@0: Created : June 10, 2014 nuclear@0: Authors : Kevin Jenkins, Chris Taylor nuclear@0: nuclear@0: Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved. nuclear@0: nuclear@0: Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); nuclear@0: you may not use the Oculus VR Rift SDK except in compliance with the License, nuclear@0: which is provided at the time of installation or download, or which nuclear@0: otherwise accompanies this software in either electronic or hard copy form. nuclear@0: nuclear@0: You may obtain a copy of the License at nuclear@0: nuclear@0: http://www.oculusvr.com/licenses/LICENSE-3.2 nuclear@0: nuclear@0: Unless required by applicable law or agreed to in writing, the Oculus VR SDK nuclear@0: distributed under the License is distributed on an "AS IS" BASIS, nuclear@0: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. nuclear@0: See the License for the specific language governing permissions and nuclear@0: limitations under the License. nuclear@0: nuclear@0: ************************************************************************************/ nuclear@0: nuclear@0: #ifndef OVR_Session_h nuclear@0: #define OVR_Session_h nuclear@0: nuclear@0: #include "OVR_Socket.h" nuclear@0: #include "OVR_PacketizedTCPSocket.h" nuclear@0: #include "../Kernel/OVR_Array.h" nuclear@0: #include "../Kernel/OVR_Threads.h" nuclear@0: #include "../Kernel/OVR_Atomic.h" nuclear@0: #include "../Kernel/OVR_RefCount.h" nuclear@0: nuclear@0: namespace OVR { namespace Net { nuclear@0: nuclear@0: class Session; nuclear@0: nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: // Based on Semantic Versioning ( http://semver.org/ ) nuclear@0: // nuclear@0: // Please update changelog below: nuclear@0: // 1.0.0 - [SDK 0.4.0] Initial version (July 21, 2014) nuclear@0: // 1.1.0 - Add Get/SetDriverMode_1, HMDCountUpdate_1 nuclear@0: // Version mismatch results (July 28, 2014) nuclear@0: //----------------------------------------------------------------------------- nuclear@0: nuclear@0: static const uint16_t RPCVersion_Major = 1; // MAJOR version when you make incompatible API changes, nuclear@0: static const uint16_t RPCVersion_Minor = 2; // MINOR version when you add functionality in a backwards-compatible manner, and nuclear@0: static const uint16_t RPCVersion_Patch = 0; // PATCH version when you make backwards-compatible bug fixes. nuclear@0: nuclear@0: // Client starts communication by sending its version number. nuclear@0: struct RPC_C2S_Hello nuclear@0: { nuclear@0: RPC_C2S_Hello() : nuclear@0: MajorVersion(0), nuclear@0: MinorVersion(0), nuclear@0: PatchVersion(0) nuclear@0: { nuclear@0: } nuclear@0: nuclear@0: String HelloString; nuclear@0: nuclear@0: // Client version info nuclear@0: uint16_t MajorVersion, MinorVersion, PatchVersion; nuclear@0: nuclear@0: void Serialize(Net::BitStream* bs) nuclear@0: { nuclear@0: bs->Write(HelloString); nuclear@0: bs->Write(MajorVersion); nuclear@0: bs->Write(MinorVersion); nuclear@0: bs->Write(PatchVersion); nuclear@0: } nuclear@0: nuclear@0: bool Deserialize(Net::BitStream* bs) nuclear@0: { nuclear@0: bs->Read(HelloString); nuclear@0: bs->Read(MajorVersion); nuclear@0: bs->Read(MinorVersion); nuclear@0: return bs->Read(PatchVersion); nuclear@0: } nuclear@0: nuclear@0: static void Generate(Net::BitStream* bs); nuclear@0: nuclear@0: bool Validate(); nuclear@0: }; nuclear@0: nuclear@0: // Server responds with an authorization accepted message, including the server's version number nuclear@0: struct RPC_S2C_Authorization nuclear@0: { nuclear@0: RPC_S2C_Authorization() : nuclear@0: MajorVersion(0), nuclear@0: MinorVersion(0), nuclear@0: PatchVersion(0) nuclear@0: { nuclear@0: } nuclear@0: nuclear@0: String AuthString; nuclear@0: nuclear@0: // Server version info nuclear@0: uint16_t MajorVersion, MinorVersion, PatchVersion; nuclear@0: nuclear@0: void Serialize(Net::BitStream* bs) nuclear@0: { nuclear@0: bs->Write(AuthString); nuclear@0: bs->Write(MajorVersion); nuclear@0: bs->Write(MinorVersion); nuclear@0: bs->Write(PatchVersion); nuclear@0: } nuclear@0: nuclear@0: bool Deserialize(Net::BitStream* bs) nuclear@0: { nuclear@0: bs->Read(AuthString); nuclear@0: bs->Read(MajorVersion); nuclear@0: bs->Read(MinorVersion); nuclear@0: return bs->Read(PatchVersion); nuclear@0: } nuclear@0: nuclear@0: static void Generate(Net::BitStream* bs, String errorString = ""); nuclear@0: nuclear@0: bool Validate(); nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: // Result of a session function nuclear@0: enum SessionResult nuclear@0: { nuclear@0: SessionResult_OK, nuclear@0: SessionResult_BindFailure, nuclear@0: SessionResult_ListenFailure, nuclear@0: SessionResult_ConnectFailure, nuclear@0: SessionResult_ConnectInProgress, nuclear@0: SessionResult_AlreadyConnected, nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: // Connection state nuclear@0: enum EConnectionState nuclear@0: { nuclear@0: State_Zombie, // Disconnected nuclear@0: nuclear@0: // Client-only: nuclear@0: Client_Connecting, // Waiting for TCP connection nuclear@0: Client_ConnectedWait, // Connected! Waiting for server to authorize nuclear@0: nuclear@0: // Server-only: nuclear@0: Server_ConnectedWait, // Connected! Waiting for client handshake nuclear@0: nuclear@0: State_Connected // Connected nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: // Generic connection over any transport nuclear@0: class Connection : public RefCountBase nuclear@0: { nuclear@0: public: nuclear@0: Connection() : nuclear@0: Transport(TransportType_None), nuclear@0: State(State_Zombie), nuclear@0: RemoteMajorVersion(0), nuclear@0: RemoteMinorVersion(0), nuclear@0: RemotePatchVersion(0) nuclear@0: { nuclear@0: } nuclear@0: virtual ~Connection() // Allow delete from base nuclear@0: { nuclear@0: } nuclear@0: nuclear@0: public: nuclear@0: virtual void SetState(EConnectionState s) {State = s;} nuclear@0: nuclear@0: TransportType Transport; nuclear@0: EConnectionState State; nuclear@0: nuclear@0: // Version number read from remote host just before connection completes nuclear@0: int RemoteMajorVersion; nuclear@0: int RemoteMinorVersion; nuclear@0: int RemotePatchVersion; nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: // Generic network connection over any network transport nuclear@0: class NetworkConnection : public Connection nuclear@0: { nuclear@0: protected: nuclear@0: NetworkConnection() nuclear@0: { nuclear@0: } nuclear@0: virtual ~NetworkConnection() nuclear@0: { nuclear@0: } nuclear@0: nuclear@0: public: nuclear@0: virtual void SetState(EConnectionState s) nuclear@0: { nuclear@0: if (s != State) nuclear@0: { nuclear@0: Mutex::Locker locker(&StateMutex); nuclear@0: nuclear@0: if (s != State) nuclear@0: { nuclear@0: State = s; nuclear@0: nuclear@0: if (State != Client_Connecting) nuclear@0: { nuclear@0: ConnectingWait.NotifyAll(); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: void WaitOnConnecting() nuclear@0: { nuclear@0: Mutex::Locker locker(&StateMutex); nuclear@0: nuclear@0: while (State == Client_Connecting) nuclear@0: { nuclear@0: ConnectingWait.Wait(&StateMutex); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: SockAddr Address; nuclear@0: Mutex StateMutex; nuclear@0: WaitCondition ConnectingWait; nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: // TCP Connection nuclear@0: class TCPConnection : public NetworkConnection nuclear@0: { nuclear@0: public: nuclear@0: TCPConnection() nuclear@0: { nuclear@0: } nuclear@0: virtual ~TCPConnection() nuclear@0: { nuclear@0: } nuclear@0: nuclear@0: public: nuclear@0: Ptr pSocket; nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: // Packetized TCP Connection nuclear@0: class PacketizedTCPConnection : public TCPConnection nuclear@0: { nuclear@0: public: nuclear@0: PacketizedTCPConnection() nuclear@0: { nuclear@0: Transport = TransportType_PacketizedTCP; nuclear@0: } nuclear@0: virtual ~PacketizedTCPConnection() nuclear@0: { nuclear@0: } nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: // Generic socket listener description nuclear@0: class ListenerDescription nuclear@0: { nuclear@0: public: nuclear@0: ListenerDescription() : nuclear@0: Transport(TransportType_None) nuclear@0: { nuclear@0: } nuclear@0: nuclear@0: TransportType Transport; nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: // Description for a Berkley socket listener nuclear@0: class BerkleyListenerDescription : public ListenerDescription nuclear@0: { nuclear@0: public: nuclear@0: static const int DefaultMaxIncomingConnections = 64; nuclear@0: static const int DefaultMaxConnections = 128; nuclear@0: nuclear@0: BerkleyListenerDescription() : nuclear@0: MaxIncomingConnections(DefaultMaxIncomingConnections), nuclear@0: MaxConnections(DefaultMaxConnections) nuclear@0: { nuclear@0: } nuclear@0: nuclear@0: Ptr BoundSocketToListenWith; nuclear@0: int MaxIncomingConnections; nuclear@0: int MaxConnections; nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: // Receive payload nuclear@0: struct ReceivePayload nuclear@0: { nuclear@0: Connection* pConnection; // Source connection nuclear@0: uint8_t* pData; // Pointer to data received nuclear@0: int Bytes; // Number of bytes of data received nuclear@0: }; nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: // Broadcast parameters nuclear@0: class BroadcastParameters nuclear@0: { nuclear@0: public: nuclear@0: BroadcastParameters() : nuclear@0: pData(NULL), nuclear@0: Bytes(0) nuclear@0: { nuclear@0: } nuclear@0: nuclear@0: BroadcastParameters(const void* _pData, int _bytes) : nuclear@0: pData(_pData), nuclear@0: Bytes(_bytes) nuclear@0: { nuclear@0: } nuclear@0: nuclear@0: public: nuclear@0: const void* pData; // Pointer to data to send nuclear@0: int Bytes; // Number of bytes of data received nuclear@0: }; nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: // Send parameters nuclear@0: class SendParameters nuclear@0: { nuclear@0: public: nuclear@0: SendParameters() : nuclear@0: pData(NULL), nuclear@0: Bytes(0) nuclear@0: { nuclear@0: } nuclear@0: SendParameters(Ptr _pConnection, const void* _pData, int _bytes) : nuclear@0: pConnection(_pConnection), nuclear@0: pData(_pData), nuclear@0: Bytes(_bytes) nuclear@0: { nuclear@0: } nuclear@0: nuclear@0: public: nuclear@0: Ptr pConnection; // Connection to use nuclear@0: const void* pData; // Pointer to data to send nuclear@0: int Bytes; // Number of bytes of data received nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: // Parameters to connect nuclear@0: struct ConnectParameters nuclear@0: { nuclear@0: public: nuclear@0: ConnectParameters() : nuclear@0: Transport(TransportType_None) nuclear@0: { nuclear@0: } nuclear@0: nuclear@0: TransportType Transport; nuclear@0: }; nuclear@0: nuclear@0: struct ConnectParametersBerkleySocket : public ConnectParameters nuclear@0: { nuclear@0: SockAddr RemoteAddress; nuclear@0: Ptr BoundSocketToConnectWith; nuclear@0: bool Blocking; nuclear@0: nuclear@0: ConnectParametersBerkleySocket(BerkleySocket* s, SockAddr* addr, bool blocking, nuclear@0: TransportType transport) : nuclear@0: RemoteAddress(*addr), nuclear@0: BoundSocketToConnectWith(s), nuclear@0: Blocking(blocking) nuclear@0: { nuclear@0: Transport = transport; nuclear@0: } nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: // Listener receive result nuclear@0: enum ListenerReceiveResult nuclear@0: { nuclear@0: /// The SessionListener used this message and it shouldn't be given to the user. nuclear@0: LRR_RETURN = 0, nuclear@0: nuclear@0: /// The SessionListener is going to hold on to this message. Do not deallocate it but do not pass it to other plugins either. nuclear@0: LRR_BREAK, nuclear@0: nuclear@0: /// This message will be processed by other SessionListeners, and at last by the user. nuclear@0: LRR_CONTINUE, nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: // SessionListener nuclear@0: nuclear@0: // Callback interface for network events such as connecting, disconnecting, getting data, independent of the transport medium nuclear@0: class SessionListener nuclear@0: { nuclear@0: public: nuclear@0: virtual ~SessionListener(){} nuclear@0: nuclear@0: // Data events nuclear@0: virtual void OnReceive(ReceivePayload* pPayload, ListenerReceiveResult* lrrOut) { OVR_UNUSED2(pPayload, lrrOut); } nuclear@0: nuclear@0: // Connection was closed nuclear@0: virtual void OnDisconnected(Connection* conn) = 0; nuclear@0: nuclear@0: // Connection was created (some data was exchanged to verify protocol compatibility too) nuclear@0: virtual void OnConnected(Connection* conn) = 0; nuclear@0: nuclear@0: // Server accepted client nuclear@0: virtual void OnNewIncomingConnection(Connection* conn) { OnConnected(conn); } nuclear@0: // Client was accepted nuclear@0: virtual void OnConnectionRequestAccepted(Connection* conn) { OnConnected(conn); } nuclear@0: nuclear@0: // Connection attempt failed for some reason nuclear@0: virtual void OnConnectionAttemptFailed(Connection* conn) { OnDisconnected(conn); } nuclear@0: nuclear@0: // Incompatible protocol nuclear@0: virtual void OnIncompatibleProtocol(Connection* conn) { OnConnectionAttemptFailed(conn); } nuclear@0: // Disconnected during initial handshake nuclear@0: virtual void OnHandshakeAttemptFailed(Connection* conn) { OnConnectionAttemptFailed(conn); } nuclear@0: nuclear@0: // Other nuclear@0: virtual void OnAddedToSession(Session* session) { OVR_UNUSED(session); } nuclear@0: virtual void OnRemovedFromSession(Session* session) { OVR_UNUSED(session); } nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: // Session nuclear@0: nuclear@0: // Interface for network events such as listening on a socket, sending data, connecting, and disconnecting. Works independently of the transport medium and also implements loopback nuclear@0: class Session : public SocketEvent_TCP, public NewOverrideBase nuclear@0: { nuclear@0: // Implement a policy to avoid releasing memory backing allBlockingTcpSockets nuclear@0: struct ArrayNoShrinkPolicy : ArrayDefaultPolicy nuclear@0: { nuclear@0: bool NeverShrinking() const { return 1; } nuclear@0: }; nuclear@0: nuclear@0: public: nuclear@0: Session() : nuclear@0: HasLoopbackListener(false) nuclear@0: { nuclear@0: } nuclear@0: virtual ~Session() nuclear@0: { nuclear@0: // Ensure memory backing the sockets array is released nuclear@0: allBlockingTcpSockets.ClearAndRelease(); nuclear@0: } nuclear@0: nuclear@0: virtual SessionResult Listen(ListenerDescription* pListenerDescription); nuclear@0: virtual SessionResult Connect(ConnectParameters* cp); nuclear@0: virtual int Send(SendParameters* payload); nuclear@0: virtual void Broadcast(BroadcastParameters* payload); nuclear@0: // DO NOT CALL Poll() FROM MULTIPLE THREADS due to allBlockingTcpSockets being a member nuclear@0: virtual void Poll(bool listeners = true); nuclear@0: virtual void AddSessionListener(SessionListener* se); nuclear@0: virtual void RemoveSessionListener(SessionListener* se); nuclear@0: virtual SInt32 GetActiveSocketsCount(); nuclear@0: nuclear@0: // Packetized TCP convenience functions nuclear@0: virtual SessionResult ListenPTCP(BerkleyBindParameters* bbp); nuclear@0: virtual SessionResult ConnectPTCP(BerkleyBindParameters* bbp, SockAddr* RemoteAddress, bool blocking); nuclear@0: nuclear@0: // Closes all the sockets; useful for interrupting the socket polling during shutdown nuclear@0: void Shutdown(); nuclear@0: nuclear@0: // Get count of successful connections (past handshake point) nuclear@0: int GetConnectionCount() const nuclear@0: { nuclear@0: return FullConnections.GetSizeI(); nuclear@0: } nuclear@0: Ptr GetConnectionAtIndex(int index); nuclear@0: nuclear@0: protected: nuclear@0: virtual Ptr AllocConnection(TransportType transportType); nuclear@0: nuclear@0: Lock SocketListenersLock, ConnectionsLock, SessionListenersLock; nuclear@0: bool HasLoopbackListener; // Has loopback listener installed? nuclear@0: Array< Ptr > SocketListeners; // List of active sockets nuclear@0: Array< Ptr > AllConnections; // List of active connections stuck at the versioning handshake nuclear@0: Array< Ptr > FullConnections; // List of active connections past the versioning handshake nuclear@0: Array< SessionListener* > SessionListeners; // List of session listeners nuclear@0: Array< Ptr< Net::TCPSocket >, ArrayNoShrinkPolicy > allBlockingTcpSockets; // Preallocated blocking sockets array nuclear@0: nuclear@0: // Tools nuclear@0: Ptr findConnectionBySocket(Array< Ptr >& connectionArray, Socket* s, int *connectionIndex = NULL); // Call with ConnectionsLock held nuclear@0: Ptr findConnectionBySockAddr(SockAddr* address); // Call with ConnectionsLock held nuclear@0: int invokeSessionListeners(ReceivePayload*); nuclear@0: void invokeSessionEvent(void(SessionListener::*f)(Connection*), Connection* pConnection); nuclear@0: nuclear@0: // TCP nuclear@0: virtual void TCP_OnRecv(Socket* pSocket, uint8_t* pData, int bytesRead); nuclear@0: virtual void TCP_OnClosed(TCPSocket* pSocket); nuclear@0: virtual void TCP_OnAccept(TCPSocket* pListener, SockAddr* pSockAddr, SocketHandle newSock); nuclear@0: virtual void TCP_OnConnected(TCPSocket* pSocket); nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: }} // OVR::Net nuclear@0: nuclear@0: #endif