mirror of
https://github.com/godotengine/godot.git
synced 2025-10-15 02:49:24 +00:00
Core: Add UNIX domain socket support
> [!NOTE] > > Later versions of Windows has support for `AF_UNIX`, so it could be > added.
This commit is contained in:
@@ -121,7 +121,10 @@ void EngineDebugger::iteration(uint64_t p_frame_ticks, uint64_t p_process_ticks,
|
||||
}
|
||||
|
||||
void EngineDebugger::initialize(const String &p_uri, bool p_skip_breakpoints, bool p_ignore_error_breaks, const Vector<String> &p_breakpoints, void (*p_allow_focus_steal_fn)()) {
|
||||
register_uri_handler("tcp://", RemoteDebuggerPeerTCP::create); // TCP is the default protocol. Platforms/modules can add more.
|
||||
register_uri_handler("tcp://", RemoteDebuggerPeerTCP::create_tcp); // TCP is the default protocol. Platforms/modules can add more.
|
||||
#ifdef UNIX_ENABLED
|
||||
register_uri_handler("unix://", RemoteDebuggerPeerTCP::create_unix);
|
||||
#endif
|
||||
if (p_uri.is_empty()) {
|
||||
return;
|
||||
}
|
||||
@@ -132,10 +135,10 @@ void EngineDebugger::initialize(const String &p_uri, bool p_skip_breakpoints, bo
|
||||
OS::get_singleton()->initialize_debugging();
|
||||
} else if (p_uri.contains("://")) {
|
||||
const String proto = p_uri.substr(0, p_uri.find("://") + 3);
|
||||
if (!protocols.has(proto)) {
|
||||
return;
|
||||
}
|
||||
RemoteDebuggerPeer *peer = protocols[proto](p_uri);
|
||||
CreatePeerFunc *create_fn = protocols.getptr(proto);
|
||||
ERR_FAIL_NULL_MSG(create_fn, vformat("Invalid protocol: %s.", proto));
|
||||
|
||||
RemoteDebuggerPeer *peer = (*create_fn)(p_uri);
|
||||
if (!peer) {
|
||||
return;
|
||||
}
|
||||
|
@@ -76,18 +76,19 @@ void RemoteDebuggerPeerTCP::close() {
|
||||
in_buf.clear();
|
||||
}
|
||||
|
||||
RemoteDebuggerPeerTCP::RemoteDebuggerPeerTCP(Ref<StreamPeerTCP> p_tcp) {
|
||||
RemoteDebuggerPeerTCP::RemoteDebuggerPeerTCP() {
|
||||
// This means remote debugger takes 16 MiB just because it exists...
|
||||
in_buf.resize((8 << 20) + 4); // 8 MiB should be way more than enough (need 4 extra bytes for encoding packet size).
|
||||
out_buf.resize(8 << 20); // 8 MiB should be way more than enough
|
||||
tcp_client = p_tcp;
|
||||
if (tcp_client.is_valid()) { // Attaching to an already connected stream.
|
||||
connected = true;
|
||||
running = true;
|
||||
thread.start(_thread_func, this);
|
||||
} else {
|
||||
tcp_client.instantiate();
|
||||
}
|
||||
}
|
||||
|
||||
RemoteDebuggerPeerTCP::RemoteDebuggerPeerTCP(Ref<StreamPeerSocket> p_stream) :
|
||||
RemoteDebuggerPeerTCP() {
|
||||
DEV_ASSERT(p_stream.is_valid());
|
||||
tcp_client = p_stream;
|
||||
connected = true;
|
||||
running = true;
|
||||
thread.start(_thread_func, this);
|
||||
}
|
||||
|
||||
RemoteDebuggerPeerTCP::~RemoteDebuggerPeerTCP() {
|
||||
@@ -154,22 +155,10 @@ void RemoteDebuggerPeerTCP::_read_in() {
|
||||
}
|
||||
}
|
||||
|
||||
Error RemoteDebuggerPeerTCP::connect_to_host(const String &p_host, uint16_t p_port) {
|
||||
IPAddress ip;
|
||||
if (p_host.is_valid_ip_address()) {
|
||||
ip = p_host;
|
||||
} else {
|
||||
ip = IP::get_singleton()->resolve_hostname(p_host);
|
||||
}
|
||||
|
||||
int port = p_port;
|
||||
|
||||
Error RemoteDebuggerPeerTCP::_try_connect(Ref<StreamPeerSocket> tcp_client) {
|
||||
const int tries = 6;
|
||||
const int waits[tries] = { 1, 10, 100, 1000, 1000, 1000 };
|
||||
|
||||
Error err = tcp_client->connect_to_host(ip, port);
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Remote Debugger: Unable to connect to host '%s:%d'.", p_host, port));
|
||||
|
||||
for (int i = 0; i < tries; i++) {
|
||||
tcp_client->poll();
|
||||
if (tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTED) {
|
||||
@@ -186,9 +175,6 @@ Error RemoteDebuggerPeerTCP::connect_to_host(const String &p_host, uint16_t p_po
|
||||
ERR_PRINT(vformat("Remote Debugger: Unable to connect. Status: %s.", String::num_int64(tcp_client->get_status())));
|
||||
return FAILED;
|
||||
}
|
||||
connected = true;
|
||||
running = true;
|
||||
thread.start(_thread_func, this);
|
||||
return OK;
|
||||
}
|
||||
|
||||
@@ -222,7 +208,7 @@ void RemoteDebuggerPeerTCP::_poll() {
|
||||
}
|
||||
}
|
||||
|
||||
RemoteDebuggerPeer *RemoteDebuggerPeerTCP::create(const String &p_uri) {
|
||||
RemoteDebuggerPeer *RemoteDebuggerPeerTCP::create_tcp(const String &p_uri) {
|
||||
ERR_FAIL_COND_V(!p_uri.begins_with("tcp://"), nullptr);
|
||||
|
||||
String debug_host = p_uri.replace("tcp://", "");
|
||||
@@ -234,13 +220,30 @@ RemoteDebuggerPeer *RemoteDebuggerPeerTCP::create(const String &p_uri) {
|
||||
debug_host = debug_host.substr(0, sep_pos);
|
||||
}
|
||||
|
||||
RemoteDebuggerPeerTCP *peer = memnew(RemoteDebuggerPeerTCP);
|
||||
Error err = peer->connect_to_host(debug_host, debug_port);
|
||||
if (err != OK) {
|
||||
memdelete(peer);
|
||||
return nullptr;
|
||||
IPAddress ip;
|
||||
if (debug_host.is_valid_ip_address()) {
|
||||
ip = debug_host;
|
||||
} else {
|
||||
ip = IP::get_singleton()->resolve_hostname(debug_host);
|
||||
}
|
||||
return peer;
|
||||
|
||||
Ref<StreamPeerTCP> stream;
|
||||
stream.instantiate();
|
||||
ERR_FAIL_COND_V_MSG(stream->connect_to_host(ip, debug_port) != OK, nullptr, vformat("Remote Debugger: Unable to connect to host '%s:%d'.", debug_host, debug_port));
|
||||
ERR_FAIL_COND_V(_try_connect(stream), nullptr);
|
||||
return memnew(RemoteDebuggerPeerTCP(stream));
|
||||
}
|
||||
|
||||
RemoteDebuggerPeer *RemoteDebuggerPeerTCP::create_unix(const String &p_uri) {
|
||||
ERR_FAIL_COND_V(!p_uri.begins_with("unix://"), nullptr);
|
||||
|
||||
String debug_path = p_uri.replace("unix://", "");
|
||||
Ref<StreamPeerUDS> stream;
|
||||
stream.instantiate();
|
||||
Error err = stream->connect_to_host(debug_path);
|
||||
ERR_FAIL_COND_V_MSG(err != OK && err != ERR_BUSY, nullptr, vformat("Remote Debugger: Unable to connect to socket path '%s'.", debug_path));
|
||||
ERR_FAIL_COND_V(_try_connect(stream), nullptr);
|
||||
return memnew(RemoteDebuggerPeerTCP(stream));
|
||||
}
|
||||
|
||||
RemoteDebuggerPeer::RemoteDebuggerPeer() {
|
||||
|
@@ -31,6 +31,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "core/io/stream_peer_tcp.h"
|
||||
#include "core/io/stream_peer_uds.h"
|
||||
#include "core/object/ref_counted.h"
|
||||
#include "core/os/mutex.h"
|
||||
#include "core/os/thread.h"
|
||||
@@ -59,7 +60,7 @@ class RemoteDebuggerPeerTCP : public RemoteDebuggerPeer {
|
||||
GDSOFTCLASS(RemoteDebuggerPeerTCP, RemoteDebuggerPeer);
|
||||
|
||||
private:
|
||||
Ref<StreamPeerTCP> tcp_client;
|
||||
Ref<StreamPeerSocket> tcp_client;
|
||||
Mutex mutex;
|
||||
Thread thread;
|
||||
List<Array> in_queue;
|
||||
@@ -78,11 +79,11 @@ private:
|
||||
void _poll();
|
||||
void _write_out();
|
||||
void _read_in();
|
||||
static Error _try_connect(Ref<StreamPeerSocket> p_stream);
|
||||
|
||||
public:
|
||||
static RemoteDebuggerPeer *create(const String &p_uri);
|
||||
|
||||
Error connect_to_host(const String &p_host, uint16_t p_port);
|
||||
static RemoteDebuggerPeer *create_tcp(const String &p_uri);
|
||||
static RemoteDebuggerPeer *create_unix(const String &p_uri);
|
||||
|
||||
bool is_peer_connected() override;
|
||||
int get_max_message_size() const override;
|
||||
@@ -92,6 +93,7 @@ public:
|
||||
void poll() override;
|
||||
void close() override;
|
||||
|
||||
RemoteDebuggerPeerTCP(Ref<StreamPeerTCP> p_stream = Ref<StreamPeerTCP>());
|
||||
RemoteDebuggerPeerTCP(Ref<StreamPeerSocket> p_stream);
|
||||
RemoteDebuggerPeerTCP();
|
||||
~RemoteDebuggerPeerTCP();
|
||||
};
|
||||
|
@@ -54,21 +54,61 @@ public:
|
||||
TYPE_UDP,
|
||||
};
|
||||
|
||||
virtual Error open(Type p_type, IP::Type &ip_type) = 0;
|
||||
enum class Family {
|
||||
NONE,
|
||||
INET,
|
||||
UNIX,
|
||||
};
|
||||
|
||||
class Address {
|
||||
Family _family = Family::NONE;
|
||||
CharString _path;
|
||||
IPAddress _ip;
|
||||
uint16_t _port = 0;
|
||||
|
||||
public:
|
||||
_FORCE_INLINE_ Family get_family() const { return _family; }
|
||||
_FORCE_INLINE_ bool is_inet() const { return _family == Family::INET; }
|
||||
_FORCE_INLINE_ bool is_unix() const { return _family == Family::UNIX; }
|
||||
_FORCE_INLINE_ bool is_valid() const { return is_inet() || is_unix(); }
|
||||
|
||||
_FORCE_INLINE_ const IPAddress &ip() const { return _ip; }
|
||||
_FORCE_INLINE_ const uint16_t &port() const { return _port; }
|
||||
|
||||
_FORCE_INLINE_ const CharString &get_path() const { return _path; }
|
||||
|
||||
Address() {}
|
||||
|
||||
Address(const IPAddress &p_addr, uint16_t p_port) :
|
||||
_family(Family::INET) {
|
||||
_ip = p_addr;
|
||||
_port = p_port;
|
||||
}
|
||||
|
||||
Address(const String &p_path) :
|
||||
_family(Family::UNIX), _path(p_path.utf8()) {
|
||||
}
|
||||
|
||||
Address(const CharString &p_path) :
|
||||
_family(Family::UNIX), _path(p_path) {
|
||||
}
|
||||
};
|
||||
|
||||
virtual Error open(Family p_family, Type p_type, IP::Type &r_ip_type) = 0;
|
||||
virtual void close() = 0;
|
||||
virtual Error bind(IPAddress p_addr, uint16_t p_port) = 0;
|
||||
virtual Error bind(Address p_addr) = 0;
|
||||
virtual Error listen(int p_max_pending) = 0;
|
||||
virtual Error connect_to_host(IPAddress p_addr, uint16_t p_port) = 0;
|
||||
virtual Error connect_to_host(Address p_addr) = 0;
|
||||
virtual Error poll(PollType p_type, int timeout) const = 0;
|
||||
virtual Error recv(uint8_t *p_buffer, int p_len, int &r_read) = 0;
|
||||
virtual Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port, bool p_peek = false) = 0;
|
||||
virtual Error send(const uint8_t *p_buffer, int p_len, int &r_sent) = 0;
|
||||
virtual Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port) = 0;
|
||||
virtual Ref<NetSocket> accept(IPAddress &r_ip, uint16_t &r_port) = 0;
|
||||
virtual Ref<NetSocket> accept(Address &r_addr) = 0;
|
||||
|
||||
virtual bool is_open() const = 0;
|
||||
virtual int get_available_bytes() const = 0;
|
||||
virtual Error get_socket_address(IPAddress *r_ip, uint16_t *r_port) const = 0;
|
||||
virtual Error get_socket_address(Address *r_addr) const = 0;
|
||||
|
||||
virtual Error set_broadcasting_enabled(bool p_enabled) = 0; // Returns OK if the socket option has been set successfully.
|
||||
virtual void set_blocking_enabled(bool p_enabled) = 0;
|
||||
|
@@ -52,7 +52,7 @@ Error PacketPeerUDP::join_multicast_group(IPAddress p_multi_address, const Strin
|
||||
|
||||
if (!_sock->is_open()) {
|
||||
IP::Type ip_type = p_multi_address.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6;
|
||||
Error err = _sock->open(NetSocket::TYPE_UDP, ip_type);
|
||||
Error err = _sock->open(NetSocket::Family::INET, NetSocket::TYPE_UDP, ip_type);
|
||||
ERR_FAIL_COND_V(err != OK, err);
|
||||
_sock->set_blocking_enabled(false);
|
||||
_sock->set_broadcasting_enabled(broadcast);
|
||||
@@ -141,7 +141,7 @@ Error PacketPeerUDP::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
|
||||
|
||||
if (!_sock->is_open()) {
|
||||
IP::Type ip_type = peer_addr.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6;
|
||||
err = _sock->open(NetSocket::TYPE_UDP, ip_type);
|
||||
err = _sock->open(NetSocket::Family::INET, NetSocket::TYPE_UDP, ip_type);
|
||||
ERR_FAIL_COND_V(err != OK, err);
|
||||
_sock->set_blocking_enabled(false);
|
||||
_sock->set_broadcasting_enabled(broadcast);
|
||||
@@ -186,7 +186,7 @@ Error PacketPeerUDP::bind(int p_port, const IPAddress &p_bind_address, int p_rec
|
||||
ip_type = p_bind_address.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6;
|
||||
}
|
||||
|
||||
err = _sock->open(NetSocket::TYPE_UDP, ip_type);
|
||||
err = _sock->open(NetSocket::Family::INET, NetSocket::TYPE_UDP, ip_type);
|
||||
|
||||
if (err != OK) {
|
||||
return ERR_CANT_CREATE;
|
||||
@@ -194,7 +194,8 @@ Error PacketPeerUDP::bind(int p_port, const IPAddress &p_bind_address, int p_rec
|
||||
|
||||
_sock->set_blocking_enabled(false);
|
||||
_sock->set_broadcasting_enabled(broadcast);
|
||||
err = _sock->bind(p_bind_address, p_port);
|
||||
NetSocket::Address addr(p_bind_address, p_port);
|
||||
err = _sock->bind(addr);
|
||||
|
||||
if (err != OK) {
|
||||
_sock->close();
|
||||
@@ -231,12 +232,13 @@ Error PacketPeerUDP::connect_to_host(const IPAddress &p_host, int p_port) {
|
||||
|
||||
if (!_sock->is_open()) {
|
||||
IP::Type ip_type = p_host.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6;
|
||||
err = _sock->open(NetSocket::TYPE_UDP, ip_type);
|
||||
err = _sock->open(NetSocket::Family::INET, NetSocket::TYPE_UDP, ip_type);
|
||||
ERR_FAIL_COND_V(err != OK, ERR_CANT_OPEN);
|
||||
_sock->set_blocking_enabled(false);
|
||||
}
|
||||
|
||||
err = _sock->connect_to_host(p_host, p_port);
|
||||
NetSocket::Address addr(p_host, p_port);
|
||||
err = _sock->connect_to_host(addr);
|
||||
|
||||
// I see no reason why we should get ERR_BUSY (wouldblock/eagain) here.
|
||||
// This is UDP, so connect is only used to tell the OS to which socket
|
||||
@@ -345,9 +347,9 @@ int PacketPeerUDP::get_packet_port() const {
|
||||
}
|
||||
|
||||
int PacketPeerUDP::get_local_port() const {
|
||||
uint16_t local_port;
|
||||
_sock->get_socket_address(nullptr, &local_port);
|
||||
return local_port;
|
||||
NetSocket::Address addr;
|
||||
_sock->get_socket_address(&addr);
|
||||
return addr.port();
|
||||
}
|
||||
|
||||
void PacketPeerUDP::set_dest_address(const IPAddress &p_address, int p_port) {
|
||||
|
90
core/io/socket_server.cpp
Normal file
90
core/io/socket_server.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
/**************************************************************************/
|
||||
/* socket_server.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "socket_server.h"
|
||||
|
||||
void SocketServer::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("is_connection_available"), &SocketServer::is_connection_available);
|
||||
ClassDB::bind_method(D_METHOD("is_listening"), &SocketServer::is_listening);
|
||||
ClassDB::bind_method(D_METHOD("stop"), &SocketServer::stop);
|
||||
ClassDB::bind_method(D_METHOD("take_socket_connection"), &SocketServer::take_socket_connection);
|
||||
}
|
||||
|
||||
Error SocketServer::_listen(const NetSocket::Address &p_addr) {
|
||||
DEV_ASSERT(_sock.is_valid());
|
||||
DEV_ASSERT(_sock->is_open());
|
||||
|
||||
_sock->set_blocking_enabled(false);
|
||||
Error err = _sock->bind(p_addr);
|
||||
|
||||
if (err != OK) {
|
||||
_sock->close();
|
||||
return ERR_ALREADY_IN_USE;
|
||||
}
|
||||
|
||||
err = _sock->listen(MAX_PENDING_CONNECTIONS);
|
||||
|
||||
if (err != OK) {
|
||||
_sock->close();
|
||||
return FAILED;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool SocketServer::is_listening() const {
|
||||
ERR_FAIL_COND_V(_sock.is_null(), false);
|
||||
|
||||
return _sock->is_open();
|
||||
}
|
||||
|
||||
bool SocketServer::is_connection_available() const {
|
||||
ERR_FAIL_COND_V(_sock.is_null(), false);
|
||||
|
||||
if (!_sock->is_open()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Error err = _sock->poll(NetSocket::POLL_TYPE_IN, 0);
|
||||
return (err == OK);
|
||||
}
|
||||
|
||||
void SocketServer::stop() {
|
||||
if (_sock.is_valid()) {
|
||||
_sock->close();
|
||||
}
|
||||
}
|
||||
|
||||
SocketServer::SocketServer() :
|
||||
_sock(NetSocket::create()) {
|
||||
}
|
||||
|
||||
SocketServer::~SocketServer() {
|
||||
stop();
|
||||
}
|
77
core/io/socket_server.h
Normal file
77
core/io/socket_server.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/**************************************************************************/
|
||||
/* socket_server.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/io/net_socket.h"
|
||||
#include "core/io/stream_peer_socket.h"
|
||||
|
||||
class SocketServer : public RefCounted {
|
||||
GDCLASS(SocketServer, RefCounted);
|
||||
|
||||
protected:
|
||||
enum {
|
||||
MAX_PENDING_CONNECTIONS = 8,
|
||||
};
|
||||
|
||||
Ref<NetSocket> _sock;
|
||||
static void _bind_methods();
|
||||
|
||||
Error _listen(const NetSocket::Address &p_addr);
|
||||
|
||||
template <typename T>
|
||||
Ref<T> _take_connection() {
|
||||
Ref<T> conn;
|
||||
if (!is_connection_available()) {
|
||||
return conn;
|
||||
}
|
||||
|
||||
Ref<NetSocket> ns;
|
||||
NetSocket::Address addr;
|
||||
ns = _sock->accept(addr);
|
||||
if (ns.is_null()) {
|
||||
return conn;
|
||||
}
|
||||
|
||||
conn.instantiate();
|
||||
conn->accept_socket(ns, addr);
|
||||
return conn;
|
||||
}
|
||||
|
||||
public:
|
||||
bool is_listening() const;
|
||||
bool is_connection_available() const;
|
||||
virtual Ref<StreamPeerSocket> take_socket_connection() = 0;
|
||||
|
||||
void stop(); // Stop listening
|
||||
|
||||
SocketServer();
|
||||
~SocketServer();
|
||||
};
|
52
core/io/stream_peer_socket.compat.inc
Normal file
52
core/io/stream_peer_socket.compat.inc
Normal file
@@ -0,0 +1,52 @@
|
||||
/**************************************************************************/
|
||||
/* stream_peer_socket.compat.inc */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
|
||||
namespace compat::StreamPeerTCP {
|
||||
enum class Status {
|
||||
STATUS_NONE = StreamPeerSocket::STATUS_NONE,
|
||||
STATUS_CONNECTING = StreamPeerSocket::STATUS_CONNECTING,
|
||||
STATUS_CONNECTED = StreamPeerSocket::STATUS_CONNECTED,
|
||||
STATUS_ERROR = StreamPeerSocket::STATUS_ERROR,
|
||||
};
|
||||
}
|
||||
|
||||
VARIANT_ENUM_CAST(compat::StreamPeerTCP::Status);
|
||||
|
||||
compat::StreamPeerTCP::Status StreamPeerSocket::_get_status_compat_107954() const {
|
||||
return (compat::StreamPeerTCP::Status)get_status();
|
||||
}
|
||||
|
||||
void StreamPeerSocket::_bind_compatibility_methods() {
|
||||
ClassDB::bind_compatibility_method(D_METHOD("get_status"), &StreamPeerSocket::_get_status_compat_107954);
|
||||
}
|
||||
|
||||
#endif
|
236
core/io/stream_peer_socket.cpp
Normal file
236
core/io/stream_peer_socket.cpp
Normal file
@@ -0,0 +1,236 @@
|
||||
/**************************************************************************/
|
||||
/* stream_peer_socket.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "stream_peer_socket.h"
|
||||
#include "stream_peer_socket.compat.inc"
|
||||
|
||||
Error StreamPeerSocket::poll() {
|
||||
if (status == STATUS_CONNECTED) {
|
||||
Error err;
|
||||
err = _sock->poll(NetSocket::POLL_TYPE_IN, 0);
|
||||
if (err == OK) {
|
||||
// FIN received
|
||||
if (_sock->get_available_bytes() == 0) {
|
||||
disconnect_from_host();
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
// Also poll write
|
||||
err = _sock->poll(NetSocket::POLL_TYPE_IN_OUT, 0);
|
||||
if (err != OK && err != ERR_BUSY) {
|
||||
// Got an error
|
||||
disconnect_from_host();
|
||||
status = STATUS_ERROR;
|
||||
return err;
|
||||
}
|
||||
return OK;
|
||||
} else if (status != STATUS_CONNECTING) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error err = _sock->connect_to_host(peer_address);
|
||||
|
||||
if (err == OK) {
|
||||
status = STATUS_CONNECTED;
|
||||
return OK;
|
||||
} else if (err == ERR_BUSY) {
|
||||
// Check for connect timeout
|
||||
if (OS::get_singleton()->get_ticks_msec() > timeout) {
|
||||
disconnect_from_host();
|
||||
status = STATUS_ERROR;
|
||||
return ERR_CONNECTION_ERROR;
|
||||
}
|
||||
// Still trying to connect
|
||||
return OK;
|
||||
}
|
||||
|
||||
disconnect_from_host();
|
||||
status = STATUS_ERROR;
|
||||
return ERR_CONNECTION_ERROR;
|
||||
}
|
||||
|
||||
Error StreamPeerSocket::write(const uint8_t *p_data, int p_bytes, int &r_sent, bool p_block) {
|
||||
ERR_FAIL_COND_V(_sock.is_null(), ERR_UNAVAILABLE);
|
||||
|
||||
if (status != STATUS_CONNECTED) {
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
Error err;
|
||||
int data_to_send = p_bytes;
|
||||
const uint8_t *offset = p_data;
|
||||
int total_sent = 0;
|
||||
|
||||
while (data_to_send) {
|
||||
int sent_amount = 0;
|
||||
err = _sock->send(offset, data_to_send, sent_amount);
|
||||
|
||||
if (err != OK) {
|
||||
if (err != ERR_BUSY) {
|
||||
disconnect_from_host();
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
if (!p_block) {
|
||||
r_sent = total_sent;
|
||||
return OK;
|
||||
}
|
||||
|
||||
// Block and wait for the socket to accept more data
|
||||
err = _sock->poll(NetSocket::POLL_TYPE_OUT, -1);
|
||||
if (err != OK) {
|
||||
disconnect_from_host();
|
||||
return FAILED;
|
||||
}
|
||||
} else {
|
||||
data_to_send -= sent_amount;
|
||||
offset += sent_amount;
|
||||
total_sent += sent_amount;
|
||||
}
|
||||
}
|
||||
|
||||
r_sent = total_sent;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error StreamPeerSocket::read(uint8_t *p_buffer, int p_bytes, int &r_received, bool p_block) {
|
||||
if (status != STATUS_CONNECTED) {
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
Error err;
|
||||
int to_read = p_bytes;
|
||||
int total_read = 0;
|
||||
r_received = 0;
|
||||
|
||||
while (to_read) {
|
||||
int read = 0;
|
||||
err = _sock->recv(p_buffer + total_read, to_read, read);
|
||||
|
||||
if (err != OK) {
|
||||
if (err != ERR_BUSY) {
|
||||
disconnect_from_host();
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
if (!p_block) {
|
||||
r_received = total_read;
|
||||
return OK;
|
||||
}
|
||||
|
||||
err = _sock->poll(NetSocket::POLL_TYPE_IN, -1);
|
||||
|
||||
if (err != OK) {
|
||||
disconnect_from_host();
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
} else if (read == 0) {
|
||||
disconnect_from_host();
|
||||
r_received = total_read;
|
||||
return ERR_FILE_EOF;
|
||||
|
||||
} else {
|
||||
to_read -= read;
|
||||
total_read += read;
|
||||
|
||||
if (!p_block) {
|
||||
r_received = total_read;
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r_received = total_read;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
StreamPeerSocket::Status StreamPeerSocket::get_status() const {
|
||||
return status;
|
||||
}
|
||||
|
||||
void StreamPeerSocket::disconnect_from_host() {
|
||||
if (_sock.is_valid() && _sock->is_open()) {
|
||||
_sock->close();
|
||||
}
|
||||
|
||||
timeout = 0;
|
||||
status = STATUS_NONE;
|
||||
peer_address = NetSocket::Address();
|
||||
}
|
||||
|
||||
Error StreamPeerSocket::wait(NetSocket::PollType p_type, int p_timeout) {
|
||||
ERR_FAIL_COND_V(_sock.is_null() || !_sock->is_open(), ERR_UNAVAILABLE);
|
||||
return _sock->poll(p_type, p_timeout);
|
||||
}
|
||||
|
||||
Error StreamPeerSocket::put_data(const uint8_t *p_data, int p_bytes) {
|
||||
int total;
|
||||
return write(p_data, p_bytes, total, true);
|
||||
}
|
||||
|
||||
Error StreamPeerSocket::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) {
|
||||
return write(p_data, p_bytes, r_sent, false);
|
||||
}
|
||||
|
||||
Error StreamPeerSocket::get_data(uint8_t *p_buffer, int p_bytes) {
|
||||
int total;
|
||||
return read(p_buffer, p_bytes, total, true);
|
||||
}
|
||||
|
||||
Error StreamPeerSocket::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) {
|
||||
return read(p_buffer, p_bytes, r_received, false);
|
||||
}
|
||||
|
||||
int StreamPeerSocket::get_available_bytes() const {
|
||||
ERR_FAIL_COND_V(_sock.is_null(), -1);
|
||||
return _sock->get_available_bytes();
|
||||
}
|
||||
|
||||
void StreamPeerSocket::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("poll"), &StreamPeerSocket::poll);
|
||||
ClassDB::bind_method(D_METHOD("get_status"), &StreamPeerSocket::get_status);
|
||||
ClassDB::bind_method(D_METHOD("disconnect_from_host"), &StreamPeerSocket::disconnect_from_host);
|
||||
|
||||
BIND_ENUM_CONSTANT(STATUS_NONE);
|
||||
BIND_ENUM_CONSTANT(STATUS_CONNECTING);
|
||||
BIND_ENUM_CONSTANT(STATUS_CONNECTED);
|
||||
BIND_ENUM_CONSTANT(STATUS_ERROR);
|
||||
}
|
||||
|
||||
StreamPeerSocket::StreamPeerSocket() :
|
||||
_sock(NetSocket::create()) {
|
||||
}
|
||||
|
||||
StreamPeerSocket::~StreamPeerSocket() {
|
||||
disconnect_from_host();
|
||||
}
|
93
core/io/stream_peer_socket.h
Normal file
93
core/io/stream_peer_socket.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/**************************************************************************/
|
||||
/* stream_peer_socket.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/io/net_socket.h"
|
||||
#include "core/io/stream_peer.h"
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
namespace compat::StreamPeerTCP {
|
||||
enum class Status;
|
||||
} //namespace compat::StreamPeerTCP
|
||||
#endif
|
||||
|
||||
class StreamPeerSocket : public StreamPeer {
|
||||
GDCLASS(StreamPeerSocket, StreamPeer);
|
||||
|
||||
public:
|
||||
enum Status {
|
||||
STATUS_NONE,
|
||||
STATUS_CONNECTING,
|
||||
STATUS_CONNECTED,
|
||||
STATUS_ERROR,
|
||||
};
|
||||
|
||||
protected:
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
compat::StreamPeerTCP::Status _get_status_compat_107954() const;
|
||||
static void _bind_compatibility_methods();
|
||||
#endif
|
||||
|
||||
Ref<NetSocket> _sock;
|
||||
uint64_t timeout = 0;
|
||||
Status status = STATUS_NONE;
|
||||
NetSocket::Address peer_address;
|
||||
|
||||
Error write(const uint8_t *p_data, int p_bytes, int &r_sent, bool p_block);
|
||||
Error read(uint8_t *p_buffer, int p_bytes, int &r_received, bool p_block);
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual void accept_socket(Ref<NetSocket> p_sock, const NetSocket::Address &p_addr) = 0;
|
||||
|
||||
void disconnect_from_host();
|
||||
|
||||
int get_available_bytes() const override;
|
||||
Status get_status() const;
|
||||
|
||||
// Poll socket updating its state.
|
||||
Error poll();
|
||||
|
||||
// Wait or check for writable, readable.
|
||||
Error wait(NetSocket::PollType p_type, int p_timeout = 0);
|
||||
|
||||
// Read/Write from StreamPeer
|
||||
Error put_data(const uint8_t *p_data, int p_bytes) override;
|
||||
Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) override;
|
||||
Error get_data(uint8_t *p_buffer, int p_bytes) override;
|
||||
Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) override;
|
||||
|
||||
StreamPeerSocket();
|
||||
virtual ~StreamPeerSocket();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(StreamPeerSocket::Status);
|
@@ -32,60 +32,14 @@
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
|
||||
Error StreamPeerTCP::poll() {
|
||||
if (status == STATUS_CONNECTED) {
|
||||
Error err;
|
||||
err = _sock->poll(NetSocket::POLL_TYPE_IN, 0);
|
||||
if (err == OK) {
|
||||
// FIN received
|
||||
if (_sock->get_available_bytes() == 0) {
|
||||
disconnect_from_host();
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
// Also poll write
|
||||
err = _sock->poll(NetSocket::POLL_TYPE_IN_OUT, 0);
|
||||
if (err != OK && err != ERR_BUSY) {
|
||||
// Got an error
|
||||
disconnect_from_host();
|
||||
status = STATUS_ERROR;
|
||||
return err;
|
||||
}
|
||||
return OK;
|
||||
} else if (status != STATUS_CONNECTING) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error err = _sock->connect_to_host(peer_host, peer_port);
|
||||
|
||||
if (err == OK) {
|
||||
status = STATUS_CONNECTED;
|
||||
return OK;
|
||||
} else if (err == ERR_BUSY) {
|
||||
// Check for connect timeout
|
||||
if (OS::get_singleton()->get_ticks_msec() > timeout) {
|
||||
disconnect_from_host();
|
||||
status = STATUS_ERROR;
|
||||
return ERR_CONNECTION_ERROR;
|
||||
}
|
||||
// Still trying to connect
|
||||
return OK;
|
||||
}
|
||||
|
||||
disconnect_from_host();
|
||||
status = STATUS_ERROR;
|
||||
return ERR_CONNECTION_ERROR;
|
||||
}
|
||||
|
||||
void StreamPeerTCP::accept_socket(Ref<NetSocket> p_sock, IPAddress p_host, uint16_t p_port) {
|
||||
void StreamPeerTCP::accept_socket(Ref<NetSocket> p_sock, const NetSocket::Address &p_addr) {
|
||||
_sock = p_sock;
|
||||
_sock->set_blocking_enabled(false);
|
||||
|
||||
timeout = OS::get_singleton()->get_ticks_msec() + (((uint64_t)GLOBAL_GET("network/limits/tcp/connect_timeout_seconds")) * 1000);
|
||||
status = STATUS_CONNECTED;
|
||||
|
||||
peer_host = p_host;
|
||||
peer_port = p_port;
|
||||
peer_address = p_addr;
|
||||
}
|
||||
|
||||
Error StreamPeerTCP::bind(int p_port, const IPAddress &p_host) {
|
||||
@@ -97,12 +51,13 @@ Error StreamPeerTCP::bind(int p_port, const IPAddress &p_host) {
|
||||
if (p_host.is_wildcard()) {
|
||||
ip_type = IP::TYPE_ANY;
|
||||
}
|
||||
Error err = _sock->open(NetSocket::TYPE_TCP, ip_type);
|
||||
Error err = _sock->open(NetSocket::Family::INET, NetSocket::TYPE_TCP, ip_type);
|
||||
if (err != OK) {
|
||||
return err;
|
||||
}
|
||||
_sock->set_blocking_enabled(false);
|
||||
return _sock->bind(p_host, p_port);
|
||||
NetSocket::Address addr(p_host, p_port);
|
||||
return _sock->bind(addr);
|
||||
}
|
||||
|
||||
Error StreamPeerTCP::connect_to_host(const IPAddress &p_host, int p_port) {
|
||||
@@ -113,7 +68,7 @@ Error StreamPeerTCP::connect_to_host(const IPAddress &p_host, int p_port) {
|
||||
|
||||
if (!_sock->is_open()) {
|
||||
IP::Type ip_type = p_host.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6;
|
||||
Error err = _sock->open(NetSocket::TYPE_TCP, ip_type);
|
||||
Error err = _sock->open(NetSocket::Family::INET, NetSocket::TYPE_TCP, ip_type);
|
||||
if (err != OK) {
|
||||
return err;
|
||||
}
|
||||
@@ -121,7 +76,9 @@ Error StreamPeerTCP::connect_to_host(const IPAddress &p_host, int p_port) {
|
||||
}
|
||||
|
||||
timeout = OS::get_singleton()->get_ticks_msec() + (((uint64_t)GLOBAL_GET("network/limits/tcp/connect_timeout_seconds")) * 1000);
|
||||
Error err = _sock->connect_to_host(p_host, p_port);
|
||||
|
||||
NetSocket::Address addr(p_host, p_port);
|
||||
Error err = _sock->connect_to_host(addr);
|
||||
|
||||
if (err == OK) {
|
||||
status = STATUS_CONNECTED;
|
||||
@@ -133,106 +90,7 @@ Error StreamPeerTCP::connect_to_host(const IPAddress &p_host, int p_port) {
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
peer_host = p_host;
|
||||
peer_port = p_port;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error StreamPeerTCP::write(const uint8_t *p_data, int p_bytes, int &r_sent, bool p_block) {
|
||||
ERR_FAIL_COND_V(_sock.is_null(), ERR_UNAVAILABLE);
|
||||
|
||||
if (status != STATUS_CONNECTED) {
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
Error err;
|
||||
int data_to_send = p_bytes;
|
||||
const uint8_t *offset = p_data;
|
||||
int total_sent = 0;
|
||||
|
||||
while (data_to_send) {
|
||||
int sent_amount = 0;
|
||||
err = _sock->send(offset, data_to_send, sent_amount);
|
||||
|
||||
if (err != OK) {
|
||||
if (err != ERR_BUSY) {
|
||||
disconnect_from_host();
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
if (!p_block) {
|
||||
r_sent = total_sent;
|
||||
return OK;
|
||||
}
|
||||
|
||||
// Block and wait for the socket to accept more data
|
||||
err = _sock->poll(NetSocket::POLL_TYPE_OUT, -1);
|
||||
if (err != OK) {
|
||||
disconnect_from_host();
|
||||
return FAILED;
|
||||
}
|
||||
} else {
|
||||
data_to_send -= sent_amount;
|
||||
offset += sent_amount;
|
||||
total_sent += sent_amount;
|
||||
}
|
||||
}
|
||||
|
||||
r_sent = total_sent;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error StreamPeerTCP::read(uint8_t *p_buffer, int p_bytes, int &r_received, bool p_block) {
|
||||
if (status != STATUS_CONNECTED) {
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
Error err;
|
||||
int to_read = p_bytes;
|
||||
int total_read = 0;
|
||||
r_received = 0;
|
||||
|
||||
while (to_read) {
|
||||
int read = 0;
|
||||
err = _sock->recv(p_buffer + total_read, to_read, read);
|
||||
|
||||
if (err != OK) {
|
||||
if (err != ERR_BUSY) {
|
||||
disconnect_from_host();
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
if (!p_block) {
|
||||
r_received = total_read;
|
||||
return OK;
|
||||
}
|
||||
|
||||
err = _sock->poll(NetSocket::POLL_TYPE_IN, -1);
|
||||
|
||||
if (err != OK) {
|
||||
disconnect_from_host();
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
} else if (read == 0) {
|
||||
disconnect_from_host();
|
||||
r_received = total_read;
|
||||
return ERR_FILE_EOF;
|
||||
|
||||
} else {
|
||||
to_read -= read;
|
||||
total_read += read;
|
||||
|
||||
if (!p_block) {
|
||||
r_received = total_read;
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r_received = total_read;
|
||||
peer_address = addr;
|
||||
|
||||
return OK;
|
||||
}
|
||||
@@ -242,61 +100,18 @@ void StreamPeerTCP::set_no_delay(bool p_enabled) {
|
||||
_sock->set_tcp_no_delay_enabled(p_enabled);
|
||||
}
|
||||
|
||||
StreamPeerTCP::Status StreamPeerTCP::get_status() const {
|
||||
return status;
|
||||
}
|
||||
|
||||
void StreamPeerTCP::disconnect_from_host() {
|
||||
if (_sock.is_valid() && _sock->is_open()) {
|
||||
_sock->close();
|
||||
}
|
||||
|
||||
timeout = 0;
|
||||
status = STATUS_NONE;
|
||||
peer_host = IPAddress();
|
||||
peer_port = 0;
|
||||
}
|
||||
|
||||
Error StreamPeerTCP::wait(NetSocket::PollType p_type, int p_timeout) {
|
||||
ERR_FAIL_COND_V(_sock.is_null() || !_sock->is_open(), ERR_UNAVAILABLE);
|
||||
return _sock->poll(p_type, p_timeout);
|
||||
}
|
||||
|
||||
Error StreamPeerTCP::put_data(const uint8_t *p_data, int p_bytes) {
|
||||
int total;
|
||||
return write(p_data, p_bytes, total, true);
|
||||
}
|
||||
|
||||
Error StreamPeerTCP::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) {
|
||||
return write(p_data, p_bytes, r_sent, false);
|
||||
}
|
||||
|
||||
Error StreamPeerTCP::get_data(uint8_t *p_buffer, int p_bytes) {
|
||||
int total;
|
||||
return read(p_buffer, p_bytes, total, true);
|
||||
}
|
||||
|
||||
Error StreamPeerTCP::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) {
|
||||
return read(p_buffer, p_bytes, r_received, false);
|
||||
}
|
||||
|
||||
int StreamPeerTCP::get_available_bytes() const {
|
||||
ERR_FAIL_COND_V(_sock.is_null(), -1);
|
||||
return _sock->get_available_bytes();
|
||||
}
|
||||
|
||||
IPAddress StreamPeerTCP::get_connected_host() const {
|
||||
return peer_host;
|
||||
return peer_address.ip();
|
||||
}
|
||||
|
||||
int StreamPeerTCP::get_connected_port() const {
|
||||
return peer_port;
|
||||
return peer_address.port();
|
||||
}
|
||||
|
||||
int StreamPeerTCP::get_local_port() const {
|
||||
uint16_t local_port;
|
||||
_sock->get_socket_address(nullptr, &local_port);
|
||||
return local_port;
|
||||
NetSocket::Address addr;
|
||||
_sock->get_socket_address(&addr);
|
||||
return addr.port();
|
||||
}
|
||||
|
||||
Error StreamPeerTCP::_connect(const String &p_address, int p_port) {
|
||||
@@ -316,24 +131,8 @@ Error StreamPeerTCP::_connect(const String &p_address, int p_port) {
|
||||
void StreamPeerTCP::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("bind", "port", "host"), &StreamPeerTCP::bind, DEFVAL("*"));
|
||||
ClassDB::bind_method(D_METHOD("connect_to_host", "host", "port"), &StreamPeerTCP::_connect);
|
||||
ClassDB::bind_method(D_METHOD("poll"), &StreamPeerTCP::poll);
|
||||
ClassDB::bind_method(D_METHOD("get_status"), &StreamPeerTCP::get_status);
|
||||
ClassDB::bind_method(D_METHOD("get_connected_host"), &StreamPeerTCP::get_connected_host);
|
||||
ClassDB::bind_method(D_METHOD("get_connected_port"), &StreamPeerTCP::get_connected_port);
|
||||
ClassDB::bind_method(D_METHOD("get_local_port"), &StreamPeerTCP::get_local_port);
|
||||
ClassDB::bind_method(D_METHOD("disconnect_from_host"), &StreamPeerTCP::disconnect_from_host);
|
||||
ClassDB::bind_method(D_METHOD("set_no_delay", "enabled"), &StreamPeerTCP::set_no_delay);
|
||||
|
||||
BIND_ENUM_CONSTANT(STATUS_NONE);
|
||||
BIND_ENUM_CONSTANT(STATUS_CONNECTING);
|
||||
BIND_ENUM_CONSTANT(STATUS_CONNECTED);
|
||||
BIND_ENUM_CONSTANT(STATUS_ERROR);
|
||||
}
|
||||
|
||||
StreamPeerTCP::StreamPeerTCP() :
|
||||
_sock(Ref<NetSocket>(NetSocket::create())) {
|
||||
}
|
||||
|
||||
StreamPeerTCP::~StreamPeerTCP() {
|
||||
disconnect_from_host();
|
||||
}
|
||||
|
@@ -32,62 +32,24 @@
|
||||
|
||||
#include "core/io/ip.h"
|
||||
#include "core/io/ip_address.h"
|
||||
#include "core/io/net_socket.h"
|
||||
#include "core/io/stream_peer.h"
|
||||
#include "core/io/stream_peer_socket.h"
|
||||
|
||||
class StreamPeerTCP : public StreamPeer {
|
||||
GDCLASS(StreamPeerTCP, StreamPeer);
|
||||
|
||||
public:
|
||||
enum Status {
|
||||
STATUS_NONE,
|
||||
STATUS_CONNECTING,
|
||||
STATUS_CONNECTED,
|
||||
STATUS_ERROR,
|
||||
};
|
||||
class StreamPeerTCP : public StreamPeerSocket {
|
||||
GDCLASS(StreamPeerTCP, StreamPeerSocket);
|
||||
|
||||
protected:
|
||||
Ref<NetSocket> _sock;
|
||||
uint64_t timeout = 0;
|
||||
Status status = STATUS_NONE;
|
||||
IPAddress peer_host;
|
||||
uint16_t peer_port = 0;
|
||||
|
||||
Error _connect(const String &p_address, int p_port);
|
||||
Error write(const uint8_t *p_data, int p_bytes, int &r_sent, bool p_block);
|
||||
Error read(uint8_t *p_buffer, int p_bytes, int &r_received, bool p_block);
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void accept_socket(Ref<NetSocket> p_sock, IPAddress p_host, uint16_t p_port);
|
||||
void accept_socket(Ref<NetSocket> p_sock, const NetSocket::Address &p_addr) override;
|
||||
|
||||
Error bind(int p_port, const IPAddress &p_host);
|
||||
Error connect_to_host(const IPAddress &p_host, int p_port);
|
||||
IPAddress get_connected_host() const;
|
||||
int get_connected_port() const;
|
||||
int get_local_port() const;
|
||||
void disconnect_from_host();
|
||||
|
||||
int get_available_bytes() const override;
|
||||
Status get_status() const;
|
||||
|
||||
void set_no_delay(bool p_enabled);
|
||||
|
||||
// Poll socket updating its state.
|
||||
Error poll();
|
||||
|
||||
// Wait or check for writable, readable.
|
||||
Error wait(NetSocket::PollType p_type, int p_timeout = 0);
|
||||
|
||||
// Read/Write from StreamPeer
|
||||
Error put_data(const uint8_t *p_data, int p_bytes) override;
|
||||
Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) override;
|
||||
Error get_data(uint8_t *p_buffer, int p_bytes) override;
|
||||
Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) override;
|
||||
|
||||
StreamPeerTCP();
|
||||
~StreamPeerTCP();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(StreamPeerTCP::Status);
|
||||
|
99
core/io/stream_peer_uds.cpp
Normal file
99
core/io/stream_peer_uds.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
/**************************************************************************/
|
||||
/* stream_peer_uds.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "stream_peer_uds.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
|
||||
void StreamPeerUDS::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("bind", "path"), &StreamPeerUDS::bind);
|
||||
ClassDB::bind_method(D_METHOD("connect_to_host", "path"), &StreamPeerUDS::connect_to_host);
|
||||
ClassDB::bind_method(D_METHOD("get_connected_path"), &StreamPeerUDS::get_connected_path);
|
||||
}
|
||||
|
||||
void StreamPeerUDS::accept_socket(Ref<NetSocket> p_sock, const NetSocket::Address &p_addr) {
|
||||
_sock = p_sock;
|
||||
_sock->set_blocking_enabled(false);
|
||||
|
||||
timeout = OS::get_singleton()->get_ticks_msec() + (((uint64_t)GLOBAL_GET("network/limits/unix/connect_timeout_seconds")) * 1000);
|
||||
status = STATUS_CONNECTED;
|
||||
}
|
||||
|
||||
Error StreamPeerUDS::bind(const String &p_path) {
|
||||
ERR_FAIL_COND_V(_sock.is_null(), ERR_UNAVAILABLE);
|
||||
ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE);
|
||||
|
||||
IP::Type ip_type = IP::TYPE_NONE;
|
||||
Error err = _sock->open(NetSocket::Family::UNIX, NetSocket::TYPE_NONE, ip_type);
|
||||
if (err != OK) {
|
||||
return err;
|
||||
}
|
||||
_sock->set_blocking_enabled(false);
|
||||
NetSocket::Address addr(p_path);
|
||||
return _sock->bind(addr);
|
||||
}
|
||||
|
||||
Error StreamPeerUDS::connect_to_host(const String &p_path) {
|
||||
ERR_FAIL_COND_V(_sock.is_null(), ERR_UNAVAILABLE);
|
||||
ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE);
|
||||
ERR_FAIL_COND_V(p_path.is_empty(), ERR_INVALID_PARAMETER);
|
||||
|
||||
if (!_sock->is_open()) {
|
||||
IP::Type ip_type = IP::TYPE_NONE;
|
||||
Error err = _sock->open(NetSocket::Family::UNIX, NetSocket::TYPE_NONE, ip_type);
|
||||
if (err != OK) {
|
||||
return err;
|
||||
}
|
||||
_sock->set_blocking_enabled(false);
|
||||
}
|
||||
|
||||
timeout = OS::get_singleton()->get_ticks_msec() + (((uint64_t)GLOBAL_GET("network/limits/unix/connect_timeout_seconds")) * 1000);
|
||||
NetSocket::Address addr(p_path);
|
||||
Error err = _sock->connect_to_host(addr);
|
||||
|
||||
if (err == OK) {
|
||||
status = STATUS_CONNECTED;
|
||||
} else if (err == ERR_BUSY) {
|
||||
status = STATUS_CONNECTING;
|
||||
} else {
|
||||
ERR_PRINT("Connection to remote host failed!");
|
||||
disconnect_from_host();
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
peer_address = addr;
|
||||
peer_path = p_path;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
const String StreamPeerUDS::get_connected_path() const {
|
||||
return String(peer_address.get_path().get_data());
|
||||
}
|
48
core/io/stream_peer_uds.h
Normal file
48
core/io/stream_peer_uds.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/**************************************************************************/
|
||||
/* stream_peer_uds.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/io/stream_peer_socket.h"
|
||||
|
||||
class StreamPeerUDS : public StreamPeerSocket {
|
||||
GDCLASS(StreamPeerUDS, StreamPeerSocket);
|
||||
|
||||
protected:
|
||||
String peer_path;
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void accept_socket(Ref<NetSocket> p_sock, const NetSocket::Address &p_addr) override;
|
||||
|
||||
Error bind(const String &p_path);
|
||||
Error connect_to_host(const String &p_path);
|
||||
const String get_connected_path() const;
|
||||
};
|
@@ -32,11 +32,8 @@
|
||||
|
||||
void TCPServer::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("listen", "port", "bind_address"), &TCPServer::listen, DEFVAL("*"));
|
||||
ClassDB::bind_method(D_METHOD("is_connection_available"), &TCPServer::is_connection_available);
|
||||
ClassDB::bind_method(D_METHOD("is_listening"), &TCPServer::is_listening);
|
||||
ClassDB::bind_method(D_METHOD("get_local_port"), &TCPServer::get_local_port);
|
||||
ClassDB::bind_method(D_METHOD("take_connection"), &TCPServer::take_connection);
|
||||
ClassDB::bind_method(D_METHOD("stop"), &TCPServer::stop);
|
||||
}
|
||||
|
||||
Error TCPServer::listen(uint16_t p_port, const IPAddress &p_bind_address) {
|
||||
@@ -52,81 +49,21 @@ Error TCPServer::listen(uint16_t p_port, const IPAddress &p_bind_address) {
|
||||
ip_type = p_bind_address.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6;
|
||||
}
|
||||
|
||||
err = _sock->open(NetSocket::TYPE_TCP, ip_type);
|
||||
err = _sock->open(NetSocket::Family::INET, NetSocket::TYPE_TCP, ip_type);
|
||||
|
||||
ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
|
||||
|
||||
_sock->set_blocking_enabled(false);
|
||||
_sock->set_reuse_address_enabled(true);
|
||||
|
||||
err = _sock->bind(p_bind_address, p_port);
|
||||
|
||||
if (err != OK) {
|
||||
_sock->close();
|
||||
return ERR_ALREADY_IN_USE;
|
||||
}
|
||||
|
||||
err = _sock->listen(MAX_PENDING_CONNECTIONS);
|
||||
|
||||
if (err != OK) {
|
||||
_sock->close();
|
||||
return FAILED;
|
||||
}
|
||||
return OK;
|
||||
return _listen(NetSocket::Address(p_bind_address, p_port));
|
||||
}
|
||||
|
||||
int TCPServer::get_local_port() const {
|
||||
uint16_t local_port;
|
||||
_sock->get_socket_address(nullptr, &local_port);
|
||||
return local_port;
|
||||
}
|
||||
|
||||
bool TCPServer::is_listening() const {
|
||||
ERR_FAIL_COND_V(_sock.is_null(), false);
|
||||
|
||||
return _sock->is_open();
|
||||
}
|
||||
|
||||
bool TCPServer::is_connection_available() const {
|
||||
ERR_FAIL_COND_V(_sock.is_null(), false);
|
||||
|
||||
if (!_sock->is_open()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Error err = _sock->poll(NetSocket::POLL_TYPE_IN, 0);
|
||||
return (err == OK);
|
||||
NetSocket::Address addr;
|
||||
_sock->get_socket_address(&addr);
|
||||
return addr.port();
|
||||
}
|
||||
|
||||
Ref<StreamPeerTCP> TCPServer::take_connection() {
|
||||
Ref<StreamPeerTCP> conn;
|
||||
if (!is_connection_available()) {
|
||||
return conn;
|
||||
}
|
||||
|
||||
Ref<NetSocket> ns;
|
||||
IPAddress ip;
|
||||
uint16_t port = 0;
|
||||
ns = _sock->accept(ip, port);
|
||||
if (ns.is_null()) {
|
||||
return conn;
|
||||
}
|
||||
|
||||
conn.instantiate();
|
||||
conn->accept_socket(ns, ip, port);
|
||||
return conn;
|
||||
}
|
||||
|
||||
void TCPServer::stop() {
|
||||
if (_sock.is_valid()) {
|
||||
_sock->close();
|
||||
}
|
||||
}
|
||||
|
||||
TCPServer::TCPServer() :
|
||||
_sock(Ref<NetSocket>(NetSocket::create())) {
|
||||
}
|
||||
|
||||
TCPServer::~TCPServer() {
|
||||
stop();
|
||||
return _take_connection<StreamPeerTCP>();
|
||||
}
|
||||
|
@@ -31,30 +31,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "core/io/ip.h"
|
||||
#include "core/io/net_socket.h"
|
||||
#include "core/io/stream_peer.h"
|
||||
#include "core/io/socket_server.h"
|
||||
#include "core/io/stream_peer_tcp.h"
|
||||
|
||||
class TCPServer : public RefCounted {
|
||||
GDCLASS(TCPServer, RefCounted);
|
||||
class TCPServer : public SocketServer {
|
||||
GDCLASS(TCPServer, SocketServer);
|
||||
|
||||
protected:
|
||||
enum {
|
||||
MAX_PENDING_CONNECTIONS = 8
|
||||
};
|
||||
|
||||
Ref<NetSocket> _sock;
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
Error listen(uint16_t p_port, const IPAddress &p_bind_address = IPAddress("*"));
|
||||
int get_local_port() const;
|
||||
bool is_listening() const;
|
||||
bool is_connection_available() const;
|
||||
Ref<StreamPeerTCP> take_connection();
|
||||
|
||||
void stop(); // Stop listening
|
||||
|
||||
TCPServer();
|
||||
~TCPServer();
|
||||
Ref<StreamPeerSocket> take_socket_connection() override { return take_connection(); }
|
||||
};
|
||||
|
@@ -99,7 +99,7 @@ Error UDPServer::listen(uint16_t p_port, const IPAddress &p_bind_address) {
|
||||
ip_type = p_bind_address.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6;
|
||||
}
|
||||
|
||||
err = _sock->open(NetSocket::TYPE_UDP, ip_type);
|
||||
err = _sock->open(NetSocket::Family::INET, NetSocket::TYPE_UDP, ip_type);
|
||||
|
||||
if (err != OK) {
|
||||
return ERR_CANT_CREATE;
|
||||
@@ -107,7 +107,8 @@ Error UDPServer::listen(uint16_t p_port, const IPAddress &p_bind_address) {
|
||||
|
||||
_sock->set_blocking_enabled(false);
|
||||
_sock->set_reuse_address_enabled(true);
|
||||
err = _sock->bind(p_bind_address, p_port);
|
||||
NetSocket::Address addr(p_bind_address, p_port);
|
||||
err = _sock->bind(addr);
|
||||
|
||||
if (err != OK) {
|
||||
stop();
|
||||
@@ -117,9 +118,9 @@ Error UDPServer::listen(uint16_t p_port, const IPAddress &p_bind_address) {
|
||||
}
|
||||
|
||||
int UDPServer::get_local_port() const {
|
||||
uint16_t local_port;
|
||||
_sock->get_socket_address(nullptr, &local_port);
|
||||
return local_port;
|
||||
NetSocket::Address addr;
|
||||
_sock->get_socket_address(&addr);
|
||||
return addr.port();
|
||||
}
|
||||
|
||||
bool UDPServer::is_listening() const {
|
||||
|
52
core/io/uds_server.cpp
Normal file
52
core/io/uds_server.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
/**************************************************************************/
|
||||
/* uds_server.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "uds_server.h"
|
||||
|
||||
void UDSServer::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("listen", "path"), &UDSServer::listen);
|
||||
ClassDB::bind_method(D_METHOD("take_connection"), &UDSServer::take_connection);
|
||||
}
|
||||
|
||||
Error UDSServer::listen(const String &p_path) {
|
||||
ERR_FAIL_COND_V(_sock.is_null(), ERR_UNAVAILABLE);
|
||||
ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE);
|
||||
ERR_FAIL_COND_V(p_path.is_empty(), ERR_INVALID_PARAMETER);
|
||||
|
||||
IP::Type ip_type = IP::TYPE_NONE;
|
||||
Error err = _sock->open(NetSocket::Family::UNIX, NetSocket::TYPE_NONE, ip_type);
|
||||
ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
|
||||
|
||||
return _listen(p_path);
|
||||
}
|
||||
|
||||
Ref<StreamPeerUDS> UDSServer::take_connection() {
|
||||
return _take_connection<StreamPeerUDS>();
|
||||
}
|
46
core/io/uds_server.h
Normal file
46
core/io/uds_server.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/**************************************************************************/
|
||||
/* uds_server.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/io/socket_server.h"
|
||||
#include "core/io/stream_peer_uds.h"
|
||||
|
||||
class UDSServer : public SocketServer {
|
||||
GDCLASS(UDSServer, SocketServer);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
Error listen(const String &p_path);
|
||||
Ref<StreamPeerUDS> take_connection();
|
||||
Ref<StreamPeerSocket> take_socket_connection() override { return take_connection(); }
|
||||
};
|
@@ -63,6 +63,7 @@
|
||||
#include "core/io/tcp_server.h"
|
||||
#include "core/io/translation_loader_po.h"
|
||||
#include "core/io/udp_server.h"
|
||||
#include "core/io/uds_server.h"
|
||||
#include "core/io/xml_parser.h"
|
||||
#include "core/math/a_star.h"
|
||||
#include "core/math/a_star_grid_2d.h"
|
||||
@@ -198,12 +199,18 @@ void register_core_types() {
|
||||
GDREGISTER_ABSTRACT_CLASS(IP);
|
||||
|
||||
GDREGISTER_ABSTRACT_CLASS(StreamPeer);
|
||||
GDREGISTER_ABSTRACT_CLASS(StreamPeerSocket);
|
||||
GDREGISTER_ABSTRACT_CLASS(SocketServer);
|
||||
GDREGISTER_CLASS(StreamPeerExtension);
|
||||
GDREGISTER_CLASS(StreamPeerBuffer);
|
||||
GDREGISTER_CLASS(StreamPeerGZIP);
|
||||
GDREGISTER_CLASS(StreamPeerTCP);
|
||||
GDREGISTER_CLASS(TCPServer);
|
||||
|
||||
// IPC using UNIX domain sockets.
|
||||
GDREGISTER_CLASS(StreamPeerUDS);
|
||||
GDREGISTER_CLASS(UDSServer);
|
||||
|
||||
GDREGISTER_ABSTRACT_CLASS(PacketPeer);
|
||||
GDREGISTER_CLASS(PacketPeerExtension);
|
||||
GDREGISTER_CLASS(PacketPeerStream);
|
||||
@@ -322,6 +329,7 @@ void register_core_types() {
|
||||
void register_core_settings() {
|
||||
// Since in register core types, globals may not be present.
|
||||
GLOBAL_DEF(PropertyInfo(Variant::INT, "network/limits/tcp/connect_timeout_seconds", PROPERTY_HINT_RANGE, "1,1800,1"), (30));
|
||||
GLOBAL_DEF(PropertyInfo(Variant::INT, "network/limits/unix/connect_timeout_seconds", PROPERTY_HINT_RANGE, "1,1800,1"), (30));
|
||||
GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "network/limits/packet_peer_stream/max_buffer_po2", PROPERTY_HINT_RANGE, "8,64,1,or_greater"), (16));
|
||||
GLOBAL_DEF(PropertyInfo(Variant::STRING, "network/tls/certificate_bundle_override", PROPERTY_HINT_FILE, "*.crt"), "");
|
||||
|
||||
|
@@ -2377,6 +2377,9 @@
|
||||
<member name="network/limits/tcp/connect_timeout_seconds" type="int" setter="" getter="" default="30">
|
||||
Timeout (in seconds) for connection attempts using TCP.
|
||||
</member>
|
||||
<member name="network/limits/unix/connect_timeout_seconds" type="int" setter="" getter="" default="30">
|
||||
Timeout (in seconds) for connection attempts using UNIX domain socket.
|
||||
</member>
|
||||
<member name="network/limits/webrtc/max_channel_in_buffer_kb" type="int" setter="" getter="" default="64">
|
||||
Maximum size (in kiB) for the [WebRTCDataChannel] input buffer.
|
||||
</member>
|
||||
|
37
doc/classes/SocketServer.xml
Normal file
37
doc/classes/SocketServer.xml
Normal file
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="SocketServer" inherits="RefCounted" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
|
||||
<brief_description>
|
||||
An abstract class for servers based on sockets.
|
||||
</brief_description>
|
||||
<description>
|
||||
A socket server.
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
<methods>
|
||||
<method name="is_connection_available" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<description>
|
||||
Returns [code]true[/code] if a connection is available for taking.
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_listening" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<description>
|
||||
Returns [code]true[/code] if the server is currently listening for connections.
|
||||
</description>
|
||||
</method>
|
||||
<method name="stop">
|
||||
<return type="void" />
|
||||
<description>
|
||||
Stops listening.
|
||||
</description>
|
||||
</method>
|
||||
<method name="take_socket_connection">
|
||||
<return type="StreamPeerSocket" />
|
||||
<description>
|
||||
If a connection is available, returns a StreamPeerSocket with the connection.
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
</class>
|
45
doc/classes/StreamPeerSocket.xml
Normal file
45
doc/classes/StreamPeerSocket.xml
Normal file
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="StreamPeerSocket" inherits="StreamPeer" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
|
||||
<brief_description>
|
||||
Abstract base class for interacting with socket streams.
|
||||
</brief_description>
|
||||
<description>
|
||||
StreamPeerSocket is an abstract base class that defines common behavior for socket-based streams.
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
<methods>
|
||||
<method name="disconnect_from_host">
|
||||
<return type="void" />
|
||||
<description>
|
||||
Disconnects from host.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_status" qualifiers="const">
|
||||
<return type="int" enum="StreamPeerSocket.Status" />
|
||||
<description>
|
||||
Returns the status of the connection.
|
||||
</description>
|
||||
</method>
|
||||
<method name="poll">
|
||||
<return type="int" enum="Error" />
|
||||
<description>
|
||||
Polls the socket, updating its state. See [method get_status].
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
<constants>
|
||||
<constant name="STATUS_NONE" value="0" enum="Status">
|
||||
The initial status of the [StreamPeerSocket]. This is also the status after disconnecting.
|
||||
</constant>
|
||||
<constant name="STATUS_CONNECTING" value="1" enum="Status">
|
||||
A status representing a [StreamPeerSocket] that is connecting to a host.
|
||||
</constant>
|
||||
<constant name="STATUS_CONNECTED" value="2" enum="Status">
|
||||
A status representing a [StreamPeerSocket] that is connected to a host.
|
||||
</constant>
|
||||
<constant name="STATUS_ERROR" value="3" enum="Status">
|
||||
A status representing a [StreamPeerSocket] in error state.
|
||||
</constant>
|
||||
</constants>
|
||||
</class>
|
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="StreamPeerTCP" inherits="StreamPeer" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
|
||||
<class name="StreamPeerTCP" inherits="StreamPeerSocket" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
|
||||
<brief_description>
|
||||
A stream peer that handles TCP connections.
|
||||
</brief_description>
|
||||
@@ -27,12 +27,6 @@
|
||||
Connects to the specified [code]host:port[/code] pair. A hostname will be resolved if valid. Returns [constant OK] on success.
|
||||
</description>
|
||||
</method>
|
||||
<method name="disconnect_from_host">
|
||||
<return type="void" />
|
||||
<description>
|
||||
Disconnects from host.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_connected_host" qualifiers="const">
|
||||
<return type="String" />
|
||||
<description>
|
||||
@@ -51,18 +45,6 @@
|
||||
Returns the local port to which this peer is bound.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_status" qualifiers="const">
|
||||
<return type="int" enum="StreamPeerTCP.Status" />
|
||||
<description>
|
||||
Returns the status of the connection.
|
||||
</description>
|
||||
</method>
|
||||
<method name="poll">
|
||||
<return type="int" enum="Error" />
|
||||
<description>
|
||||
Poll the socket, updating its state. See [method get_status].
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_no_delay">
|
||||
<return type="void" />
|
||||
<param index="0" name="enabled" type="bool" />
|
||||
@@ -72,18 +54,4 @@
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
<constants>
|
||||
<constant name="STATUS_NONE" value="0" enum="Status">
|
||||
The initial status of the [StreamPeerTCP]. This is also the status after disconnecting.
|
||||
</constant>
|
||||
<constant name="STATUS_CONNECTING" value="1" enum="Status">
|
||||
A status representing a [StreamPeerTCP] that is connecting to a host.
|
||||
</constant>
|
||||
<constant name="STATUS_CONNECTED" value="2" enum="Status">
|
||||
A status representing a [StreamPeerTCP] that is connected to a host.
|
||||
</constant>
|
||||
<constant name="STATUS_ERROR" value="3" enum="Status">
|
||||
A status representing a [StreamPeerTCP] in error state.
|
||||
</constant>
|
||||
</constants>
|
||||
</class>
|
||||
|
35
doc/classes/StreamPeerUDS.xml
Normal file
35
doc/classes/StreamPeerUDS.xml
Normal file
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="StreamPeerUDS" inherits="StreamPeerSocket" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
|
||||
<brief_description>
|
||||
A stream peer that handles UNIX Domain Socket (UDS) connections.
|
||||
</brief_description>
|
||||
<description>
|
||||
A stream peer that handles UNIX Domain Socket (UDS) connections. This object can be used to connect to UDS servers, or also is returned by a UDS server. Unix Domain Sockets provide inter-process communication on the same machine using the filesystem namespace.
|
||||
[b]Note:[/b] UNIX Domain Sockets are only available on UNIX-like systems (Linux, macOS, etc.) and are not supported on Windows.
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
<methods>
|
||||
<method name="bind">
|
||||
<return type="int" enum="Error" />
|
||||
<param index="0" name="path" type="String" />
|
||||
<description>
|
||||
Opens the UDS socket, and binds it to the specified socket path.
|
||||
This method is generally not needed, and only used to force the subsequent call to [method connect_to_host] to use the specified [param path] as the source address.
|
||||
</description>
|
||||
</method>
|
||||
<method name="connect_to_host">
|
||||
<return type="int" enum="Error" />
|
||||
<param index="0" name="path" type="String" />
|
||||
<description>
|
||||
Connects to the specified UNIX Domain Socket path. Returns [constant OK] on success.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_connected_path" qualifiers="const">
|
||||
<return type="String" />
|
||||
<description>
|
||||
Returns the socket path of this peer.
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
</class>
|
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="TCPServer" inherits="RefCounted" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
|
||||
<class name="TCPServer" inherits="SocketServer" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
|
||||
<brief_description>
|
||||
A TCP server.
|
||||
</brief_description>
|
||||
@@ -16,18 +16,6 @@
|
||||
Returns the local port this server is listening to.
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_connection_available" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<description>
|
||||
Returns [code]true[/code] if a connection is available for taking.
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_listening" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<description>
|
||||
Returns [code]true[/code] if the server is currently listening for connections.
|
||||
</description>
|
||||
</method>
|
||||
<method name="listen">
|
||||
<return type="int" enum="Error" />
|
||||
<param index="0" name="port" type="int" />
|
||||
@@ -39,12 +27,6 @@
|
||||
If [param bind_address] is set to any valid address (e.g. [code]"192.168.1.101"[/code], [code]"::1"[/code], etc.), the server will only listen on the interface with that address (or fail if no interface with the given address exists).
|
||||
</description>
|
||||
</method>
|
||||
<method name="stop">
|
||||
<return type="void" />
|
||||
<description>
|
||||
Stops listening.
|
||||
</description>
|
||||
</method>
|
||||
<method name="take_connection">
|
||||
<return type="StreamPeerTCP" />
|
||||
<description>
|
||||
|
28
doc/classes/UDSServer.xml
Normal file
28
doc/classes/UDSServer.xml
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="UDSServer" inherits="SocketServer" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
|
||||
<brief_description>
|
||||
A Unix Domain Socket (UDS) server.
|
||||
</brief_description>
|
||||
<description>
|
||||
A Unix Domain Socket (UDS) server. Listens to connections on a socket path and returns a [StreamPeerUDS] when it gets an incoming connection. Unix Domain Sockets provide inter-process communication on the same machine using the filesystem namespace.
|
||||
[b]Note:[/b] Unix Domain Sockets are only available on Unix-like systems (Linux, macOS, etc.) and are not supported on Windows.
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
<methods>
|
||||
<method name="listen">
|
||||
<return type="int" enum="Error" />
|
||||
<param index="0" name="path" type="String" />
|
||||
<description>
|
||||
Listens on the socket at [param path]. The socket file will be created at the specified path.
|
||||
[b]Note:[/b] The socket file must not already exist at the specified path. You may need to remove any existing socket file before calling this method.
|
||||
</description>
|
||||
</method>
|
||||
<method name="take_connection">
|
||||
<return type="StreamPeerUDS" />
|
||||
<description>
|
||||
If a connection is available, returns a StreamPeerUDS with the connection.
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
</class>
|
@@ -41,6 +41,7 @@
|
||||
#include <poll.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <cerrno>
|
||||
@@ -94,6 +95,20 @@ size_t NetSocketUnix::_set_addr_storage(struct sockaddr_storage *p_addr, const I
|
||||
}
|
||||
}
|
||||
|
||||
socklen_t NetSocketUnix::_unix_set_sockaddr(struct sockaddr_un *p_addr, const CharString &p_path) {
|
||||
memset(p_addr, 0, sizeof(struct sockaddr_un));
|
||||
p_addr->sun_family = AF_UNIX;
|
||||
|
||||
// Path must not exceed maximum path length for Unix domain socket
|
||||
size_t path_len = p_path.length();
|
||||
ERR_FAIL_COND_V(path_len >= sizeof(p_addr->sun_path) - 1, 0);
|
||||
|
||||
// Regular file system socket
|
||||
memcpy(p_addr->sun_path, p_path.get_data(), path_len);
|
||||
p_addr->sun_path[path_len] = '\0';
|
||||
return sizeof(struct sockaddr_un);
|
||||
}
|
||||
|
||||
void NetSocketUnix::_set_ip_port(struct sockaddr_storage *p_addr, IPAddress *r_ip, uint16_t *r_port) {
|
||||
if (p_addr->ss_family == AF_INET) {
|
||||
struct sockaddr_in *addr4 = (struct sockaddr_in *)p_addr;
|
||||
@@ -172,8 +187,14 @@ bool NetSocketUnix::_can_use_ip(const IPAddress &p_ip, const bool p_for_bind) co
|
||||
return !(_ip_type != IP::TYPE_ANY && !p_ip.is_wildcard() && _ip_type != type);
|
||||
}
|
||||
|
||||
bool NetSocketUnix::_can_use_path(const CharString &p_path) const {
|
||||
// Path must not exceed maximum path length for Unix domain socket
|
||||
return !p_path.is_empty() && (size_t)p_path.length() < sizeof(((sockaddr_un *)0)->sun_path);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ Error NetSocketUnix::_change_multicast_group(IPAddress p_ip, String p_if_name, bool p_add) {
|
||||
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
|
||||
ERR_FAIL_COND_V(_family != Family::INET, ERR_UNAVAILABLE);
|
||||
ERR_FAIL_COND_V(!_can_use_ip(p_ip, false), ERR_INVALID_PARAMETER);
|
||||
|
||||
// Need to force level and af_family to IP(v4) when using dual stacking and provided multicast group is IPv4.
|
||||
@@ -240,36 +261,36 @@ void NetSocketUnix::_set_close_exec_enabled(bool p_enabled) {
|
||||
fcntl(_sock, F_SETFD, opts | FD_CLOEXEC);
|
||||
}
|
||||
|
||||
Error NetSocketUnix::open(Type p_sock_type, IP::Type &ip_type) {
|
||||
ERR_FAIL_COND_V(is_open(), ERR_ALREADY_IN_USE);
|
||||
ERR_FAIL_COND_V(ip_type > IP::TYPE_ANY || ip_type < IP::TYPE_NONE, ERR_INVALID_PARAMETER);
|
||||
Error NetSocketUnix::_inet_open(Type p_sock_type, IP::Type &r_ip_type) {
|
||||
ERR_FAIL_COND_V(r_ip_type > IP::TYPE_ANY || r_ip_type < IP::TYPE_NONE, ERR_INVALID_PARAMETER);
|
||||
|
||||
#if defined(__OpenBSD__)
|
||||
// OpenBSD does not support dual stacking, fallback to IPv4 only.
|
||||
if (ip_type == IP::TYPE_ANY) {
|
||||
ip_type = IP::TYPE_IPV4;
|
||||
if (r_ip_type == IP::TYPE_ANY) {
|
||||
r_ip_type = IP::TYPE_IPV4;
|
||||
}
|
||||
#endif
|
||||
|
||||
int family = ip_type == IP::TYPE_IPV4 ? AF_INET : AF_INET6;
|
||||
int family = r_ip_type == IP::TYPE_IPV4 ? AF_INET : AF_INET6;
|
||||
int protocol = p_sock_type == TYPE_TCP ? IPPROTO_TCP : IPPROTO_UDP;
|
||||
int type = p_sock_type == TYPE_TCP ? SOCK_STREAM : SOCK_DGRAM;
|
||||
_sock = socket(family, type, protocol);
|
||||
|
||||
if (_sock == -1 && ip_type == IP::TYPE_ANY) {
|
||||
if (_sock == -1 && r_ip_type == IP::TYPE_ANY) {
|
||||
// Careful here, changing the referenced parameter so the caller knows that we are using an IPv4 socket
|
||||
// in place of a dual stack one, and further calls to _set_sock_addr will work as expected.
|
||||
ip_type = IP::TYPE_IPV4;
|
||||
r_ip_type = IP::TYPE_IPV4;
|
||||
family = AF_INET;
|
||||
_sock = socket(family, type, protocol);
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V(_sock == -1, FAILED);
|
||||
_ip_type = ip_type;
|
||||
_ip_type = r_ip_type;
|
||||
_family = Family::INET;
|
||||
|
||||
if (family == AF_INET6) {
|
||||
// Select IPv4 over IPv6 mapping.
|
||||
set_ipv6_only_enabled(ip_type != IP::TYPE_ANY);
|
||||
set_ipv6_only_enabled(r_ip_type != IP::TYPE_ANY);
|
||||
}
|
||||
|
||||
if (protocol == IPPROTO_UDP) {
|
||||
@@ -293,18 +314,59 @@ Error NetSocketUnix::open(Type p_sock_type, IP::Type &ip_type) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error NetSocketUnix::_unix_open() {
|
||||
_sock = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
ERR_FAIL_COND_V(_sock == -1, FAILED);
|
||||
|
||||
_family = Family::UNIX;
|
||||
|
||||
_set_close_exec_enabled(true);
|
||||
|
||||
#if defined(SO_NOSIGPIPE)
|
||||
// Disable SIGPIPE (should only be relevant to stream sockets, but seems to affect UDP too on iOS).
|
||||
int par = 1;
|
||||
if (setsockopt(_sock, SOL_SOCKET, SO_NOSIGPIPE, &par, sizeof(int)) != 0) {
|
||||
print_verbose("Unable to turn off SIGPIPE on socket.");
|
||||
}
|
||||
#endif
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error NetSocketUnix::open(NetSocket::Family p_family, NetSocket::Type p_sock_type, IP::Type &r_ip_type) {
|
||||
ERR_FAIL_COND_V(is_open(), ERR_ALREADY_IN_USE);
|
||||
|
||||
switch (p_family) {
|
||||
case Family::INET:
|
||||
return _inet_open(p_sock_type, r_ip_type);
|
||||
case Family::UNIX:
|
||||
return _unix_open();
|
||||
case Family::NONE:
|
||||
default:
|
||||
return ERR_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
void NetSocketUnix::close() {
|
||||
if (_sock != -1) {
|
||||
::close(_sock);
|
||||
|
||||
if (_family == Family::UNIX) {
|
||||
if (_unlink_on_close) {
|
||||
::unlink(_unix_path.get_data());
|
||||
_unlink_on_close = false;
|
||||
_unix_path = CharString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_sock = -1;
|
||||
_family = Family::NONE;
|
||||
_ip_type = IP::TYPE_NONE;
|
||||
_is_stream = false;
|
||||
}
|
||||
|
||||
Error NetSocketUnix::bind(IPAddress p_addr, uint16_t p_port) {
|
||||
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
|
||||
Error NetSocketUnix::_inet_bind(IPAddress p_addr, uint16_t p_port) {
|
||||
ERR_FAIL_COND_V(!_can_use_ip(p_addr, true), ERR_INVALID_PARAMETER);
|
||||
|
||||
sockaddr_storage addr;
|
||||
@@ -320,6 +382,69 @@ Error NetSocketUnix::bind(IPAddress p_addr, uint16_t p_port) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error NetSocketUnix::_unix_bind(const CharString &p_path) {
|
||||
ERR_FAIL_COND_V(p_path.is_empty(), ERR_INVALID_PARAMETER);
|
||||
|
||||
struct sockaddr_un addr;
|
||||
socklen_t addr_size = _unix_set_sockaddr(&addr, p_path);
|
||||
ERR_FAIL_COND_V(addr_size == 0, ERR_INVALID_PARAMETER);
|
||||
|
||||
// If the socket file exists, attempt to remove it.
|
||||
if (access(p_path.get_data(), F_OK) == 0) {
|
||||
// Check if it's a socket
|
||||
struct stat st;
|
||||
if (stat(p_path.get_data(), &st) == 0) {
|
||||
if (S_ISSOCK(st.st_mode)) {
|
||||
// It is a socket, try to remove it.
|
||||
if (unlink(p_path.get_data()) != 0) {
|
||||
// Failed to remove existing socket file.
|
||||
return FAILED;
|
||||
}
|
||||
} else {
|
||||
// It's not a socket, don't remove it.
|
||||
return ERR_ALREADY_EXISTS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_unlink_on_close = true;
|
||||
|
||||
if (::bind(_sock, (struct sockaddr *)&addr, addr_size) != 0) {
|
||||
NetError err = _get_socket_error();
|
||||
print_verbose("Failed to bind socket. Error: " + itos(err) + ".");
|
||||
close();
|
||||
switch (err) {
|
||||
case ERR_NET_UNAUTHORIZED:
|
||||
return ERR_UNAUTHORIZED;
|
||||
default:
|
||||
return ERR_UNAVAILABLE;
|
||||
}
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error NetSocketUnix::bind(NetSocket::Address p_addr) {
|
||||
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
|
||||
ERR_FAIL_COND_V(_family != p_addr.get_family(), ERR_INVALID_PARAMETER);
|
||||
switch (p_addr.get_family()) {
|
||||
case Family::INET: {
|
||||
Error res = _inet_bind(p_addr.ip(), p_addr.port());
|
||||
ERR_FAIL_COND_V(res != OK, res);
|
||||
} break;
|
||||
case Family::UNIX: {
|
||||
_unix_path = p_addr.get_path();
|
||||
Error res = _unix_bind(_unix_path);
|
||||
ERR_FAIL_COND_V(res != OK, res);
|
||||
} break;
|
||||
case Family::NONE:
|
||||
default:
|
||||
return ERR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error NetSocketUnix::listen(int p_max_pending) {
|
||||
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
|
||||
|
||||
@@ -333,8 +458,7 @@ Error NetSocketUnix::listen(int p_max_pending) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error NetSocketUnix::connect_to_host(IPAddress p_host, uint16_t p_port) {
|
||||
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
|
||||
Error NetSocketUnix::_inet_connect_to_host(IPAddress p_host, uint16_t p_port) {
|
||||
ERR_FAIL_COND_V(!_can_use_ip(p_host, false), ERR_INVALID_PARAMETER);
|
||||
|
||||
struct sockaddr_storage addr;
|
||||
@@ -361,6 +485,49 @@ Error NetSocketUnix::connect_to_host(IPAddress p_host, uint16_t p_port) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error NetSocketUnix::_unix_connect_to_host(const CharString &p_path) {
|
||||
ERR_FAIL_COND_V(!_can_use_path(p_path), ERR_INVALID_PARAMETER);
|
||||
|
||||
struct sockaddr_un addr;
|
||||
socklen_t addr_size = _unix_set_sockaddr(&addr, p_path);
|
||||
ERR_FAIL_COND_V(addr_size == 0, ERR_INVALID_PARAMETER);
|
||||
|
||||
if (::connect(_sock, (struct sockaddr *)&addr, addr_size) != 0) {
|
||||
NetError err = _get_socket_error();
|
||||
switch (err) {
|
||||
case ERR_NET_ADDRESS_INVALID_OR_UNAVAILABLE:
|
||||
return ERR_INVALID_PARAMETER;
|
||||
// Still waiting to connect, try again in a while.
|
||||
case ERR_NET_WOULD_BLOCK:
|
||||
case ERR_NET_IN_PROGRESS:
|
||||
return ERR_BUSY;
|
||||
case ERR_NET_UNAUTHORIZED:
|
||||
return ERR_UNAUTHORIZED;
|
||||
default:
|
||||
print_verbose("Connection to host failed.");
|
||||
close();
|
||||
return FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error NetSocketUnix::connect_to_host(NetSocket::Address p_addr) {
|
||||
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
|
||||
ERR_FAIL_COND_V(_family != p_addr.get_family(), ERR_INVALID_PARAMETER);
|
||||
|
||||
switch (p_addr.get_family()) {
|
||||
case Family::INET:
|
||||
return _inet_connect_to_host(p_addr.ip(), p_addr.port());
|
||||
case Family::UNIX:
|
||||
return _unix_connect_to_host(p_addr.get_path());
|
||||
case Family::NONE:
|
||||
default:
|
||||
return ERR_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
Error NetSocketUnix::poll(PollType p_type, int p_timeout) const {
|
||||
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
|
||||
|
||||
@@ -418,6 +585,7 @@ Error NetSocketUnix::recv(uint8_t *p_buffer, int p_len, int &r_read) {
|
||||
|
||||
Error NetSocketUnix::recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port, bool p_peek) {
|
||||
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
|
||||
ERR_FAIL_COND_V(_family != Family::INET, ERR_UNAVAILABLE);
|
||||
|
||||
struct sockaddr_storage from;
|
||||
socklen_t len = sizeof(struct sockaddr_storage);
|
||||
@@ -459,7 +627,7 @@ Error NetSocketUnix::send(const uint8_t *p_buffer, int p_len, int &r_sent) {
|
||||
|
||||
int flags = 0;
|
||||
#ifdef MSG_NOSIGNAL
|
||||
if (_is_stream) {
|
||||
if (_is_stream || _family == Family::UNIX) {
|
||||
flags = MSG_NOSIGNAL;
|
||||
}
|
||||
#endif
|
||||
@@ -482,6 +650,7 @@ Error NetSocketUnix::send(const uint8_t *p_buffer, int p_len, int &r_sent) {
|
||||
|
||||
Error NetSocketUnix::sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port) {
|
||||
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
|
||||
ERR_FAIL_COND_V(_family != Family::INET, ERR_UNAVAILABLE);
|
||||
|
||||
struct sockaddr_storage addr;
|
||||
size_t addr_size = _set_addr_storage(&addr, p_ip, p_port, _ip_type);
|
||||
@@ -580,9 +749,7 @@ int NetSocketUnix::get_available_bytes() const {
|
||||
return len;
|
||||
}
|
||||
|
||||
Error NetSocketUnix::get_socket_address(IPAddress *r_ip, uint16_t *r_port) const {
|
||||
ERR_FAIL_COND_V(!is_open(), FAILED);
|
||||
|
||||
Error NetSocketUnix::_inet_get_socket_address(IPAddress *r_ip, uint16_t *r_port) const {
|
||||
struct sockaddr_storage saddr;
|
||||
socklen_t len = sizeof(saddr);
|
||||
if (getsockname(_sock, (struct sockaddr *)&saddr, &len) != 0) {
|
||||
@@ -594,17 +761,40 @@ Error NetSocketUnix::get_socket_address(IPAddress *r_ip, uint16_t *r_port) const
|
||||
return OK;
|
||||
}
|
||||
|
||||
Ref<NetSocket> NetSocketUnix::accept(IPAddress &r_ip, uint16_t &r_port) {
|
||||
Ref<NetSocket> out;
|
||||
ERR_FAIL_COND_V(!is_open(), out);
|
||||
Error NetSocketUnix::get_socket_address(NetSocket::Address *r_addr) const {
|
||||
ERR_FAIL_COND_V(!is_open(), FAILED);
|
||||
switch (_family) {
|
||||
case Family::INET: {
|
||||
IPAddress ip;
|
||||
uint16_t port = 0;
|
||||
Error res = _inet_get_socket_address(&ip, &port);
|
||||
ERR_FAIL_COND_V(res != OK, res);
|
||||
if (r_addr) {
|
||||
Address addr(ip, port);
|
||||
*r_addr = addr;
|
||||
}
|
||||
} break;
|
||||
case Family::UNIX: {
|
||||
if (r_addr) {
|
||||
*r_addr = Address(_unix_path);
|
||||
}
|
||||
} break;
|
||||
case Family::NONE:
|
||||
default:
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Ref<NetSocket> NetSocketUnix::_inet_accept(IPAddress &r_ip, uint16_t &r_port) {
|
||||
struct sockaddr_storage their_addr;
|
||||
socklen_t size = sizeof(their_addr);
|
||||
int fd = ::accept(_sock, (struct sockaddr *)&their_addr, &size);
|
||||
if (fd == -1) {
|
||||
_get_socket_error();
|
||||
print_verbose("Error when accepting socket connection.");
|
||||
return out;
|
||||
return Ref<NetSocket>();
|
||||
}
|
||||
|
||||
_set_ip_port(&their_addr, &r_ip, &r_port);
|
||||
@@ -615,6 +805,48 @@ Ref<NetSocket> NetSocketUnix::accept(IPAddress &r_ip, uint16_t &r_port) {
|
||||
return Ref<NetSocket>(ns);
|
||||
}
|
||||
|
||||
Ref<NetSocket> NetSocketUnix::_unix_accept() {
|
||||
struct sockaddr_un addr;
|
||||
socklen_t addr_len = sizeof(addr);
|
||||
|
||||
int fd = ::accept(_sock, (struct sockaddr *)&addr, &addr_len);
|
||||
if (fd == -1) {
|
||||
_get_socket_error();
|
||||
print_verbose("Error when accepting socket connection.");
|
||||
return Ref<NetSocket>();
|
||||
}
|
||||
|
||||
NetSocketUnix *ret = memnew(NetSocketUnix);
|
||||
ret->_sock = fd;
|
||||
ret->_family = _family;
|
||||
ret->_unix_path = _unix_path;
|
||||
ret->set_blocking_enabled(false);
|
||||
return Ref<NetSocket>(ret);
|
||||
}
|
||||
|
||||
Ref<NetSocket> NetSocketUnix::accept(NetSocket::Address &r_addr) {
|
||||
Ref<NetSocket> out;
|
||||
ERR_FAIL_COND_V(!is_open(), out);
|
||||
|
||||
switch (_family) {
|
||||
case Family::INET: {
|
||||
IPAddress ip;
|
||||
uint16_t port;
|
||||
out = _inet_accept(ip, port);
|
||||
if (out.is_valid()) {
|
||||
r_addr = Address(ip, port);
|
||||
}
|
||||
} break;
|
||||
case Family::UNIX: {
|
||||
out = _unix_accept();
|
||||
} break;
|
||||
case Family::NONE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
Error NetSocketUnix::join_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) {
|
||||
return _change_multicast_group(p_multi_address, p_if_name, true);
|
||||
}
|
||||
|
@@ -35,14 +35,19 @@
|
||||
#include "core/io/net_socket.h"
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
class NetSocketUnix : public NetSocket {
|
||||
GDSOFTCLASS(NetSocketUnix, NetSocket);
|
||||
|
||||
private:
|
||||
int _sock = -1;
|
||||
Family _family = Family::NONE;
|
||||
IP::Type _ip_type = IP::TYPE_NONE;
|
||||
bool _is_stream = false;
|
||||
CharString _unix_path;
|
||||
// If this is Family::UNIX,
|
||||
bool _unlink_on_close = false;
|
||||
|
||||
enum NetError {
|
||||
ERR_NET_WOULD_BLOCK,
|
||||
@@ -63,6 +68,19 @@ protected:
|
||||
static NetSocket *_create_func();
|
||||
|
||||
bool _can_use_ip(const IPAddress &p_ip, const bool p_for_bind) const;
|
||||
bool _can_use_path(const CharString &p_path) const;
|
||||
|
||||
Error _inet_open(Type p_sock_type, IP::Type &r_ip_type);
|
||||
Error _inet_bind(IPAddress p_addr, uint16_t p_port);
|
||||
Error _inet_connect_to_host(IPAddress p_addr, uint16_t p_port);
|
||||
Error _inet_get_socket_address(IPAddress *r_ip, uint16_t *r_port) const;
|
||||
Ref<NetSocket> _inet_accept(IPAddress &r_ip, uint16_t &r_port);
|
||||
|
||||
static socklen_t _unix_set_sockaddr(struct sockaddr_un *p_addr, const CharString &p_path);
|
||||
Error _unix_open();
|
||||
Error _unix_bind(const CharString &p_path);
|
||||
Error _unix_connect_to_host(const CharString &p_path);
|
||||
Ref<NetSocket> _unix_accept();
|
||||
|
||||
public:
|
||||
static void make_default();
|
||||
@@ -70,21 +88,21 @@ public:
|
||||
static void _set_ip_port(struct sockaddr_storage *p_addr, IPAddress *r_ip, uint16_t *r_port);
|
||||
static size_t _set_addr_storage(struct sockaddr_storage *p_addr, const IPAddress &p_ip, uint16_t p_port, IP::Type p_ip_type);
|
||||
|
||||
virtual Error open(Type p_sock_type, IP::Type &ip_type) override;
|
||||
virtual Error open(Family p_family, Type p_sock_type, IP::Type &r_ip_type) override;
|
||||
virtual void close() override;
|
||||
virtual Error bind(IPAddress p_addr, uint16_t p_port) override;
|
||||
virtual Error bind(Address p_addr) override;
|
||||
virtual Error listen(int p_max_pending) override;
|
||||
virtual Error connect_to_host(IPAddress p_host, uint16_t p_port) override;
|
||||
virtual Error connect_to_host(Address p_addr) override;
|
||||
virtual Error poll(PollType p_type, int timeout) const override;
|
||||
virtual Error recv(uint8_t *p_buffer, int p_len, int &r_read) override;
|
||||
virtual Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port, bool p_peek = false) override;
|
||||
virtual Error send(const uint8_t *p_buffer, int p_len, int &r_sent) override;
|
||||
virtual Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port) override;
|
||||
virtual Ref<NetSocket> accept(IPAddress &r_ip, uint16_t &r_port) override;
|
||||
virtual Ref<NetSocket> accept(Address &r_addr) override;
|
||||
|
||||
virtual bool is_open() const override;
|
||||
virtual int get_available_bytes() const override;
|
||||
virtual Error get_socket_address(IPAddress *r_ip, uint16_t *r_port) const override;
|
||||
virtual Error get_socket_address(Address *r_addr) const override;
|
||||
|
||||
virtual Error set_broadcasting_enabled(bool p_enabled) override;
|
||||
virtual void set_blocking_enabled(bool p_enabled) override;
|
||||
|
@@ -217,7 +217,8 @@ void NetSocketWinSock::_set_socket(SOCKET p_sock, IP::Type p_ip_type, bool p_is_
|
||||
_is_stream = p_is_stream;
|
||||
}
|
||||
|
||||
Error NetSocketWinSock::open(Type p_sock_type, IP::Type &ip_type) {
|
||||
Error NetSocketWinSock::open(Family p_family, Type p_sock_type, IP::Type &ip_type) {
|
||||
ERR_FAIL_COND_V(p_family != Family::INET, ERR_UNAVAILABLE);
|
||||
ERR_FAIL_COND_V(is_open(), ERR_ALREADY_IN_USE);
|
||||
ERR_FAIL_COND_V(ip_type > IP::TYPE_ANY || ip_type < IP::TYPE_NONE, ERR_INVALID_PARAMETER);
|
||||
|
||||
@@ -275,12 +276,13 @@ void NetSocketWinSock::close() {
|
||||
_is_stream = false;
|
||||
}
|
||||
|
||||
Error NetSocketWinSock::bind(IPAddress p_addr, uint16_t p_port) {
|
||||
Error NetSocketWinSock::bind(Address p_addr) {
|
||||
ERR_FAIL_COND_V(!p_addr.is_inet(), ERR_UNAVAILABLE);
|
||||
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
|
||||
ERR_FAIL_COND_V(!_can_use_ip(p_addr, true), ERR_INVALID_PARAMETER);
|
||||
ERR_FAIL_COND_V(!_can_use_ip(p_addr.ip(), true), ERR_INVALID_PARAMETER);
|
||||
|
||||
sockaddr_storage addr;
|
||||
size_t addr_size = _set_addr_storage(&addr, p_addr, p_port, _ip_type);
|
||||
size_t addr_size = _set_addr_storage(&addr, p_addr.ip(), p_addr.port(), _ip_type);
|
||||
|
||||
if (::bind(_sock, (struct sockaddr *)&addr, addr_size) != 0) {
|
||||
NetError err = _get_socket_error();
|
||||
@@ -305,12 +307,13 @@ Error NetSocketWinSock::listen(int p_max_pending) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error NetSocketWinSock::connect_to_host(IPAddress p_host, uint16_t p_port) {
|
||||
Error NetSocketWinSock::connect_to_host(Address p_addr) {
|
||||
ERR_FAIL_COND_V(!p_addr.is_inet(), ERR_UNAVAILABLE);
|
||||
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
|
||||
ERR_FAIL_COND_V(!_can_use_ip(p_host, false), ERR_INVALID_PARAMETER);
|
||||
ERR_FAIL_COND_V(!_can_use_ip(p_addr.ip(), false), ERR_INVALID_PARAMETER);
|
||||
|
||||
struct sockaddr_storage addr;
|
||||
size_t addr_size = _set_addr_storage(&addr, p_host, p_port, _ip_type);
|
||||
size_t addr_size = _set_addr_storage(&addr, p_addr.ip(), p_addr.port(), _ip_type);
|
||||
|
||||
if (::WSAConnect(_sock, (struct sockaddr *)&addr, addr_size, nullptr, nullptr, nullptr, nullptr) != 0) {
|
||||
NetError err = _get_socket_error();
|
||||
@@ -567,7 +570,7 @@ int NetSocketWinSock::get_available_bytes() const {
|
||||
return len;
|
||||
}
|
||||
|
||||
Error NetSocketWinSock::get_socket_address(IPAddress *r_ip, uint16_t *r_port) const {
|
||||
Error NetSocketWinSock::get_socket_address(Address *r_addr) const {
|
||||
ERR_FAIL_COND_V(!is_open(), FAILED);
|
||||
|
||||
struct sockaddr_storage saddr;
|
||||
@@ -577,11 +580,16 @@ Error NetSocketWinSock::get_socket_address(IPAddress *r_ip, uint16_t *r_port) co
|
||||
print_verbose("Error when reading local socket address.");
|
||||
return FAILED;
|
||||
}
|
||||
_set_ip_port(&saddr, r_ip, r_port);
|
||||
IPAddress ip;
|
||||
uint16_t port = 0;
|
||||
_set_ip_port(&saddr, &ip, &port);
|
||||
if (r_addr) {
|
||||
*r_addr = Address(ip, port);
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
Ref<NetSocket> NetSocketWinSock::accept(IPAddress &r_ip, uint16_t &r_port) {
|
||||
Ref<NetSocket> NetSocketWinSock::accept(Address &r_addr) {
|
||||
Ref<NetSocket> out;
|
||||
ERR_FAIL_COND_V(!is_open(), out);
|
||||
|
||||
@@ -594,7 +602,10 @@ Ref<NetSocket> NetSocketWinSock::accept(IPAddress &r_ip, uint16_t &r_port) {
|
||||
return out;
|
||||
}
|
||||
|
||||
_set_ip_port(&their_addr, &r_ip, &r_port);
|
||||
IPAddress ip;
|
||||
uint16_t port = 0;
|
||||
_set_ip_port(&their_addr, &ip, &port);
|
||||
r_addr = Address(ip, port);
|
||||
|
||||
NetSocketWinSock *ns = memnew(NetSocketWinSock);
|
||||
ns->_set_socket(fd, _ip_type, _is_stream);
|
||||
|
@@ -70,21 +70,21 @@ public:
|
||||
static void _set_ip_port(struct sockaddr_storage *p_addr, IPAddress *r_ip, uint16_t *r_port);
|
||||
static size_t _set_addr_storage(struct sockaddr_storage *p_addr, const IPAddress &p_ip, uint16_t p_port, IP::Type p_ip_type);
|
||||
|
||||
virtual Error open(Type p_sock_type, IP::Type &ip_type) override;
|
||||
virtual Error open(Family p_family, Type p_sock_type, IP::Type &ip_type) override;
|
||||
virtual void close() override;
|
||||
virtual Error bind(IPAddress p_addr, uint16_t p_port) override;
|
||||
virtual Error bind(Address p_addr) override;
|
||||
virtual Error listen(int p_max_pending) override;
|
||||
virtual Error connect_to_host(IPAddress p_host, uint16_t p_port) override;
|
||||
virtual Error connect_to_host(Address p_addr) override;
|
||||
virtual Error poll(PollType p_type, int timeout) const override;
|
||||
virtual Error recv(uint8_t *p_buffer, int p_len, int &r_read) override;
|
||||
virtual Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port, bool p_peek = false) override;
|
||||
virtual Error send(const uint8_t *p_buffer, int p_len, int &r_sent) override;
|
||||
virtual Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port) override;
|
||||
virtual Ref<NetSocket> accept(IPAddress &r_ip, uint16_t &r_port) override;
|
||||
virtual Ref<NetSocket> accept(Address &r_addr) override;
|
||||
|
||||
virtual bool is_open() const override;
|
||||
virtual int get_available_bytes() const override;
|
||||
virtual Error get_socket_address(IPAddress *r_ip, uint16_t *r_port) const override;
|
||||
virtual Error get_socket_address(Address *r_addr) const override;
|
||||
|
||||
virtual Error set_broadcasting_enabled(bool p_enabled) override;
|
||||
virtual void set_blocking_enabled(bool p_enabled) override;
|
||||
|
@@ -243,8 +243,7 @@ ScriptEditorDebugger *EditorDebuggerNode::get_default_debugger() const {
|
||||
}
|
||||
|
||||
String EditorDebuggerNode::get_server_uri() const {
|
||||
ERR_FAIL_COND_V(server.is_null(), "");
|
||||
return server->get_uri();
|
||||
return server.is_valid() ? server->get_uri() : "";
|
||||
}
|
||||
|
||||
void EditorDebuggerNode::set_keep_open(bool p_keep_open) {
|
||||
|
@@ -31,30 +31,36 @@
|
||||
#include "editor_debugger_server.h"
|
||||
|
||||
#include "core/io/tcp_server.h"
|
||||
#include "core/io/uds_server.h"
|
||||
#include "core/os/thread.h"
|
||||
#include "editor/editor_log.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/settings/editor_settings.h"
|
||||
|
||||
class EditorDebuggerServerTCP : public EditorDebuggerServer {
|
||||
GDSOFTCLASS(EditorDebuggerServerTCP, EditorDebuggerServer);
|
||||
template <typename T>
|
||||
class EditorDebuggerServerSocket : public EditorDebuggerServer {
|
||||
GDSOFTCLASS(EditorDebuggerServerSocket, EditorDebuggerServer);
|
||||
|
||||
private:
|
||||
Ref<TCPServer> server;
|
||||
protected:
|
||||
Ref<T> server;
|
||||
String endpoint;
|
||||
|
||||
public:
|
||||
static EditorDebuggerServer *create(const String &p_protocol);
|
||||
|
||||
virtual void poll() override {}
|
||||
virtual String get_uri() const override;
|
||||
virtual Error start(const String &p_uri) override;
|
||||
virtual void stop() override;
|
||||
virtual bool is_active() const override;
|
||||
virtual bool is_connection_available() const override;
|
||||
virtual Ref<RemoteDebuggerPeer> take_connection() override;
|
||||
|
||||
EditorDebuggerServerTCP();
|
||||
EditorDebuggerServerSocket();
|
||||
};
|
||||
|
||||
class EditorDebuggerServerTCP : public EditorDebuggerServerSocket<TCPServer> {
|
||||
public:
|
||||
static EditorDebuggerServer *create(const String &p_protocol);
|
||||
|
||||
virtual Error start(const String &p_uri) override;
|
||||
};
|
||||
|
||||
EditorDebuggerServer *EditorDebuggerServerTCP::create(const String &p_protocol) {
|
||||
@@ -62,11 +68,13 @@ EditorDebuggerServer *EditorDebuggerServerTCP::create(const String &p_protocol)
|
||||
return memnew(EditorDebuggerServerTCP);
|
||||
}
|
||||
|
||||
EditorDebuggerServerTCP::EditorDebuggerServerTCP() {
|
||||
template <typename T>
|
||||
EditorDebuggerServerSocket<T>::EditorDebuggerServerSocket() {
|
||||
server.instantiate();
|
||||
}
|
||||
|
||||
String EditorDebuggerServerTCP::get_uri() const {
|
||||
template <typename T>
|
||||
String EditorDebuggerServerSocket<T>::get_uri() const {
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
@@ -104,29 +112,61 @@ Error EditorDebuggerServerTCP::start(const String &p_uri) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
void EditorDebuggerServerTCP::stop() {
|
||||
template <typename T>
|
||||
void EditorDebuggerServerSocket<T>::stop() {
|
||||
server->stop();
|
||||
}
|
||||
|
||||
bool EditorDebuggerServerTCP::is_active() const {
|
||||
template <typename T>
|
||||
bool EditorDebuggerServerSocket<T>::is_active() const {
|
||||
return server->is_listening();
|
||||
}
|
||||
|
||||
bool EditorDebuggerServerTCP::is_connection_available() const {
|
||||
template <typename T>
|
||||
bool EditorDebuggerServerSocket<T>::is_connection_available() const {
|
||||
return server->is_listening() && server->is_connection_available();
|
||||
}
|
||||
|
||||
Ref<RemoteDebuggerPeer> EditorDebuggerServerTCP::take_connection() {
|
||||
ERR_FAIL_COND_V(!is_connection_available(), Ref<RemoteDebuggerPeer>());
|
||||
return memnew(RemoteDebuggerPeerTCP(server->take_connection()));
|
||||
template <typename T>
|
||||
Ref<RemoteDebuggerPeer> EditorDebuggerServerSocket<T>::take_connection() {
|
||||
const Ref<RemoteDebuggerPeer> out;
|
||||
ERR_FAIL_COND_V(!is_connection_available(), out);
|
||||
Ref<StreamPeerSocket> stream = server->take_socket_connection();
|
||||
ERR_FAIL_COND_V(stream.is_null(), out);
|
||||
return memnew(RemoteDebuggerPeerTCP(stream));
|
||||
}
|
||||
|
||||
class EditorDebuggerServerUDS : public EditorDebuggerServerSocket<UDSServer> {
|
||||
public:
|
||||
static EditorDebuggerServer *create(const String &p_protocol);
|
||||
|
||||
virtual Error start(const String &p_uri) override;
|
||||
};
|
||||
|
||||
EditorDebuggerServer *EditorDebuggerServerUDS::create(const String &p_protocol) {
|
||||
ERR_FAIL_COND_V(p_protocol != "unix://", nullptr);
|
||||
return memnew(EditorDebuggerServerUDS);
|
||||
}
|
||||
|
||||
Error EditorDebuggerServerUDS::start(const String &p_uri) {
|
||||
String bind_path = p_uri.is_empty() ? String("/tmp/godot_debugger.sock") : p_uri.replace("unix://", "");
|
||||
|
||||
const Error err = server->listen(bind_path);
|
||||
if (err != OK) {
|
||||
EditorNode::get_log()->add_message(vformat("Cannot listen at path %s, remote debugging unavailable.", bind_path), EditorLog::MSG_TYPE_ERROR);
|
||||
return err;
|
||||
}
|
||||
endpoint = "unix://" + bind_path;
|
||||
return OK;
|
||||
}
|
||||
|
||||
/// EditorDebuggerServer
|
||||
HashMap<StringName, EditorDebuggerServer::CreateServerFunc> EditorDebuggerServer::protocols;
|
||||
|
||||
EditorDebuggerServer *EditorDebuggerServer::create(const String &p_protocol) {
|
||||
ERR_FAIL_COND_V(!protocols.has(p_protocol), nullptr);
|
||||
return protocols[p_protocol](p_protocol);
|
||||
CreateServerFunc *create_fn = protocols.getptr(p_protocol);
|
||||
ERR_FAIL_NULL_V(create_fn, nullptr);
|
||||
return (*create_fn)(p_protocol);
|
||||
}
|
||||
|
||||
void EditorDebuggerServer::register_protocol_handler(const String &p_protocol, CreateServerFunc p_func) {
|
||||
@@ -136,6 +176,9 @@ void EditorDebuggerServer::register_protocol_handler(const String &p_protocol, C
|
||||
|
||||
void EditorDebuggerServer::initialize() {
|
||||
register_protocol_handler("tcp://", EditorDebuggerServerTCP::create);
|
||||
#if defined(UNIX_ENABLED)
|
||||
register_protocol_handler("unix://", EditorDebuggerServerUDS::create);
|
||||
#endif
|
||||
}
|
||||
|
||||
void EditorDebuggerServer::deinitialize() {
|
||||
|
@@ -314,8 +314,12 @@ void EditorRunBar::_run_scene(const String &p_scene_path, const Vector<String> &
|
||||
if (!EditorNode::get_singleton()->call_build()) {
|
||||
return;
|
||||
}
|
||||
|
||||
EditorDebuggerNode::get_singleton()->start();
|
||||
// Use the existing URI, in case it is overridden by the CLI.
|
||||
String uri = EditorDebuggerNode::get_singleton()->get_server_uri();
|
||||
if (uri.is_empty()) {
|
||||
uri = "tcp://";
|
||||
}
|
||||
EditorDebuggerNode::get_singleton()->start(uri);
|
||||
Error error = editor_run.run(run_filename, write_movie_file, p_run_args);
|
||||
if (error != OK) {
|
||||
EditorDebuggerNode::get_singleton()->stop();
|
||||
|
@@ -8,6 +8,18 @@ Add new entries at the end of the file.
|
||||
|
||||
## Changes between 4.5-stable and 4.6-stable
|
||||
|
||||
GH-107954
|
||||
---------
|
||||
Validate extension JSON: API was removed: classes/TCPServer/methods/is_connection_available
|
||||
Validate extension JSON: API was removed: classes/TCPServer/methods/is_listening
|
||||
Validate extension JSON: API was removed: classes/TCPServer/methods/stop
|
||||
Validate extension JSON: API was removed: classes/StreamPeerTCP/methods/disconnect_from_host
|
||||
Validate extension JSON: API was removed: classes/StreamPeerTCP/methods/get_status
|
||||
Validate extension JSON: API was removed: classes/StreamPeerTCP/methods/poll
|
||||
|
||||
These were moved to the parent classes, and are still available.
|
||||
|
||||
|
||||
GH-110250
|
||||
---------
|
||||
Validate extension JSON: JSON file: Field was added in a way that breaks compatibility 'classes/Control/methods/grab_focus': arguments
|
||||
|
@@ -43,21 +43,21 @@ protected:
|
||||
public:
|
||||
static void make_default();
|
||||
|
||||
virtual Error open(Type p_sock_type, IP::Type &ip_type) override { return ERR_UNAVAILABLE; }
|
||||
virtual Error open(Family p_family, Type p_sock_type, IP::Type &ip_type) override { return ERR_UNAVAILABLE; }
|
||||
virtual void close() override {}
|
||||
virtual Error bind(IPAddress p_addr, uint16_t p_port) override { return ERR_UNAVAILABLE; }
|
||||
virtual Error bind(Address p_addr) override { return ERR_UNAVAILABLE; }
|
||||
virtual Error listen(int p_max_pending) override { return ERR_UNAVAILABLE; }
|
||||
virtual Error connect_to_host(IPAddress p_host, uint16_t p_port) override { return ERR_UNAVAILABLE; }
|
||||
virtual Error connect_to_host(Address p_addr) override { return ERR_UNAVAILABLE; }
|
||||
virtual Error poll(PollType p_type, int timeout) const override { return ERR_UNAVAILABLE; }
|
||||
virtual Error recv(uint8_t *p_buffer, int p_len, int &r_read) override { return ERR_UNAVAILABLE; }
|
||||
virtual Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port, bool p_peek = false) override { return ERR_UNAVAILABLE; }
|
||||
virtual Error send(const uint8_t *p_buffer, int p_len, int &r_sent) override { return ERR_UNAVAILABLE; }
|
||||
virtual Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port) override { return ERR_UNAVAILABLE; }
|
||||
virtual Ref<NetSocket> accept(IPAddress &r_ip, uint16_t &r_port) override { return Ref<NetSocket>(); }
|
||||
virtual Ref<NetSocket> accept(Address &r_addr) override { return Ref<NetSocket>(); }
|
||||
|
||||
virtual bool is_open() const override { return false; }
|
||||
virtual int get_available_bytes() const override { return -1; }
|
||||
virtual Error get_socket_address(IPAddress *r_ip, uint16_t *r_port) const override { return ERR_UNAVAILABLE; }
|
||||
virtual Error get_socket_address(Address *r_addr) const override { return ERR_UNAVAILABLE; }
|
||||
|
||||
virtual Error set_broadcasting_enabled(bool p_enabled) override { return ERR_UNAVAILABLE; }
|
||||
virtual void set_blocking_enabled(bool p_enabled) override {}
|
||||
|
302
tests/core/io/test_uds_server.h
Normal file
302
tests/core/io/test_uds_server.h
Normal file
@@ -0,0 +1,302 @@
|
||||
/**************************************************************************/
|
||||
/* test_uds_server.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/io/stream_peer_uds.h"
|
||||
#include "core/io/uds_server.h"
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace TestUDSServer {
|
||||
|
||||
#ifdef UNIX_ENABLED
|
||||
|
||||
const String SOCKET_PATH = "/tmp/godot_test_uds_socket";
|
||||
const uint32_t SLEEP_DURATION = 1000;
|
||||
const uint64_t MAX_WAIT_USEC = 2000000;
|
||||
|
||||
void wait_for_condition(std::function<bool()> f_test) {
|
||||
const uint64_t time = OS::get_singleton()->get_ticks_usec();
|
||||
while (!f_test() && (OS::get_singleton()->get_ticks_usec() - time) < MAX_WAIT_USEC) {
|
||||
OS::get_singleton()->delay_usec(SLEEP_DURATION);
|
||||
}
|
||||
}
|
||||
|
||||
void cleanup_socket_file() {
|
||||
// Remove socket file if it exists
|
||||
if (FileAccess::exists(SOCKET_PATH)) {
|
||||
DirAccess::remove_absolute(SOCKET_PATH);
|
||||
}
|
||||
}
|
||||
|
||||
Ref<UDSServer> create_server(const String &p_path) {
|
||||
cleanup_socket_file();
|
||||
|
||||
Ref<UDSServer> server;
|
||||
server.instantiate();
|
||||
|
||||
REQUIRE_EQ(server->listen(p_path), Error::OK);
|
||||
REQUIRE(server->is_listening());
|
||||
CHECK_FALSE(server->is_connection_available());
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
Ref<StreamPeerUDS> create_client(const String &p_path) {
|
||||
Ref<StreamPeerUDS> client;
|
||||
client.instantiate();
|
||||
|
||||
Error err = client->connect_to_host(p_path);
|
||||
REQUIRE_EQ(err, Error::OK);
|
||||
|
||||
// UDS connections may be immediately connected or in connecting state
|
||||
StreamPeerUDS::Status status = client->get_status();
|
||||
REQUIRE((status == StreamPeerUDS::STATUS_CONNECTED || status == StreamPeerUDS::STATUS_CONNECTING));
|
||||
|
||||
if (status == StreamPeerUDS::STATUS_CONNECTED) {
|
||||
CHECK_EQ(client->get_connected_path(), p_path);
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
Ref<StreamPeerUDS> accept_connection(Ref<UDSServer> &p_server) {
|
||||
wait_for_condition([&]() {
|
||||
return p_server->is_connection_available();
|
||||
});
|
||||
|
||||
REQUIRE(p_server->is_connection_available());
|
||||
Ref<StreamPeerUDS> client_from_server = p_server->take_connection();
|
||||
REQUIRE(client_from_server.is_valid());
|
||||
CHECK_EQ(client_from_server->get_status(), StreamPeerUDS::STATUS_CONNECTED);
|
||||
|
||||
return client_from_server;
|
||||
}
|
||||
|
||||
TEST_CASE("[UDSServer] Instantiation") {
|
||||
Ref<UDSServer> server;
|
||||
server.instantiate();
|
||||
|
||||
REQUIRE(server.is_valid());
|
||||
CHECK_FALSE(server->is_listening());
|
||||
}
|
||||
|
||||
TEST_CASE("[UDSServer] Accept a connection and receive/send data") {
|
||||
Ref<UDSServer> server = create_server(SOCKET_PATH);
|
||||
Ref<StreamPeerUDS> client = create_client(SOCKET_PATH);
|
||||
Ref<StreamPeerUDS> client_from_server = accept_connection(server);
|
||||
|
||||
wait_for_condition([&]() {
|
||||
return client->poll() != Error::OK || client->get_status() == StreamPeerUDS::STATUS_CONNECTED;
|
||||
});
|
||||
|
||||
CHECK_EQ(client->get_status(), StreamPeerUDS::STATUS_CONNECTED);
|
||||
|
||||
// Sending data from client to server.
|
||||
const String hello_world = "Hello World!";
|
||||
client->put_string(hello_world);
|
||||
CHECK_EQ(client_from_server->get_string(), hello_world);
|
||||
|
||||
// Sending data from server to client.
|
||||
const float pi = 3.1415;
|
||||
client_from_server->put_float(pi);
|
||||
CHECK_EQ(client->get_float(), pi);
|
||||
|
||||
client->disconnect_from_host();
|
||||
server->stop();
|
||||
CHECK_FALSE(server->is_listening());
|
||||
|
||||
cleanup_socket_file();
|
||||
}
|
||||
|
||||
TEST_CASE("[UDSServer] Handle multiple clients at the same time") {
|
||||
Ref<UDSServer> server = create_server(SOCKET_PATH);
|
||||
|
||||
Vector<Ref<StreamPeerUDS>> clients;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
clients.push_back(create_client(SOCKET_PATH));
|
||||
}
|
||||
|
||||
Vector<Ref<StreamPeerUDS>> clients_from_server;
|
||||
for (int i = 0; i < clients.size(); i++) {
|
||||
clients_from_server.push_back(accept_connection(server));
|
||||
}
|
||||
|
||||
wait_for_condition([&]() {
|
||||
bool should_exit = true;
|
||||
for (Ref<StreamPeerUDS> &c : clients) {
|
||||
if (c->poll() != Error::OK) {
|
||||
return true;
|
||||
}
|
||||
StreamPeerUDS::Status status = c->get_status();
|
||||
if (status != StreamPeerUDS::STATUS_CONNECTED && status != StreamPeerUDS::STATUS_CONNECTING) {
|
||||
return true;
|
||||
}
|
||||
if (status != StreamPeerUDS::STATUS_CONNECTED) {
|
||||
should_exit = false;
|
||||
}
|
||||
}
|
||||
return should_exit;
|
||||
});
|
||||
|
||||
for (Ref<StreamPeerUDS> &c : clients) {
|
||||
REQUIRE_EQ(c->get_status(), StreamPeerUDS::STATUS_CONNECTED);
|
||||
}
|
||||
|
||||
// Sending data from each client to server.
|
||||
for (int i = 0; i < clients.size(); i++) {
|
||||
String hello_client = "Hello " + itos(i);
|
||||
clients[i]->put_string(hello_client);
|
||||
CHECK_EQ(clients_from_server[i]->get_string(), hello_client);
|
||||
}
|
||||
|
||||
for (Ref<StreamPeerUDS> &c : clients) {
|
||||
c->disconnect_from_host();
|
||||
}
|
||||
server->stop();
|
||||
|
||||
cleanup_socket_file();
|
||||
}
|
||||
|
||||
TEST_CASE("[UDSServer] When stopped shouldn't accept new connections") {
|
||||
Ref<UDSServer> server = create_server(SOCKET_PATH);
|
||||
Ref<StreamPeerUDS> client = create_client(SOCKET_PATH);
|
||||
Ref<StreamPeerUDS> client_from_server = accept_connection(server);
|
||||
|
||||
wait_for_condition([&]() {
|
||||
return client->poll() != Error::OK || client->get_status() == StreamPeerUDS::STATUS_CONNECTED;
|
||||
});
|
||||
|
||||
CHECK_EQ(client->get_status(), StreamPeerUDS::STATUS_CONNECTED);
|
||||
|
||||
// Sending data from client to server.
|
||||
const String hello_world = "Hello World!";
|
||||
client->put_string(hello_world);
|
||||
CHECK_EQ(client_from_server->get_string(), hello_world);
|
||||
|
||||
client->disconnect_from_host();
|
||||
server->stop();
|
||||
CHECK_FALSE(server->is_listening());
|
||||
|
||||
// Clean up the socket file after server stops
|
||||
cleanup_socket_file();
|
||||
|
||||
// Try to connect to non-existent socket
|
||||
Ref<StreamPeerUDS> new_client;
|
||||
new_client.instantiate();
|
||||
Error err = new_client->connect_to_host(SOCKET_PATH);
|
||||
|
||||
// Connection should fail since socket doesn't exist
|
||||
CHECK_NE(err, Error::OK);
|
||||
CHECK_FALSE(server->is_connection_available());
|
||||
|
||||
cleanup_socket_file();
|
||||
}
|
||||
|
||||
TEST_CASE("[UDSServer] Should disconnect client") {
|
||||
Ref<UDSServer> server = create_server(SOCKET_PATH);
|
||||
Ref<StreamPeerUDS> client = create_client(SOCKET_PATH);
|
||||
Ref<StreamPeerUDS> client_from_server = accept_connection(server);
|
||||
|
||||
wait_for_condition([&]() {
|
||||
return client->poll() != Error::OK || client->get_status() == StreamPeerUDS::STATUS_CONNECTED;
|
||||
});
|
||||
|
||||
CHECK_EQ(client->get_status(), StreamPeerUDS::STATUS_CONNECTED);
|
||||
|
||||
// Sending data from client to server.
|
||||
const String hello_world = "Hello World!";
|
||||
client->put_string(hello_world);
|
||||
CHECK_EQ(client_from_server->get_string(), hello_world);
|
||||
|
||||
client_from_server->disconnect_from_host();
|
||||
server->stop();
|
||||
CHECK_FALSE(server->is_listening());
|
||||
|
||||
// Wait for disconnection
|
||||
wait_for_condition([&]() {
|
||||
return client->poll() != Error::OK || client->get_status() == StreamPeerUDS::STATUS_NONE;
|
||||
});
|
||||
|
||||
// Wait for disconnection
|
||||
wait_for_condition([&]() {
|
||||
return client_from_server->poll() != Error::OK || client_from_server->get_status() == StreamPeerUDS::STATUS_NONE;
|
||||
});
|
||||
|
||||
CHECK_EQ(client->get_status(), StreamPeerUDS::STATUS_NONE);
|
||||
CHECK_EQ(client_from_server->get_status(), StreamPeerUDS::STATUS_NONE);
|
||||
|
||||
ERR_PRINT_OFF;
|
||||
CHECK_EQ(client->get_string(), String());
|
||||
CHECK_EQ(client_from_server->get_string(), String());
|
||||
ERR_PRINT_ON;
|
||||
|
||||
cleanup_socket_file();
|
||||
}
|
||||
|
||||
TEST_CASE("[UDSServer] Test with different socket paths") {
|
||||
// Test with a different socket path
|
||||
const String alt_socket_path = "/tmp/godot_test_uds_socket_alt";
|
||||
|
||||
// Clean up before test
|
||||
if (FileAccess::exists(alt_socket_path)) {
|
||||
DirAccess::remove_absolute(alt_socket_path);
|
||||
}
|
||||
|
||||
Ref<UDSServer> server = create_server(alt_socket_path);
|
||||
Ref<StreamPeerUDS> client = create_client(alt_socket_path);
|
||||
Ref<StreamPeerUDS> client_from_server = accept_connection(server);
|
||||
|
||||
wait_for_condition([&]() {
|
||||
return client->poll() != Error::OK || client->get_status() == StreamPeerUDS::STATUS_CONNECTED;
|
||||
});
|
||||
|
||||
CHECK_EQ(client->get_status(), StreamPeerUDS::STATUS_CONNECTED);
|
||||
|
||||
// Test data exchange
|
||||
const int test_number = 42;
|
||||
client->put_32(test_number);
|
||||
CHECK_EQ(client_from_server->get_32(), test_number);
|
||||
|
||||
client->disconnect_from_host();
|
||||
server->stop();
|
||||
|
||||
// Clean up
|
||||
if (FileAccess::exists(alt_socket_path)) {
|
||||
DirAccess::remove_absolute(alt_socket_path);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace TestUDSServer
|
@@ -62,6 +62,7 @@
|
||||
#include "tests/core/io/test_stream_peer_gzip.h"
|
||||
#include "tests/core/io/test_tcp_server.h"
|
||||
#include "tests/core/io/test_udp_server.h"
|
||||
#include "tests/core/io/test_uds_server.h"
|
||||
#include "tests/core/io/test_xml_parser.h"
|
||||
#include "tests/core/math/test_aabb.h"
|
||||
#include "tests/core/math/test_astar.h"
|
||||
|
10
thirdparty/enet/enet_godot.cpp
vendored
10
thirdparty/enet/enet_godot.cpp
vendored
@@ -74,7 +74,7 @@ public:
|
||||
ENetUDP() {
|
||||
sock = Ref<NetSocket>(NetSocket::create());
|
||||
IP::Type ip_type = IP::TYPE_ANY;
|
||||
sock->open(NetSocket::TYPE_UDP, ip_type);
|
||||
sock->open(NetSocket::Family::INET, NetSocket::TYPE_UDP, ip_type);
|
||||
}
|
||||
|
||||
~ENetUDP() {
|
||||
@@ -88,11 +88,15 @@ public:
|
||||
Error bind(IPAddress p_ip, uint16_t p_port) {
|
||||
local_address = p_ip;
|
||||
bound = true;
|
||||
return sock->bind(p_ip, p_port);
|
||||
NetSocket::Address addr(p_ip, p_port);
|
||||
return sock->bind(addr);
|
||||
}
|
||||
|
||||
Error get_socket_address(IPAddress *r_ip, uint16_t *r_port) {
|
||||
Error err = sock->get_socket_address(r_ip, r_port);
|
||||
NetSocket::Address addr;
|
||||
Error err = sock->get_socket_address(&addr);
|
||||
*r_ip = addr.ip();
|
||||
*r_port = addr.port();
|
||||
if (bound) {
|
||||
*r_ip = local_address;
|
||||
}
|
||||
|
Reference in New Issue
Block a user