mirror of
https://github.com/godotengine/godot.git
synced 2025-12-08 06:09:55 +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:
parent
a078895ad2
commit
7227fdd805
39 changed files with 1791 additions and 529 deletions
|
|
@ -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(); }
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue