![]() |
CUGL 3.0
Cornell University Game Library
|
#include <CUWebSocket.h>
Public Types | |
enum class | State : int { INACTIVE = -1 , CONNECTING = 0 , OPEN = 2 , CLOSING = 3 , CLOSED = 4 , FAILED = 5 } |
typedef std::function< void(State state)> | StateCallback |
typedef std::function< void(const std::vector< std::byte > &message, Uint64 time)> | Dispatcher |
Public Member Functions | |
WebSocket () | |
~WebSocket () | |
const InetAddress & | getAddress () const |
const std::string | getPath () const |
bool | isOpen () |
State | getState () |
size_t | getCapacity () |
void | setCapacity (size_t capacity) |
void | open (bool secure=false) |
void | close () |
bool | send (const std::vector< std::byte > &data) |
bool | send (std::vector< std::byte > &&data) |
void | receive (const Dispatcher &dispatcher) |
void | onReceipt (Dispatcher callback) |
void | onStateChange (StateCallback callback) |
void | setDebug (bool flag) |
bool | getDebug () const |
Static Public Member Functions | |
static std::shared_ptr< WebSocket > | alloc (const InetAddress &address) |
static std::shared_ptr< WebSocket > | allocWithPath (const InetAddress &address, const std::string path) |
This class represents a connection to a central server.
The class NetcodeConnection
is built upon WebRTC for high speed communication. A side effect of this is that CUGL has access to websockets, an ubiquitous framework for creating servers. While not as performant as WebRTC, websockets are relatively simple to use, which makes sense to expose them to the rest of the engine.
Websockets can still be a little difficult for beginners, as bi-directional communication requires either a multi-threaded or an asynchronous (e.g. coroutines) environment. While these are possible in CUGL, it makes much more sense to synchronize messages receival and dispatch with the game loop, in much the same way that we did for NetcodeConnection
. Hence this class shares a lot of the same features as that class.
With that said, this class is much more limited than NetcodeConnection
. The websocket can only talk to one machine at a time. There is some basic connection handling, but no concept of migration or game session management. There is also no UUID for the connected server. The server is identified solely by its URL.
The biggest downside of this class is that the websocket server must have a publicly available address to connect to. This is unlikely to be the case between mobile devices (our primary application). That is why a lobby server (like the one used by NetcodeConnection
is so important.
In addition, as a layer on top of TCP, the performance of this connection will not be as high as that of NetcodeConnection
which uses UDP style communication.
It is completely unsafe for network connections to be used on the stack. For that reason, this class hides the initialization methods (and the constructors create uninitialized connections). You are forced to go through the static allocator alloc
to create instances of this class.
The dispatcher is called by the receive
function to consume data from the message buffer. Unlike NetcodeConnection
, this dispatcher only does it relays the message data since there can only be one source. However, we do include a timestamp of the number of microseconds that have passed since the function NetworkLayer#start
was called.
The function type is equivalent to
const std::function<void(const std::vector<std::byte>& message)>
message | The message data |
time | The number of microseconds since NetworkLayer#start |
This type represents a callback for the WebSocket
class.
This callback is invoked when the connection state has changed. The parameter marks the new connection state. This is particularly helpful for monitoring host migrations.
Callback functions differ from listeners (found in the input classes) in that only one callback of any type is allowed in a WebSocket
class. Callback functions are guaranteed to be called at the start of an animation frame, before the method Application#update(float)
.
The function type is equivalent to
std::function<void(State state)>
state | The new connection state |
|
strong |
An enum representing the current connection state.
This state is the relationship of this connection to the websocket server. The peer connections and data channels have their own separate states.
Enumerator | |
---|---|
INACTIVE | The connection is initialized, but |
CONNECTING | The connection is in the initial connection phase. This represent the initial handshake with the websocket server. This state ends when the connection is officially marked as open. |
OPEN | The connection is complete and the web socket is ready for use. This state ends when either the connection is broken or the socket is closed at either end (e.g at this end or by the server). |
CLOSING | The connection is in the process of closing. This states marks the transition period between when a socket started the process of closing, and when it actually marked as closed. |
CLOSED | The connection is disconnected. This state occurs when the socket has finished closing and is no longer usable. It is possible to reopen a websocket after it has closed. |
FAILED | The connection has failed with an unknown error. |
cugl::netcode::WebSocket::WebSocket | ( | ) |
Creates a degenerate websocket connection.
This object has not been initialized with an address and cannot be used.
You should NEVER USE THIS CONSTRUCTOR. All connections should be created by the static constructor alloc
instead.
cugl::netcode::WebSocket::~WebSocket | ( | ) |
Deletes this websocket connection, disposing all resources
|
inlinestatic |
Returns a newly allocated websocket connection to a server.
This method initializes this websocket connection with all of the correct settings. However, it does not connect to the server. You must call the method open
to initiate connection. This design decision is intended to give the user a chance to set the callback functions before connection is established.
Websocket servers typically reference their connections by a "path" provided by the user. The path is not an necessary an identifier, as multiple connections can use the same path. It is simply a way of logically grouping connections This path can be any string, though the socket applies a prefix of "/" if it is not already there. This This version uses an empty path.
This method will always return false if the NetworkLayer
failed to initialize.
address | The server internet address |
|
inlinestatic |
Returns a newly allocated websocket connection to a server.
This method initializes this websocket connection with all of the correct settings. However, it does not connect to the server. You must call the method open
to initiate connection. This design decision is intended to give the user a chance to set the callback functions before connection is established.
Websocket servers typically reference their connections by a "path" provided by the user. The path is not an necessary an identifier, as multiple connections can use the same path. It is simply a way of logically grouping connections This path can be any string, though this method will apply a prefix of "/" if it is not already there.
This method will always return false if the NetworkLayer
failed to initialize.
address | The server internet address |
path | The connection path |
void cugl::netcode::WebSocket::close | ( | ) |
Closes this connection normally.
Because this requires coordination with this connection, this method does not close the connection immediately. Verify that the state is State#CLOSED
before destroying this object.
|
inline |
Returns the internet address of this connection
size_t cugl::netcode::WebSocket::getCapacity | ( | ) |
Returns the message buffer capacity.
It is possible for this connection to receive several messages over the network before it has a chance to all receive
. This buffer stores those messages to be read later. The capacity indicates the number of messages that can be stored.
This method is not const because it requires a lock.
|
inline |
Returns the debugging status of this connection.
If debugging is active, connections will be quite verbose
|
inline |
Returns the path for this connection.
Websocket servers typically reference their connections by a "path" provided by the user. The path is not an necessary an identifier, as multiple connections can use the same path. It is simply a way of logically grouping connections. This path can be any string, though it must start with a "/".
State cugl::netcode::WebSocket::getState | ( | ) |
Returns the current state of this connection.
Monitoring state is one of the most important components of working with a web socket. It is possible for a connection to close remotely, without any input from this side.
This method is not constant because it performs an internal query.
State can either be monitored via polling with this method, or with a callback set to onStateChange
.
bool cugl::netcode::WebSocket::isOpen | ( | ) |
Returns true if this connection is open
Technically a connection is not open if the state is CONNECTING.
This method is not constant because it performs an internal query.
void cugl::netcode::WebSocket::onReceipt | ( | Dispatcher | callback | ) |
Sets a callback function to invoke on message receipt
This callback is alternative to the method receive
. Instead of buffering messages and calling that method each frame, this callback function will be invoked as soon as the message is received.
All callback functions are guaranteed to be called on the main thread. They are called at the start of an animation frame, before the method Application#update(float)
.
callback | The dispatcher callback |
void cugl::netcode::WebSocket::onStateChange | ( | StateCallback | callback | ) |
Sets a callback function to invoke on state changes
Monitoring state is one of the most important components of working with a web socket. It is possible for a connection to close remotely, without any input from this side.
State can either be monitored via a callback with this method, or with a polling the method getState
.
callback | The state change callback |
void cugl::netcode::WebSocket::open | ( | bool | secure = false | ) |
Opens the connection to the websocket sever
This process is not instantaneous. Upon calling this method, you should wait for getState
or the callback onStateChange
to return State#OPEN
. Once that happens, it is possible to start communicating with the serve.
This method allows us to control the type of connection (ws:// or wss://). Note that a server must have a SSL certificate to support a secure connection.
This method will be ignored if the socket is already open. However, it is possible to use this method reopen a closed connection.
secure | Whether to connect to a server via SSL |
void cugl::netcode::WebSocket::receive | ( | const Dispatcher & | dispatcher | ) |
Receives incoming network messages.
When executed, the function dispatch
willl be called on every received byte array since the last call to receive
. It is up to you to interpret this data on your own or with NetcodeDeserializer
A network frame can, but need not be, the same as a render frame. Your dispatch function should be prepared to be called multiple times a render frame, or even not at all.
If a dispatcher callback has been registered with onReceipt
, this method will never do anything. In that case, messages are not buffered and are processed as soon as they are received.
dispatcher | The function to process received data |
bool cugl::netcode::WebSocket::send | ( | const std::vector< std::byte > & | data | ) |
Sends a byte array to the server.
Communication is guaranteed to be ordered. So if this socket sends two messages to the server, the server will receive those messages in the same order.
You may choose to either send a byte array directly, or you can use the NetcodeSerializer
and NetcodeDeserializer
classes to encode more complex data.
This method requires that the socket be open. Otherwise it will return false.
data | The byte array to send. |
bool cugl::netcode::WebSocket::send | ( | std::vector< std::byte > && | data | ) |
Sends a byte array to the server.
Communication is guaranteed to be ordered. So if this socket sends two messages to the server, the server will receive those messages in the same order.
You may choose to either send a byte array directly, or you can use the NetcodeSerializer
and NetcodeDeserializer
classes to encode more complex data.
This method requires that the socket be open. Otherwise it will return false.
data | The byte array to send. |
void cugl::netcode::WebSocket::setCapacity | ( | size_t | capacity | ) |
Sets the message buffer capacity.
It is possible for this connection to recieve several messages over the network before it has a chance to all receive
. This buffer stores those messages to be read later. The capacity indicates the number of messages that can be stored.
capacity | The new message buffer capacity. |
void cugl::netcode::WebSocket::setDebug | ( | bool | flag | ) |
Toggles the debugging status of this connection.
If debugging is active, connections will be quite verbose
flag | Whether to activate debugging |