diff options
author | Fabio Alessandrelli <fabio.alessandrelli@gmail.com> | 2018-09-02 06:36:45 +0200 |
---|---|---|
committer | Fabio Alessandrelli <fabio.alessandrelli@gmail.com> | 2018-09-12 15:56:20 +0200 |
commit | 30327872e0d7989220a28a91a39d847dacca406d (patch) | |
tree | e922ff8448dd000d7670ad45dee1a68362d26555 /core/io | |
parent | 1b99806b478b823ff04ad0e2538e211e55c037cb (diff) |
Unify StreamPeerTCP/TCP_Server with NetSocket API
Diffstat (limited to 'core/io')
-rw-r--r-- | core/io/file_access_network.cpp | 2 | ||||
-rw-r--r-- | core/io/http_client.cpp | 2 | ||||
-rw-r--r-- | core/io/stream_peer_tcp.cpp | 297 | ||||
-rw-r--r-- | core/io/stream_peer_tcp.h | 38 | ||||
-rw-r--r-- | core/io/tcp_server.cpp | 97 | ||||
-rw-r--r-- | core/io/tcp_server.h | 19 |
6 files changed, 402 insertions, 53 deletions
diff --git a/core/io/file_access_network.cpp b/core/io/file_access_network.cpp index 8bffe3e88b..6b6856dcc8 100644 --- a/core/io/file_access_network.cpp +++ b/core/io/file_access_network.cpp @@ -227,7 +227,7 @@ FileAccessNetworkClient::FileAccessNetworkClient() { quit = false; singleton = this; last_id = 0; - client = Ref<StreamPeerTCP>(StreamPeerTCP::create_ref()); + client.instance(); sem = Semaphore::create(); lockcount = 0; } diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp index bfa2d3b389..c47ae05dc2 100644 --- a/core/io/http_client.cpp +++ b/core/io/http_client.cpp @@ -683,7 +683,7 @@ void HTTPClient::set_read_chunk_size(int p_size) { HTTPClient::HTTPClient() { - tcp_connection = StreamPeerTCP::create_ref(); + tcp_connection.instance(); resolving = IP::RESOLVER_INVALID_ID; status = STATUS_DISCONNECTED; conn_port = -1; diff --git a/core/io/stream_peer_tcp.cpp b/core/io/stream_peer_tcp.cpp index 54ebb3ae0d..484e160f0e 100644 --- a/core/io/stream_peer_tcp.cpp +++ b/core/io/stream_peer_tcp.cpp @@ -30,7 +30,280 @@ #include "stream_peer_tcp.h" -StreamPeerTCP *(*StreamPeerTCP::_create)() = NULL; +Error StreamPeerTCP::_poll_connection() { + + ERR_FAIL_COND_V(status != STATUS_CONNECTING || !_sock.is_valid() || !_sock->is_open(), FAILED); + + Error err = _sock->connect_to_host(peer_host, peer_port); + + if (err == OK) { + status = STATUS_CONNECTED; + return OK; + } else if (err == ERR_BUSY) { + // Still trying to connect + return OK; + } + + status = STATUS_ERROR; + return ERR_CONNECTION_ERROR; +} + +void StreamPeerTCP::accept_socket(Ref<NetSocket> p_sock, IP_Address p_host, uint16_t p_port) { + + _sock = p_sock; + _sock->set_blocking_enabled(false); + + status = STATUS_CONNECTING; + + peer_host = p_host; + peer_port = p_port; +} + +Error StreamPeerTCP::connect_to_host(const IP_Address &p_host, uint16_t p_port) { + + ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); + ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE); + ERR_FAIL_COND_V(!p_host.is_valid(), ERR_INVALID_PARAMETER); + + Error err; + IP::Type ip_type = p_host.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; + + err = _sock->open(NetSocket::TYPE_TCP, ip_type); + ERR_FAIL_COND_V(err != OK, FAILED); + + _sock->set_blocking_enabled(false); + + err = _sock->connect_to_host(p_host, p_port); + + if (err != OK) { + if (err == ERR_BUSY) { + status = STATUS_CONNECTING; + } else { + ERR_PRINT("Connection to remote host failed!"); + disconnect_from_host(); + return FAILED; + } + } + + status = STATUS_CONNECTED; + 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_valid(), ERR_UNAVAILABLE); + + if (status == STATUS_NONE || status == STATUS_ERROR) { + + return FAILED; + } + + if (status != STATUS_CONNECTED) { + + if (_poll_connection() != OK) { + + return FAILED; + } + + if (status != STATUS_CONNECTED) { + r_sent = 0; + return OK; + } + } + + if (!_sock->is_open()) + 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 (!is_connected_to_host()) { + + return FAILED; + }; + + if (status == STATUS_CONNECTING) { + + if (_poll_connection() != OK) { + + return FAILED; + }; + + if (status != STATUS_CONNECTED) { + r_received = 0; + return OK; + }; + }; + + 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) { + + _sock->close(); + status = STATUS_NONE; + peer_port = 0; + peer_host = IP_Address(); + r_received = total_read; + return ERR_FILE_EOF; + + } else { + + to_read -= read; + total_read += read; + } + } + + r_received = total_read; + + return OK; +} + +void StreamPeerTCP::set_no_delay(bool p_enabled) { + + ERR_FAIL_COND(!is_connected_to_host()); + _sock->set_tcp_no_delay_enabled(p_enabled); +} + +bool StreamPeerTCP::is_connected_to_host() const { + + if (status == STATUS_NONE || status == STATUS_ERROR) { + + return false; + } + + if (status != STATUS_CONNECTED) { + return true; + } + + return _sock.is_valid() && _sock->is_open(); +} + +StreamPeerTCP::Status StreamPeerTCP::get_status() { + + if (status == STATUS_CONNECTING) { + _poll_connection(); + } + + return status; +} + +void StreamPeerTCP::disconnect_from_host() { + + if (_sock.is_valid() && _sock->is_open()) + _sock->close(); + + status = STATUS_NONE; + peer_host = IP_Address(); + peer_port = 0; +} + +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_valid(), -1); + return _sock->get_available_bytes(); +} + +IP_Address StreamPeerTCP::get_connected_host() const { + + return peer_host; +} + +uint16_t StreamPeerTCP::get_connected_port() const { + + return peer_port; +} Error StreamPeerTCP::_connect(const String &p_address, int p_port) { @@ -62,23 +335,15 @@ void StreamPeerTCP::_bind_methods() { BIND_ENUM_CONSTANT(STATUS_ERROR); } -Ref<StreamPeerTCP> StreamPeerTCP::create_ref() { +StreamPeerTCP::StreamPeerTCP() { - if (!_create) - return Ref<StreamPeerTCP>(); - return Ref<StreamPeerTCP>(_create()); + _sock = Ref<NetSocket>(NetSocket::create()); + status = STATUS_NONE; + peer_host = IP_Address(); + peer_port = 0; } -StreamPeerTCP *StreamPeerTCP::create() { +StreamPeerTCP::~StreamPeerTCP() { - if (!_create) - return NULL; - return _create(); + disconnect_from_host(); } - -StreamPeerTCP::StreamPeerTCP() { -} - -StreamPeerTCP::~StreamPeerTCP(){ - -}; diff --git a/core/io/stream_peer_tcp.h b/core/io/stream_peer_tcp.h index dcda3b5bd7..de364915cd 100644 --- a/core/io/stream_peer_tcp.h +++ b/core/io/stream_peer_tcp.h @@ -33,6 +33,7 @@ #include "core/io/ip.h" #include "core/io/ip_address.h" +#include "core/io/net_socket.h" #include "core/io/stream_peer.h" class StreamPeerTCP : public StreamPeer { @@ -50,24 +51,37 @@ public: }; protected: - virtual Error _connect(const String &p_address, int p_port); - static StreamPeerTCP *(*_create)(); + Ref<NetSocket> _sock; + Status status; + IP_Address peer_host; + uint16_t peer_port; + + Error _connect(const String &p_address, int p_port); + Error _poll_connection(); + 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 Error connect_to_host(const IP_Address &p_host, uint16_t p_port) = 0; + void accept_socket(Ref<NetSocket> p_sock, IP_Address p_host, uint16_t p_port); + + Error connect_to_host(const IP_Address &p_host, uint16_t p_port); + bool is_connected_to_host() const; + IP_Address get_connected_host() const; + uint16_t get_connected_port() const; + void disconnect_from_host(); - //read/write from streampeer + int get_available_bytes() const; + Status get_status(); - virtual bool is_connected_to_host() const = 0; - virtual Status get_status() const = 0; - virtual void disconnect_from_host() = 0; - virtual IP_Address get_connected_host() const = 0; - virtual uint16_t get_connected_port() const = 0; - virtual void set_no_delay(bool p_enabled) = 0; + void set_no_delay(bool p_enabled); - static Ref<StreamPeerTCP> create_ref(); - static StreamPeerTCP *create(); + // Read/Write from StreamPeer + Error put_data(const uint8_t *p_data, int p_bytes); + Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent); + Error get_data(uint8_t *p_buffer, int p_bytes); + Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received); StreamPeerTCP(); ~StreamPeerTCP(); diff --git a/core/io/tcp_server.cpp b/core/io/tcp_server.cpp index 5916d58390..b8194cb17f 100644 --- a/core/io/tcp_server.cpp +++ b/core/io/tcp_server.cpp @@ -30,29 +30,98 @@ #include "tcp_server.h" -TCP_Server *(*TCP_Server::_create)() = NULL; +void TCP_Server::_bind_methods() { + + ClassDB::bind_method(D_METHOD("listen", "port", "bind_address"), &TCP_Server::listen, DEFVAL("*")); + ClassDB::bind_method(D_METHOD("is_connection_available"), &TCP_Server::is_connection_available); + ClassDB::bind_method(D_METHOD("take_connection"), &TCP_Server::take_connection); + ClassDB::bind_method(D_METHOD("stop"), &TCP_Server::stop); +} + +Error TCP_Server::listen(uint16_t p_port, const IP_Address &p_bind_address) { + + ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); + ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE); + ERR_FAIL_COND_V(!p_bind_address.is_valid() && !p_bind_address.is_wildcard(), ERR_INVALID_PARAMETER); + + Error err; + IP::Type ip_type = IP::TYPE_ANY; + + // If the bind address is valid use its type as the socket type + if (p_bind_address.is_valid()) + ip_type = p_bind_address.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; + + err = _sock->open(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; + } -Ref<TCP_Server> TCP_Server::create_ref() { + err = _sock->listen(MAX_PENDING_CONNECTIONS); - if (!_create) - return NULL; - return Ref<TCP_Server>(_create()); + if (err != OK) { + _sock->close(); + return FAILED; + } + return OK; } -TCP_Server *TCP_Server::create() { +bool TCP_Server::is_connection_available() const { - if (!_create) - return NULL; - return _create(); + ERR_FAIL_COND_V(!_sock.is_valid(), false); + + if (!_sock->is_open()) + return false; + + Error err = _sock->poll(NetSocket::POLL_TYPE_IN, 0); + if (err != OK) { + return false; + } + + return true; } -void TCP_Server::_bind_methods() { +Ref<StreamPeerTCP> TCP_Server::take_connection() { - ClassDB::bind_method(D_METHOD("listen", "port", "bind_address"), &TCP_Server::listen, DEFVAL("*")); - ClassDB::bind_method(D_METHOD("is_connection_available"), &TCP_Server::is_connection_available); - ClassDB::bind_method(D_METHOD("take_connection"), &TCP_Server::take_connection); - ClassDB::bind_method(D_METHOD("stop"), &TCP_Server::stop); + Ref<StreamPeerTCP> conn; + if (!is_connection_available()) { + return conn; + } + + Ref<NetSocket> ns; + IP_Address ip; + uint16_t port = 0; + ns = _sock->accept(ip, port); + if (!ns.is_valid()) + return conn; + + conn = Ref<StreamPeerTCP>(memnew(StreamPeerTCP)); + conn->accept_socket(ns, ip, port); + return conn; +} + +void TCP_Server::stop() { + + if (_sock.is_valid()) { + _sock->close(); + } } TCP_Server::TCP_Server() { + + _sock = Ref<NetSocket>(NetSocket::create()); +} + +TCP_Server::~TCP_Server() { + + stop(); } diff --git a/core/io/tcp_server.h b/core/io/tcp_server.h index 7353390bef..4c89197d9a 100644 --- a/core/io/tcp_server.h +++ b/core/io/tcp_server.h @@ -32,6 +32,7 @@ #define TCP_SERVER_H #include "core/io/ip.h" +#include "core/io/net_socket.h" #include "core/io/stream_peer.h" #include "core/io/stream_peer_tcp.h" @@ -40,22 +41,22 @@ class TCP_Server : public Reference { GDCLASS(TCP_Server, Reference); protected: - static TCP_Server *(*_create)(); + enum { + MAX_PENDING_CONNECTIONS = 8 + }; - //bind helper + Ref<NetSocket> _sock; static void _bind_methods(); public: - virtual Error listen(uint16_t p_port, const IP_Address &p_bind_address = IP_Address("*")) = 0; - virtual bool is_connection_available() const = 0; - virtual Ref<StreamPeerTCP> take_connection() = 0; + Error listen(uint16_t p_port, const IP_Address &p_bind_address = IP_Address("*")); + bool is_connection_available() const; + Ref<StreamPeerTCP> take_connection(); - virtual void stop() = 0; //stop listening - - static Ref<TCP_Server> create_ref(); - static TCP_Server *create(); + void stop(); // Stop listening TCP_Server(); + ~TCP_Server(); }; #endif // TCP_SERVER_H |