From b4e3be7519d576cd341da5c8b36cd44ab4e13c45 Mon Sep 17 00:00:00 2001 From: Fabio Alessandrelli Date: Sat, 1 Sep 2018 20:15:42 +0200 Subject: New NetSocket interface, BSD/Win implementation Unified BSD and Winsock sockets into a single implementation of a generic NetSocket interface. This is some ground work for few network improvements: - Reuse as much code as possible between Posix and Windows. - Provide a single point of implementation for exotic sdks (consoles). - Provide platform agnostic StreamPeerTCP and PacketPeerUDP in core. - Implement connect for UDP allowing for DTLS implementation. --- core/io/net_socket.cpp | 42 +++++++++++++++++++++++++++ core/io/net_socket.h | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 core/io/net_socket.cpp create mode 100644 core/io/net_socket.h (limited to 'core') diff --git a/core/io/net_socket.cpp b/core/io/net_socket.cpp new file mode 100644 index 0000000000..10bcf62eda --- /dev/null +++ b/core/io/net_socket.cpp @@ -0,0 +1,42 @@ +/*************************************************************************/ +/* net_socket.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "net_socket.h" + +NetSocket *(*NetSocket::_create)() = NULL; + +NetSocket *NetSocket::create() { + + if (_create) + return _create(); + + ERR_PRINT("Unable to create network socket, platform not supported"); + return NULL; +} diff --git a/core/io/net_socket.h b/core/io/net_socket.h new file mode 100644 index 0000000000..0665bec9fd --- /dev/null +++ b/core/io/net_socket.h @@ -0,0 +1,79 @@ +/*************************************************************************/ +/* net_socket.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 NET_SOCKET_H +#define NET_SOCKET_H + +#include "core/io/ip.h" +#include "core/reference.h" + +class NetSocket : public Reference { + +protected: + static NetSocket *(*_create)(); + +public: + static NetSocket *create(); + + enum PollType { + POLL_TYPE_IN, + POLL_TYPE_OUT, + POLL_TYPE_IN_OUT + }; + + enum Type { + TYPE_NONE, + TYPE_TCP, + TYPE_UDP, + }; + + virtual Error open(Type p_type, IP::Type &ip_type) = 0; + virtual void close() = 0; + virtual Error bind(IP_Address p_addr, uint16_t p_port) = 0; + virtual Error listen(int p_max_pending) = 0; + virtual Error connect_to_host(IP_Address p_addr, uint16_t p_port) = 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, IP_Address &r_ip, uint16_t &r_port) = 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, IP_Address p_ip, uint16_t p_port) = 0; + virtual Ref accept(IP_Address &r_ip, uint16_t &r_port) = 0; + + virtual bool is_open() const = 0; + virtual int get_available_bytes() const = 0; + + virtual void set_broadcasting_enabled(bool p_enabled) = 0; + virtual void set_blocking_enabled(bool p_enabled) = 0; + virtual void set_ipv6_only_enabled(bool p_enabled) = 0; + virtual void set_tcp_no_delay_enabled(bool p_enabled) = 0; + virtual void set_reuse_address_enabled(bool p_enabled) = 0; +}; + +#endif // NET_SOCKET_H -- cgit v1.2.3 From 1b99806b478b823ff04ad0e2538e211e55c037cb Mon Sep 17 00:00:00 2001 From: Fabio Alessandrelli Date: Sun, 2 Sep 2018 05:32:12 +0200 Subject: Unify PacketPeerUDP using NetSocket --- core/io/packet_peer_udp.cpp | 196 +++++++++++++++++++++++++++++++++++++++---- core/io/packet_peer_udp.h | 38 ++++++--- core/register_core_types.cpp | 2 +- 3 files changed, 209 insertions(+), 27 deletions(-) (limited to 'core') diff --git a/core/io/packet_peer_udp.cpp b/core/io/packet_peer_udp.cpp index ef4fdd689c..d33ba6f855 100644 --- a/core/io/packet_peer_udp.cpp +++ b/core/io/packet_peer_udp.cpp @@ -32,8 +32,6 @@ #include "core/io/ip.h" -PacketPeerUDP *(*PacketPeerUDP::_create)() = NULL; - void PacketPeerUDP::set_blocking_mode(bool p_enable) { blocking = p_enable; @@ -59,6 +57,177 @@ Error PacketPeerUDP::_set_dest_address(const String &p_address, int p_port) { return OK; } +int PacketPeerUDP::get_available_packet_count() const { + + // TODO we should deprecate this, and expose poll instead! + Error err = const_cast(this)->_poll(); + if (err != OK) + return -1; + + return queue_count; +} + +Error PacketPeerUDP::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { + + Error err = _poll(); + if (err != OK) + return err; + if (queue_count == 0) + return ERR_UNAVAILABLE; + + uint32_t size = 0; + uint8_t ipv6[16]; + rb.read(ipv6, 16, true); + packet_ip.set_ipv6(ipv6); + rb.read((uint8_t *)&packet_port, 4, true); + rb.read((uint8_t *)&size, 4, true); + rb.read(packet_buffer, size, true); + --queue_count; + *r_buffer = packet_buffer; + r_buffer_size = size; + return OK; +} + +Error PacketPeerUDP::put_packet(const uint8_t *p_buffer, int p_buffer_size) { + + ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); + ERR_FAIL_COND_V(!peer_addr.is_valid(), ERR_UNCONFIGURED); + + Error err; + int sent = -1; + + 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_FAIL_COND_V(err != OK, err); + _sock->set_blocking_enabled(false); + } + + do { + err = _sock->sendto(p_buffer, p_buffer_size, sent, peer_addr, peer_port); + if (err != OK) { + if (err != ERR_BUSY) + return FAILED; + else if (!blocking) + return ERR_BUSY; + // Keep trying to send full packet + continue; + } + return OK; + + } while (sent != p_buffer_size); + + return OK; +} + +int PacketPeerUDP::get_max_packet_size() const { + + return 512; // uhm maybe not +} + +Error PacketPeerUDP::listen(int p_port, const IP_Address &p_bind_address, int p_recv_buffer_size) { + + 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 (p_bind_address.is_valid()) + ip_type = p_bind_address.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; + + err = _sock->open(NetSocket::TYPE_UDP, ip_type); + + if (err != OK) + return 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; + } + rb.resize(nearest_shift(p_recv_buffer_size)); + return OK; +} + +void PacketPeerUDP::close() { + + if (_sock.is_valid()) + _sock->close(); + rb.resize(16); + queue_count = 0; +} + +Error PacketPeerUDP::wait() { + + ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); + return _sock->poll(NetSocket::POLL_TYPE_IN, -1); +} + +Error PacketPeerUDP::_poll() { + + ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); + + if (!_sock->is_open()) { + return FAILED; + } + + Error err; + int read; + IP_Address ip; + uint16_t port; + + while (true) { + err = _sock->recvfrom(recv_buffer, sizeof(recv_buffer), read, ip, port); + + if (err != OK) { + if (err == ERR_BUSY) + break; + return FAILED; + } + + if (rb.space_left() < read + 24) { +#ifdef TOOLS_ENABLED + WARN_PRINTS("Buffer full, dropping packets!"); +#endif + continue; + } + + uint32_t port32 = port; + rb.write(ip.get_ipv6(), 16); + rb.write((uint8_t *)&port32, 4); + rb.write((uint8_t *)&read, 4); + rb.write(recv_buffer, read); + ++queue_count; + } + + return OK; +} +bool PacketPeerUDP::is_listening() const { + + return _sock.is_valid() && _sock->is_open(); +} + +IP_Address PacketPeerUDP::get_packet_address() const { + + return packet_ip; +} + +int PacketPeerUDP::get_packet_port() const { + + return packet_port; +} + +void PacketPeerUDP::set_dest_address(const IP_Address &p_address, int p_port) { + + peer_addr = p_address; + peer_port = p_port; +} + void PacketPeerUDP::_bind_methods() { ClassDB::bind_method(D_METHOD("listen", "port", "bind_address", "recv_buf_size"), &PacketPeerUDP::listen, DEFVAL("*"), DEFVAL(65536)); @@ -66,26 +235,21 @@ void PacketPeerUDP::_bind_methods() { ClassDB::bind_method(D_METHOD("wait"), &PacketPeerUDP::wait); ClassDB::bind_method(D_METHOD("is_listening"), &PacketPeerUDP::is_listening); ClassDB::bind_method(D_METHOD("get_packet_ip"), &PacketPeerUDP::_get_packet_ip); - //ClassDB::bind_method(D_METHOD("get_packet_address"),&PacketPeerUDP::_get_packet_address); ClassDB::bind_method(D_METHOD("get_packet_port"), &PacketPeerUDP::get_packet_port); ClassDB::bind_method(D_METHOD("set_dest_address", "host", "port"), &PacketPeerUDP::_set_dest_address); } -Ref PacketPeerUDP::create_ref() { - - if (!_create) - return Ref(); - return Ref(_create()); -} - -PacketPeerUDP *PacketPeerUDP::create() { +PacketPeerUDP::PacketPeerUDP() { - if (!_create) - return NULL; - return _create(); + _sock = Ref(NetSocket::create()); + blocking = true; + packet_port = 0; + queue_count = 0; + peer_port = 0; + rb.resize(16); } -PacketPeerUDP::PacketPeerUDP() { +PacketPeerUDP::~PacketPeerUDP() { - blocking = true; + close(); } diff --git a/core/io/packet_peer_udp.h b/core/io/packet_peer_udp.h index 2ed53cef7f..4366b0eb82 100644 --- a/core/io/packet_peer_udp.h +++ b/core/io/packet_peer_udp.h @@ -32,36 +32,54 @@ #define PACKET_PEER_UDP_H #include "core/io/ip.h" +#include "core/io/net_socket.h" #include "core/io/packet_peer.h" class PacketPeerUDP : public PacketPeer { GDCLASS(PacketPeerUDP, PacketPeer); protected: + enum { + PACKET_BUFFER_SIZE = 65536 + }; + + RingBuffer rb; + uint8_t recv_buffer[PACKET_BUFFER_SIZE]; + uint8_t packet_buffer[PACKET_BUFFER_SIZE]; + IP_Address packet_ip; + int packet_port; + int queue_count; + + IP_Address peer_addr; + int peer_port; bool blocking; + Ref _sock; - static PacketPeerUDP *(*_create)(); static void _bind_methods(); String _get_packet_ip() const; Error _set_dest_address(const String &p_address, int p_port); + Error _poll(); public: void set_blocking_mode(bool p_enable); - virtual Error listen(int p_port, const IP_Address &p_bind_address = IP_Address("*"), int p_recv_buffer_size = 65536) = 0; - virtual void close() = 0; - virtual Error wait() = 0; - virtual bool is_listening() const = 0; - virtual IP_Address get_packet_address() const = 0; - virtual int get_packet_port() const = 0; - virtual void set_dest_address(const IP_Address &p_address, int p_port) = 0; + Error listen(int p_port, const IP_Address &p_bind_address = IP_Address("*"), int p_recv_buffer_size = 65536); + void close(); + Error wait(); + bool is_listening() const; + IP_Address get_packet_address() const; + int get_packet_port() const; + void set_dest_address(const IP_Address &p_address, int p_port); - static Ref create_ref(); - static PacketPeerUDP *create(); + Error put_packet(const uint8_t *p_buffer, int p_buffer_size); + Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); + int get_available_packet_count() const; + int get_max_packet_size() const; PacketPeerUDP(); + ~PacketPeerUDP(); }; #endif // PACKET_PEER_UDP_H diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index d93cad3f94..dcf7ee1de6 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -148,7 +148,7 @@ void register_core_types() { ClassDB::register_class(); ClassDB::register_custom_instance_class(); ClassDB::register_custom_instance_class(); - ClassDB::register_custom_instance_class(); + ClassDB::register_class(); ClassDB::register_custom_instance_class(); ClassDB::register_virtual_class(); ClassDB::register_virtual_class(); -- cgit v1.2.3 From 30327872e0d7989220a28a91a39d847dacca406d Mon Sep 17 00:00:00 2001 From: Fabio Alessandrelli Date: Sun, 2 Sep 2018 06:36:45 +0200 Subject: Unify StreamPeerTCP/TCP_Server with NetSocket API --- core/io/file_access_network.cpp | 2 +- core/io/http_client.cpp | 2 +- core/io/stream_peer_tcp.cpp | 297 +++++++++++++++++++++++++++++++++++++--- core/io/stream_peer_tcp.h | 38 +++-- core/io/tcp_server.cpp | 97 +++++++++++-- core/io/tcp_server.h | 19 +-- core/register_core_types.cpp | 4 +- core/script_debugger_remote.cpp | 2 +- 8 files changed, 405 insertions(+), 56 deletions(-) (limited to 'core') 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::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 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::create_ref() { +StreamPeerTCP::StreamPeerTCP() { - if (!_create) - return Ref(); - return Ref(_create()); + _sock = Ref(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 _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 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 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::create_ref() { + err = _sock->listen(MAX_PENDING_CONNECTIONS); - if (!_create) - return NULL; - return Ref(_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 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 conn; + if (!is_connection_available()) { + return conn; + } + + Ref ns; + IP_Address ip; + uint16_t port = 0; + ns = _sock->accept(ip, port); + if (!ns.is_valid()) + return conn; + + conn = Ref(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::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 _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 take_connection() = 0; + Error listen(uint16_t p_port, const IP_Address &p_bind_address = IP_Address("*")); + bool is_connection_available() const; + Ref take_connection(); - virtual void stop() = 0; //stop listening - - static Ref create_ref(); - static TCP_Server *create(); + void stop(); // Stop listening TCP_Server(); + ~TCP_Server(); }; #endif // TCP_SERVER_H diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index dcf7ee1de6..b0023b4c26 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -146,8 +146,8 @@ void register_core_types() { ClassDB::register_class(); ClassDB::register_virtual_class(); ClassDB::register_class(); - ClassDB::register_custom_instance_class(); - ClassDB::register_custom_instance_class(); + ClassDB::register_class(); + ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_custom_instance_class(); ClassDB::register_virtual_class(); diff --git a/core/script_debugger_remote.cpp b/core/script_debugger_remote.cpp index ac684af6fe..0519807e63 100644 --- a/core/script_debugger_remote.cpp +++ b/core/script_debugger_remote.cpp @@ -1084,7 +1084,7 @@ ScriptDebuggerRemote::ScriptDebuggerRemote() : max_frame_functions(16), skip_profile_frame(false), reload_all_scripts(false), - tcp_client(StreamPeerTCP::create_ref()), + tcp_client(Ref(memnew(StreamPeerTCP))), packet_peer_stream(Ref(memnew(PacketPeerStream))), last_perf_time(0), performance(Engine::get_singleton()->get_singleton_object("Performance")), -- cgit v1.2.3