mirror of
https://github.com/defold/extension-websocket.git
synced 2025-06-27 09:47:44 +02:00
571 lines
18 KiB
C++
571 lines
18 KiB
C++
// Copyright 2020 The Defold Foundation
|
|
// Licensed under the Defold License version 1.0 (the "License"); you may not use
|
|
// this file except in compliance with the License.
|
|
//
|
|
// You may obtain a copy of the License, together with FAQs at
|
|
// https://www.defold.com/license
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software distributed
|
|
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
// specific language governing permissions and limitations under the License.
|
|
|
|
#ifndef DM_SOCKET_H
|
|
#define DM_SOCKET_H
|
|
|
|
#include <stdint.h>
|
|
#include <string.h> // memset, memcmp
|
|
|
|
#if defined(__linux__) || defined(__MACH__) || defined(ANDROID) || defined(__EMSCRIPTEN__) || defined(__NX__)
|
|
#include <sys/select.h>
|
|
#elif defined(_WIN32)
|
|
#include <winsock2.h>
|
|
#else
|
|
#error "Unsupported platform"
|
|
#endif
|
|
|
|
/**
|
|
* Socket abstraction
|
|
* @note For Recv* and Send* function ETIMEDOUT is translated to EWOULDBLOCK
|
|
* on win32 for compatibility with BSD sockets.
|
|
*/
|
|
namespace dmSocket
|
|
{
|
|
struct Selector
|
|
{
|
|
fd_set m_FdSets[3];
|
|
int m_Nfds;
|
|
Selector();
|
|
};
|
|
|
|
/**
|
|
* Socket result
|
|
*/
|
|
enum Result
|
|
{
|
|
RESULT_OK = 0, //!< RESULT_OK
|
|
|
|
RESULT_ACCES = -1, //!< RESULT_ACCES
|
|
RESULT_AFNOSUPPORT = -2, //!< RESULT_AFNOSUPPORT
|
|
RESULT_WOULDBLOCK = -3, //!< RESULT_WOULDBLOCK
|
|
RESULT_BADF = -4, //!< RESULT_BADF
|
|
RESULT_CONNRESET = -5, //!< RESULT_CONNRESET
|
|
RESULT_DESTADDRREQ = -6, //!< RESULT_DESTADDRREQ
|
|
RESULT_FAULT = -7, //!< RESULT_FAULT
|
|
RESULT_HOSTUNREACH = -8, //!< RESULT_HOSTUNREACH
|
|
RESULT_INTR = -9, //!< RESULT_INTR
|
|
RESULT_INVAL = -10, //!< RESULT_INVAL
|
|
RESULT_ISCONN = -11, //!< RESULT_ISCONN
|
|
RESULT_MFILE = -12, //!< RESULT_MFILE
|
|
RESULT_MSGSIZE = -13, //!< RESULT_MSGSIZE
|
|
RESULT_NETDOWN = -14, //!< RESULT_NETDOWN
|
|
RESULT_NETUNREACH = -15, //!< RESULT_NETUNREACH
|
|
//RESULT_NFILE = -16,
|
|
RESULT_NOBUFS = -17, //!< RESULT_NOBUFS
|
|
//RESULT_NOENT = -18,
|
|
//RESULT_NOMEM = -19,
|
|
RESULT_NOTCONN = -20, //!< RESULT_NOTCONN
|
|
//RESULT_NOTDIR = -21,
|
|
RESULT_NOTSOCK = -22, //!< RESULT_NOTSOCK
|
|
RESULT_OPNOTSUPP = -23, //!< RESULT_OPNOTSUPP
|
|
RESULT_PIPE = -24, //!< RESULT_PIPE
|
|
RESULT_PROTONOSUPPORT = -25, //!< RESULT_PROTONOSUPPORT
|
|
RESULT_PROTOTYPE = -26, //!< RESULT_PROTOTYPE
|
|
RESULT_TIMEDOUT = -27, //!< RESULT_TIMEDOUT
|
|
|
|
RESULT_ADDRNOTAVAIL = -28, //!< RESULT_ADDRNOTAVAIL
|
|
RESULT_CONNREFUSED = -29, //!< RESULT_CONNREFUSED
|
|
RESULT_ADDRINUSE = -30, //!< RESULT_ADDRINUSE
|
|
RESULT_CONNABORTED = -31, //!< RESULT_CONNABORTED
|
|
RESULT_INPROGRESS = -32, //!< RESULT_INPROGRESS
|
|
|
|
// gethostbyname errors
|
|
RESULT_HOST_NOT_FOUND = -100, //!< RESULT_HOST_NOT_FOUND
|
|
RESULT_TRY_AGAIN = -101, //!< RESULT_TRY_AGAIN
|
|
RESULT_NO_RECOVERY = -102, //!< RESULT_NO_RECOVERY
|
|
RESULT_NO_DATA = -103, //!< RESULT_NO_DATA
|
|
|
|
RESULT_UNKNOWN = -1000,//!< RESULT_UNKNOWN
|
|
};
|
|
|
|
/**
|
|
* Socket handle
|
|
* @note Use INVALID_SOCKET_HANDLE instead of zero for unset values. This is an exception
|
|
* from all other handles.
|
|
*/
|
|
typedef int Socket;
|
|
|
|
enum SelectorKind
|
|
{
|
|
SELECTOR_KIND_READ = 0,
|
|
SELECTOR_KIND_WRITE = 1,
|
|
SELECTOR_KIND_EXCEPT = 2,
|
|
};
|
|
|
|
enum Flags
|
|
{
|
|
FLAGS_UP = (1 << 0),
|
|
FLAGS_RUNNING = (1 << 1),
|
|
FLAGS_INET = (1 << 2),
|
|
FLAGS_LINK = (1 << 3),
|
|
};
|
|
|
|
/**
|
|
* Invalid socket handle
|
|
*/
|
|
const Socket INVALID_SOCKET_HANDLE = 0xffffffff;
|
|
|
|
/**
|
|
* Domain type
|
|
*/
|
|
enum Domain
|
|
{
|
|
DOMAIN_MISSING, //!< DOMAIN_MISSING
|
|
DOMAIN_IPV4, //!< DOMAIN_IPV4
|
|
DOMAIN_IPV6, //!< DOMAIN_IPV6
|
|
DOMAIN_UNKNOWN, //!< DOMAIN_UNKNOWN
|
|
};
|
|
|
|
/**
|
|
* Socket type
|
|
*/
|
|
enum Type
|
|
{
|
|
TYPE_STREAM, //!< TYPE_STREAM
|
|
TYPE_DGRAM, //!< TYPE_DGRAM
|
|
};
|
|
|
|
/**
|
|
* Network protocol
|
|
*/
|
|
enum Protocol
|
|
{
|
|
PROTOCOL_TCP, //!< PROTOCOL_TCP
|
|
PROTOCOL_UDP, //!< PROTOCOL_UDP
|
|
};
|
|
|
|
/**
|
|
* Socket shutdown type
|
|
*/
|
|
enum ShutdownType
|
|
{
|
|
SHUTDOWNTYPE_READ,
|
|
SHUTDOWNTYPE_WRITE,
|
|
SHUTDOWNTYPE_READWRITE,
|
|
};
|
|
|
|
/**
|
|
* Network address
|
|
* Network addresses were previously represented as an uint32_t, but in
|
|
* order to support IPv6 the internal representation was changed to a
|
|
* struct.
|
|
*/
|
|
struct Address
|
|
{
|
|
|
|
Address() {
|
|
m_family = dmSocket::DOMAIN_MISSING;
|
|
memset(m_address, 0x0, sizeof(m_address));
|
|
}
|
|
|
|
Domain m_family;
|
|
uint32_t m_address[4];
|
|
};
|
|
|
|
/**
|
|
* Comparison operators for dmSocket::Address (network address).
|
|
* These operators are required since network code was initially designed
|
|
* with the assumption that addresses were stored as uint32_t (IPv4), and
|
|
* thus sortable.
|
|
*/
|
|
inline bool operator==(const Address& lhs, const Address& rhs)
|
|
{
|
|
return memcmp(lhs.m_address, rhs.m_address, sizeof(lhs.m_address)) == 0;
|
|
}
|
|
|
|
inline bool operator< (const Address& lhs, const Address& rhs)
|
|
{
|
|
return memcmp(lhs.m_address, rhs.m_address, sizeof(lhs.m_address)) < 0;
|
|
}
|
|
|
|
inline bool operator!=(const Address& lhs, const Address& rhs) { return !operator==(lhs,rhs); }
|
|
inline bool operator> (const Address& lhs, const Address& rhs) { return operator< (rhs,lhs); }
|
|
inline bool operator<=(const Address& lhs, const Address& rhs) { return !operator> (lhs,rhs); }
|
|
inline bool operator>=(const Address& lhs, const Address& rhs) { return !operator< (lhs,rhs); }
|
|
|
|
struct IfAddr
|
|
{
|
|
char m_Name[128];
|
|
uint32_t m_Flags;
|
|
Address m_Address;
|
|
uint8_t m_MacAddress[6];
|
|
};
|
|
|
|
/**
|
|
* Initialize socket system. Network initialization is required on some platforms.
|
|
* @return RESULT_OK on success
|
|
*/
|
|
Result Initialize();
|
|
|
|
/**
|
|
* Finalize socket system.
|
|
* @return RESULT_OK on success
|
|
*/
|
|
Result Finalize();
|
|
|
|
/**
|
|
* Create a new socket. Corresponds to BSD socket function socket().
|
|
* @note SIGPIPE is disabled on applicable platforms. This has the implication
|
|
* that Receive can return zero bytes when the connection is closed by remote peer.
|
|
* @param type Soccket type
|
|
* @param protocol Protocol
|
|
* @param socket Pointer to created socket
|
|
* @return RESULT_OK on succcess
|
|
*/
|
|
Result New(Domain domain, Type type, enum Protocol protocol, Socket* socket);
|
|
|
|
/**
|
|
* Delete a socket. Corresponds to BSD socket function close()
|
|
* @param socket Socket to close
|
|
* @return RESULT_OK on success
|
|
*/
|
|
Result Delete(Socket socket);
|
|
|
|
/**
|
|
* Get underlying file descriptor
|
|
* @param socket socket to get fd for
|
|
* @return file-descriptor
|
|
*/
|
|
int GetFD(Socket socket);
|
|
|
|
/**
|
|
* Set reuse socket address option on socket. Socket option SO_REUSEADDR on most platforms
|
|
* @param socket Socket to set reuse address to
|
|
* @param reuse True if reuse
|
|
* @return RESULT_OK on success
|
|
*/
|
|
Result SetReuseAddress(Socket socket, bool reuse);
|
|
|
|
|
|
/**
|
|
* Set broadcast address option on socket. Socket option SO_BROADCAST on most platforms.
|
|
* @param socket Socket to set reuse address to
|
|
* @param broadcast True if broadcast
|
|
* @return RESULT_OK on success
|
|
*/
|
|
Result SetBroadcast(Socket socket, bool broadcast);
|
|
|
|
/**
|
|
* Set blocking option on a socket
|
|
* @param socket Socket to set blocking on
|
|
* @param blocking True to block
|
|
* @return RESULT_OK on success
|
|
*/
|
|
Result SetBlocking(Socket socket, bool blocking);
|
|
|
|
/**
|
|
* Set TCP_NODELAY on socket
|
|
* @param socket Socket to set TCP_NODELAY on
|
|
* @param no_delay True for no delay
|
|
* @return RESULT_OK on success
|
|
*/
|
|
Result SetNoDelay(Socket socket, bool no_delay);
|
|
|
|
/**
|
|
* Set socket send timeout
|
|
* @note Timeout resolution might be in milliseconds, e.g. windows. Use values
|
|
* larger than or equal to 1000.
|
|
* @param socket socket
|
|
* @param timeout timeout in microseconds
|
|
* @return RESULT_OK on success
|
|
*/
|
|
Result SetSendTimeout(Socket socket, uint64_t timeout);
|
|
|
|
/**
|
|
* Set socket receive timeout
|
|
* @note Timeout resolution might be in milliseconds, e.g. windows. Use values
|
|
* larger than or equal to 1000
|
|
* @param socket socket
|
|
* @param timeout timeout in microseconds
|
|
* @return RESULT_OK on success
|
|
*/
|
|
Result SetReceiveTimeout(Socket socket, uint64_t timeout);
|
|
|
|
/**
|
|
* Add multicast membership
|
|
* @param socket socket to add membership on
|
|
* @param multi_addr multicast address
|
|
* @param interface_addr interface address
|
|
* @param ttl multicast package time to live
|
|
* @return RESULT_OK
|
|
*/
|
|
Result AddMembership(Socket socket, Address multi_addr, Address interface_addr, int ttl);
|
|
|
|
/**
|
|
* Set address for outgoing multicast datagrams
|
|
* @param socket socket to set multicast address for
|
|
* @param address address of network interface to use
|
|
* @return RESULT_OK
|
|
*/
|
|
Result SetMulticastIf(Socket socket, Address address);
|
|
|
|
/**
|
|
* Accept a connection on a socket
|
|
* @param socket Socket to accept connections on
|
|
* @param address Result address parameter
|
|
* @param accept_socket Pointer to accepted socket (result)
|
|
* @return RESULT_OK on success
|
|
*/
|
|
Result Accept(Socket socket, Address* address, Socket* accept_socket);
|
|
|
|
/**
|
|
* Bind a name to a socket
|
|
* @param socket Socket to bind name to
|
|
* @param address Address to bind
|
|
* @param port Port to bind to
|
|
* @return RESULT_OK on success
|
|
*/
|
|
Result Bind(Socket socket, Address address, int port);
|
|
|
|
/**
|
|
* Initiate a connection on a socket
|
|
* @param socket Socket to initiate connection on
|
|
* @param address Address to connect to
|
|
* @param port Port to connect to
|
|
* @return RESULT_OK on success
|
|
*/
|
|
Result Connect(Socket socket, Address address, int port);
|
|
|
|
/**
|
|
* Listen for connections on a socket
|
|
* @param socket Socket to listen on
|
|
* @param backlog Maximum length for the queue of pending connections
|
|
* @return RESULT_OK on success
|
|
*/
|
|
Result Listen(Socket socket, int backlog);
|
|
|
|
/**
|
|
* Shutdown part of a socket connection
|
|
* @param socket Socket to shutdown connection ow
|
|
* @param how Shutdown type
|
|
* @return RESULT_OK on success
|
|
*/
|
|
Result Shutdown(Socket socket, ShutdownType how);
|
|
|
|
/**
|
|
* Send a message on a socket
|
|
* @param socket Socket to send a message on
|
|
* @param buffer Buffer to send
|
|
* @param length Length of buffer to send
|
|
* @param sent_bytes Number of bytes sent (result)
|
|
* @return RESULT_OK on success
|
|
*/
|
|
Result Send(Socket socket, const void* buffer, int length, int* sent_bytes);
|
|
|
|
/**
|
|
* Send a message to a specific address
|
|
* @param socket Socket to send a message on
|
|
* @param buffer Buffer to send
|
|
* @param length Length of buffer to send
|
|
* @param sent_bytes Number of bytes sent (result)
|
|
* @param to_addr To address
|
|
* @param to_port From addres
|
|
* @return RESULT_OK on success
|
|
*/
|
|
Result SendTo(Socket socket, const void* buffer, int length, int* sent_bytes, Address to_addr, uint16_t to_port);
|
|
|
|
/**
|
|
* Receive data on a socket
|
|
* @param socket Socket to receive data on
|
|
* @param buffer Buffer to receive to
|
|
* @param length Receive buffer length
|
|
* @param received_bytes Number of received bytes (result)
|
|
* @return RESULT_OK on success
|
|
*/
|
|
Result Receive(Socket socket, void* buffer, int length, int* received_bytes);
|
|
|
|
/**
|
|
* Receive from socket
|
|
* @param socket Socket to receive data on
|
|
* @param buffer Buffer to receive to
|
|
* @param length Receive buffer length
|
|
* @param received_bytes Number of received bytes (result)
|
|
* @param from_addr From address (result)
|
|
* @param from_port To address (result)
|
|
* @return RESULT_OK on success
|
|
*/
|
|
Result ReceiveFrom(Socket socket, void* buffer, int length, int* received_bytes,
|
|
Address* from_addr, uint16_t* from_port);
|
|
|
|
|
|
/**
|
|
* Get name, address and port for socket
|
|
* @param socket Socket to get name for
|
|
* @param address Address (result)
|
|
* @param port Socket (result)
|
|
* @return RESULT_OK on success
|
|
*/
|
|
Result GetName(Socket socket, Address*address, uint16_t* port);
|
|
|
|
/**
|
|
* Get local hostname
|
|
* @param hostname hostname buffer
|
|
* @param hostname_length hostname buffer length
|
|
* @return RESULT_OK on success
|
|
*/
|
|
Result GetHostname(char* hostname, int hostname_length);
|
|
|
|
/**
|
|
* Get first local IP address
|
|
* The function tries to determine the local IP address. If several
|
|
* IP addresses are available only a single is returned
|
|
* @note This function might fallback to 127.0.0.1 if no adapter is found
|
|
* Sometimes it might be appropriate to run this function periodically
|
|
* @param address address result
|
|
* @return RESULT_OK on success
|
|
*/
|
|
Result GetLocalAddress(Address* address);
|
|
|
|
/**
|
|
* Get address from ip string
|
|
* @param address IP-string
|
|
* @return Address
|
|
*/
|
|
Address AddressFromIPString(const char* address);
|
|
|
|
/**
|
|
* Convert address to ip string
|
|
* @param address address to convert
|
|
* @return IP string. The caller is responsible to free the string using free()
|
|
*/
|
|
char* AddressToIPString(Address address);
|
|
|
|
/**
|
|
* Get host by name.
|
|
* @param name Hostname to resolve
|
|
* @param address Host address result
|
|
* @param ipv4 Whether or not to search for IPv4 addresses
|
|
* @param ipv6 Whether or not to search for IPv6 addresses
|
|
* @return RESULT_OK on success
|
|
*/
|
|
Result GetHostByName(const char* name, Address* address, bool ipv4 = true, bool ipv6 = true);
|
|
|
|
/**
|
|
* Get information about network adapters (loopback devices are not included)
|
|
* @note Make sure that addresses is large enough. If too small
|
|
* the result is capped.
|
|
* @param addresses array of if-addresses
|
|
* @param addresses_count count
|
|
* @param count actual count
|
|
*/
|
|
void GetIfAddresses(IfAddr* addresses, uint32_t addresses_count, uint32_t* count);
|
|
|
|
/**
|
|
* Convert result value to string
|
|
* @param result Result to convert
|
|
* @return Result as string
|
|
*/
|
|
const char* ResultToString(Result result);
|
|
|
|
/**
|
|
* Converts a native result (error) to dmSocket::Result
|
|
* Also logs the error
|
|
* @param filename the file that calls this function
|
|
* @param line the line number of this call
|
|
* @param r the native result
|
|
* @return Result
|
|
*/
|
|
Result NativeToResult(const char* filename, int line, int r);
|
|
|
|
/**
|
|
* Check if a network address is empty (all zeroes).
|
|
* @param address The address to check
|
|
* @return True if the address is empty, false otherwise
|
|
*/
|
|
bool Empty(Address address);
|
|
|
|
/**
|
|
* Return a pointer to the IPv4 buffer of address.
|
|
* @note Make sure the address family of address is actually AF_INET before
|
|
* attempting to retrieve the IPv4 buffer, otherwise an assert will trigger.
|
|
* @param address Pointer to the address containing the buffer
|
|
* @return Pointer to the buffer that holds the IPv4 address
|
|
*/
|
|
uint32_t* IPv4(Address* address);
|
|
|
|
/**
|
|
* Return a pointer to the IPv6 buffer of address.
|
|
* @note Make sure the address family of address is actually AF_INET6 before
|
|
* attempting to retrieve the IPv6 buffer, otherwise an assert will trigger.
|
|
* @param address Pointer to the address containing the buffer
|
|
* @return Pointer to the buffer that holds the IPv6 address
|
|
*/
|
|
uint32_t* IPv6(Address* address);
|
|
|
|
/**
|
|
* Checks if a socket was created for IPv4 (AF_INET).
|
|
* @param socket The socket to check
|
|
* @return True if the socket was created for IPv4 communication, false otherwise
|
|
*/
|
|
bool IsSocketIPv4(Socket socket);
|
|
|
|
/**
|
|
* Checks if a socket was created for IPv6 (AF_INET6).
|
|
* @param socket The socket to check
|
|
* @return True if the socket was created for IPv6 communication, false otherwise
|
|
*/
|
|
bool IsSocketIPv6(Socket socket);
|
|
|
|
/**
|
|
* Calculate the number of bits that differs between address a and b.
|
|
* @note This is used for the Hamming Distance.
|
|
* @param a The first address to compare
|
|
* @param b The second address to compare
|
|
* @return Number of bits that differs between a and b
|
|
*/
|
|
uint32_t BitDifference(Address a, Address b);
|
|
|
|
struct Selector;
|
|
|
|
/**
|
|
* Clear selector for socket. Similar to FD_CLR
|
|
* @param selector Selector
|
|
* @param selector_kind Kind to clear
|
|
* @param socket Socket to clear
|
|
*/
|
|
void SelectorClear(Selector* selector, SelectorKind selector_kind, Socket socket);
|
|
|
|
/**
|
|
* Set selector for socket. Similar to FD_SET
|
|
* @param selector Selector
|
|
* @param selector_kind Kind to clear
|
|
* @param socket Socket to set
|
|
*/
|
|
void SelectorSet(Selector* selector, SelectorKind selector_kind, Socket socket);
|
|
|
|
/**
|
|
* Check if selector is set. Similar to FD_ISSET
|
|
* @param selector Selector
|
|
* @param selector_kind Selector kind
|
|
* @param socket Socket to check for
|
|
* @return True if set.
|
|
*/
|
|
bool SelectorIsSet(Selector* selector, SelectorKind selector_kind, Socket socket);
|
|
|
|
/**
|
|
* Clear selector (all kinds). Similar to FD_ZERO
|
|
* @param selector Selector
|
|
*/
|
|
void SelectorZero(Selector* selector);
|
|
|
|
/**
|
|
* Select for pending data
|
|
* @param selector Selector
|
|
* @param timeout Timeout. For blocking pass -1
|
|
* @return RESULT_OK on success
|
|
*/
|
|
Result Select(Selector* selector, int32_t timeout);
|
|
}
|
|
|
|
#endif // DM_SOCKET_H
|