axmol/thirdparty/yasio/yasio.hpp

1273 lines
40 KiB
C++
Raw Normal View History

2020-11-16 14:47:43 +08:00
//////////////////////////////////////////////////////////////////////////////////////////
2021-01-14 15:30:18 +08:00
// A multi-platform support c++11 library with focus on asynchronous socket I/O for any
// client application.
2020-11-16 14:47:43 +08:00
//////////////////////////////////////////////////////////////////////////////////////////
/*
The MIT License (MIT)
2023-01-01 17:36:38 +08:00
Copyright (c) 2012-2023 HALX99
2020-11-16 14:47:43 +08:00
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 YASIO__CORE_HPP
#define YASIO__CORE_HPP
#include <algorithm>
#include <atomic>
#include <condition_variable>
#include <mutex>
#include <thread>
#include <vector>
#include <chrono>
#include <functional>
#if defined(_WIN32)
# include <map>
#endif
#include "yasio/detail/sz.hpp"
#include "yasio/detail/config.hpp"
#include "yasio/detail/object_pool.hpp"
#include "yasio/detail/singleton.hpp"
#include "yasio/detail/select_interrupter.hpp"
#include "yasio/detail/concurrent_queue.hpp"
#include "yasio/detail/utils.hpp"
#include "yasio/detail/errc.hpp"
#include "yasio/detail/byte_buffer.hpp"
2023-01-01 17:36:38 +08:00
#include "yasio/detail/fd_set_adapter.hpp"
2023-01-04 23:20:07 +08:00
#include "yasio/stl/memory.hpp"
#include "yasio/stl/string_view.hpp"
2020-11-16 14:47:43 +08:00
#include "yasio/xxsocket.hpp"
#if !defined(YASIO_HAVE_CARES)
2023-01-04 23:20:07 +08:00
# include "yasio/stl/shared_mutex.hpp"
2020-11-16 14:47:43 +08:00
#endif
#if defined(YASIO_HAVE_KCP)
typedef struct IKCPCB ikcpcb;
#endif
2021-01-14 15:30:18 +08:00
#if defined(YASIO_SSL_BACKEND)
2020-11-16 14:47:43 +08:00
typedef struct ssl_ctx_st SSL_CTX;
typedef struct ssl_st SSL;
#endif
#if defined(YASIO_HAVE_CARES)
typedef struct ares_channeldata* ares_channel;
typedef struct ares_addrinfo ares_addrinfo;
#endif
#define yasio__find(cont, v) ::std::find(cont.begin(), cont.end(), v)
#define yasio__find_if(cont, pred) ::std::find_if(cont.begin(), cont.end(), pred)
namespace yasio
{
2021-06-14 22:54:50 +08:00
YASIO__NS_INLINE
2020-11-16 14:47:43 +08:00
namespace inet
{
// options
enum
{ // lgtm [cpp/irregular-enum-init]
2020-12-21 18:12:13 +08:00
// Set whether deferred dispatch event.
2020-11-16 14:47:43 +08:00
// params: deferred_event:int(1)
YOPT_S_DEFERRED_EVENT = 1,
2020-12-15 18:58:04 +08:00
// Set custom resolve function, native C++ ONLY.
2020-11-16 14:47:43 +08:00
// params: func:resolv_fn_t*
2020-12-15 18:58:04 +08:00
// remarks: you must ensure thread safe of it.
2020-11-16 14:47:43 +08:00
YOPT_S_RESOLV_FN,
2020-12-15 18:58:04 +08:00
// Set custom print function, native C++ ONLY.
2020-11-16 14:47:43 +08:00
// parmas: func:print_fn_t
2020-12-15 18:58:04 +08:00
// remarks: you must ensure thread safe of it.
2020-11-16 14:47:43 +08:00
YOPT_S_PRINT_FN,
2020-12-15 18:58:04 +08:00
// Set custom print function, native C++ ONLY.
2020-11-16 14:47:43 +08:00
// parmas: func:print_fn2_t
2020-12-15 18:58:04 +08:00
// remarks: you must ensure thread safe of it.
2020-11-16 14:47:43 +08:00
YOPT_S_PRINT_FN2,
2020-12-15 18:58:04 +08:00
// Set event callback
2020-11-16 14:47:43 +08:00
// params: func:event_cb_t*
2020-12-21 18:12:13 +08:00
// remarks: this callback will be invoke at io_service::dispatch caller thread
2020-11-16 14:47:43 +08:00
YOPT_S_EVENT_CB,
2020-12-21 18:12:13 +08:00
// Sets callback before defer dispatch event.
// params: func:defer_event_cb_t*
// remarks: this callback invoke at io_service thread
YOPT_S_DEFER_EVENT_CB,
2020-11-16 14:47:43 +08:00
// Set tcp keepalive in seconds, probes is tries.
// params: idle:int(7200), interal:int(75), probes:int(10)
YOPT_S_TCP_KEEPALIVE,
// Don't start a new thread to run event loop
2020-12-15 18:58:04 +08:00
// params: value:int(0)
2020-11-16 14:47:43 +08:00
YOPT_S_NO_NEW_THREAD,
// Sets ssl verification cert, if empty, don't verify
2020-12-15 18:58:04 +08:00
// params: path:const char*
2020-11-16 14:47:43 +08:00
YOPT_S_SSL_CACERT,
// Set connect timeout in seconds
// params: connect_timeout:int(10)
YOPT_S_CONNECT_TIMEOUT,
2021-07-07 13:23:39 +08:00
// Set connect timeout in milliseconds
// params: connect_timeout : int(10000),
YOPT_S_CONNECT_TIMEOUTMS,
2020-11-16 14:47:43 +08:00
// Set dns cache timeout in seconds
// params: dns_cache_timeout : int(600),
YOPT_S_DNS_CACHE_TIMEOUT,
2021-07-07 13:23:39 +08:00
// Set dns cache timeout in milliseconds
// params: dns_cache_timeout : int(600000),
YOPT_S_DNS_CACHE_TIMEOUTMS,
// Set dns queries timeout in seconds, default is: 5
// params: dns_queries_timeout : int(5)
2020-12-15 18:58:04 +08:00
// remarks:
2020-11-16 14:47:43 +08:00
// a. this option must be set before 'io_service::start'
// b. only works when have c-ares
2021-07-07 13:23:39 +08:00
// c. the timeout algorithm of c-ares is complicated, usually, by default, dns queries
2020-11-16 14:47:43 +08:00
// will failed with timeout after more than 75 seconds.
2021-07-07 13:23:39 +08:00
// d. for more detail, please see:
2020-11-16 14:47:43 +08:00
// https://c-ares.haxx.se/ares_init_options.html
YOPT_S_DNS_QUERIES_TIMEOUT,
2021-07-07 13:23:39 +08:00
// Set dns queries timeout in milliseconds, default is: 5000
// see also: YOPT_S_DNS_QUERIES_TIMEOUT
2020-11-16 14:47:43 +08:00
YOPT_S_DNS_QUERIES_TIMEOUTMS,
// Set dns queries tries when timeout reached, default is: 4
// params: dns_queries_tries : int(4)
2020-12-15 18:58:04 +08:00
// remarks:
2020-11-16 14:47:43 +08:00
// a. this option must be set before 'io_service::start'
// b. relative option: YOPT_S_DNS_QUERIES_TIMEOUT
YOPT_S_DNS_QUERIES_TRIES,
// Set dns server dirty
// params: reserved : int(1)
// remarks: you should set this option after your device network changed
2020-11-16 14:47:43 +08:00
YOPT_S_DNS_DIRTY,
2021-10-23 21:51:50 +08:00
// Set custom dns servers
2021-10-25 15:21:18 +08:00
// params: servers: const char*
// remarks:
// a. IPv4 address is 8.8.8.8 or 8.8.8.8:53, the port is optional
// b. IPv6 addresses with ports require square brackets [fe80::1%lo0]:53
2021-10-23 21:51:50 +08:00
YOPT_S_DNS_LIST,
2023-01-01 23:09:17 +08:00
// Set whether forward event without GC alloc
// params: forward: int(0)
// reamrks:
// when forward event enabled, the option YOPT_S_DEFERRED_EVENT was ignored
YOPT_S_FORWARD_EVENT,
2023-01-04 23:20:07 +08:00
// Set ssl server cert and private key file
// params:
// crtfile: const char*
// keyfile: const char*
YOPT_S_SSL_CERT,
2020-11-16 14:47:43 +08:00
// Sets channel length field based frame decode function, native C++ ONLY
// params: index:int, func:decode_len_fn_t*
2022-02-26 14:27:14 +08:00
YOPT_C_UNPACK_FN = 101,
YOPT_C_LFBFD_FN = YOPT_C_UNPACK_FN,
2020-11-16 14:47:43 +08:00
// Sets channel length field based frame decode params
// params:
// index:int,
// max_frame_length:int(10MBytes),
// length_field_offset:int(-1),
// length_field_length:int(4),
// length_adjustment:int(0),
2021-01-18 15:39:38 +08:00
YOPT_C_UNPACK_PARAMS,
YOPT_C_LFBFD_PARAMS = YOPT_C_UNPACK_PARAMS,
2020-11-16 14:47:43 +08:00
2020-12-15 18:58:04 +08:00
// Sets channel length field based frame decode initial bytes to strip
2020-11-16 14:47:43 +08:00
// params:
// index:int,
// initial_bytes_to_strip:int(0)
2021-01-18 15:39:38 +08:00
YOPT_C_UNPACK_STRIP,
YOPT_C_LFBFD_IBTS = YOPT_C_UNPACK_STRIP,
2020-11-16 14:47:43 +08:00
// Sets channel remote host
// params: index:int, ip:const char*
YOPT_C_REMOTE_HOST,
// Sets channel remote port
// params: index:int, port:int
YOPT_C_REMOTE_PORT,
// Sets channel remote endpoint
// params: index:int, ip:const char*, port:int
YOPT_C_REMOTE_ENDPOINT,
// Sets local host for client channel only
// params: index:int, ip:const char*
YOPT_C_LOCAL_HOST,
// Sets local port for client channel only
// params: index:int, port:int
YOPT_C_LOCAL_PORT,
// Sets local endpoint for client channel only
// params: index:int, ip:const char*, port:int
YOPT_C_LOCAL_ENDPOINT,
2020-12-15 18:58:04 +08:00
// Mods channl flags
2020-11-16 14:47:43 +08:00
// params: index:int, flagsToAdd:int, flagsToRemove:int
YOPT_C_MOD_FLAGS,
2021-10-29 17:54:49 +08:00
// Sets channel multicast interface, required on BSD-like system
// params: index:int, multi_ifaddr:const char*
// remarks:
// a. On BSD-like(APPLE, etc...) system: ipv6 addr must be "::1%lo0" or "::%en0"
YOPT_C_MCAST_IF,
2020-11-16 14:47:43 +08:00
// Enable channel multicast mode
2021-10-29 17:54:49 +08:00
// params: index:int, multi_addr:const char*, loopback:int,
// remarks:
// a. On BSD-like(APPLE, etc...) system: ipv6 addr must be: "ff02::1%lo0" or "ff02::1%en0"
// refer to: https://www.tldp.org/HOWTO/Multicast-HOWTO-2.html
2020-11-16 14:47:43 +08:00
YOPT_C_ENABLE_MCAST,
// Disable channel multicast mode
// params: index:int
YOPT_C_DISABLE_MCAST,
// The kcp conv id, must equal in two endpoint from the same connection
// params: index:int, conv:int
YOPT_C_KCP_CONV,
2022-01-24 16:39:18 +08:00
// Whether never perform bswap for length field
// params: index:int, no_bswap:int(0)
YOPT_C_UNPACK_NO_BSWAP,
2020-11-16 14:47:43 +08:00
// Change 4-tuple association for io_transport_udp
// params: transport:transport_handle_t
2020-12-15 18:58:04 +08:00
// remarks: only works for udp client transport
2020-11-16 14:47:43 +08:00
YOPT_T_CONNECT,
// Dissolve 4-tuple association for io_transport_udp
// params: transport:transport_handle_t
2020-12-15 18:58:04 +08:00
// remarks: only works for udp client transport
2020-11-16 14:47:43 +08:00
YOPT_T_DISCONNECT,
// Sets io_base sockopt
// params: io_base*,level:int,optname:int,optval:int,optlen:int
YOPT_B_SOCKOPT = 201,
};
// channel masks: only for internal use, not for user
enum
{
YCM_CLIENT = 1,
YCM_SERVER = 1 << 1,
YCM_TCP = 1 << 2,
YCM_UDP = 1 << 3,
YCM_KCP = 1 << 4,
YCM_SSL = 1 << 5,
YCM_UDS = 1 << 6, // IPC: posix domain socket
};
// channel kinds: for user to call io_service::open
enum
{
YCK_TCP_CLIENT = YCM_TCP | YCM_CLIENT,
YCK_TCP_SERVER = YCM_TCP | YCM_SERVER,
YCK_UDP_CLIENT = YCM_UDP | YCM_CLIENT,
YCK_UDP_SERVER = YCM_UDP | YCM_SERVER,
YCK_KCP_CLIENT = YCM_KCP | YCM_CLIENT | YCM_UDP,
YCK_KCP_SERVER = YCM_KCP | YCM_SERVER | YCM_UDP,
2023-01-04 23:20:07 +08:00
YCK_SSL_CLIENT = YCK_TCP_CLIENT | YCM_SSL,
YCK_SSL_SERVER = YCK_TCP_SERVER | YCM_SSL,
2020-11-16 14:47:43 +08:00
};
// channel flags
enum
{
/* Whether setsockopt SO_REUSEADDR and SO_REUSEPORT */
YCF_REUSEADDR = 1 << 9,
/* For winsock security issue, see:
https://docs.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse
*/
YCF_EXCLUSIVEADDRUSE = 1 << 10,
};
// event kinds
enum
{
2021-05-18 00:25:11 +08:00
YEK_ON_OPEN = 1,
YEK_ON_CLOSE,
YEK_ON_PACKET,
2021-11-23 12:53:13 +08:00
YEK_CONNECT_RESPONSE = YEK_ON_OPEN, // implicit deprecated alias
YEK_CONNECTION_LOST = YEK_ON_CLOSE, // implicit deprecated alias
YEK_PACKET = YEK_ON_PACKET, // implicit deprecated alias
2020-11-16 14:47:43 +08:00
};
// the network core service log level
enum
{
YLOG_V,
YLOG_D,
YLOG_I,
2021-09-05 22:19:19 +08:00
YLOG_W,
2020-11-16 14:47:43 +08:00
YLOG_E,
};
// class fwds
class highp_timer;
class io_send_op;
class io_sendto_op;
class io_event;
class io_channel;
class io_transport;
class io_transport_tcp; // tcp client/server
class io_transport_ssl; // ssl client
class io_transport_udp; // udp client/server
class io_transport_kcp; // kcp client/server
class io_service;
// recommand user always use transport_handle_t, in the future, it's maybe void* or intptr_t
typedef io_transport* transport_handle_t;
// typedefs
2021-11-19 18:29:37 +08:00
typedef std::shared_ptr<xxsocket> xxsocket_ptr;
2020-11-16 14:47:43 +08:00
typedef std::unique_ptr<io_send_op> send_op_ptr;
typedef std::unique_ptr<io_event> event_ptr;
typedef std::shared_ptr<highp_timer> highp_timer_ptr;
2021-01-19 15:00:08 +08:00
typedef std::function<bool(io_service&)> timer_cb_t;
typedef std::function<void(io_service&)> timerv_cb_t;
2020-11-16 14:47:43 +08:00
typedef std::function<void(event_ptr&&)> event_cb_t;
2020-12-21 18:12:13 +08:00
typedef std::function<bool(event_ptr&)> defer_event_cb_t;
2020-11-16 14:47:43 +08:00
typedef std::function<void(int, size_t)> completion_cb_t;
2021-01-18 15:39:38 +08:00
typedef std::function<int(void* d, int n)> decode_len_fn_t;
2020-11-16 14:47:43 +08:00
typedef std::function<int(std::vector<ip::endpoint>&, const char*, unsigned short)> resolv_fn_t;
typedef std::function<void(const char*)> print_fn_t;
typedef std::function<void(int level, const char*)> print_fn2_t;
typedef std::pair<highp_timer*, timer_cb_t> timer_impl_t;
// alias, for compatible purpose only
typedef highp_timer deadline_timer;
typedef highp_timer_ptr deadline_timer_ptr;
typedef event_cb_t io_event_cb_t;
typedef completion_cb_t io_completion_cb_t;
2023-01-04 23:20:07 +08:00
// the ssl role
enum ssl_role
{
YSSL_CLIENT,
YSSL_SERVER,
};
2022-12-29 15:45:34 +08:00
2020-11-16 14:47:43 +08:00
struct io_hostent {
io_hostent() = default;
2020-12-15 18:58:04 +08:00
io_hostent(cxx17::string_view ip, u_short port) : host_(cxx17::svtos(ip)), port_(port) {}
2023-01-04 23:20:07 +08:00
io_hostent(io_hostent&& rhs) YASIO__NOEXCEPT : host_(std::move(rhs.host_)), port_(rhs.port_) {}
2020-11-16 14:47:43 +08:00
io_hostent(const io_hostent& rhs) : host_(rhs.host_), port_(rhs.port_) {}
2020-12-15 18:58:04 +08:00
void set_ip(cxx17::string_view ip) { cxx17::assign(host_, ip); }
2020-11-16 14:47:43 +08:00
const std::string& get_ip() const { return host_; }
void set_port(u_short port) { port_ = port; }
u_short get_port() const { return port_; }
std::string host_;
u_short port_ = 0;
};
2021-05-18 00:25:11 +08:00
class YASIO_API highp_timer {
2020-11-16 14:47:43 +08:00
public:
2021-06-25 18:29:16 +08:00
highp_timer() = default;
highp_timer(const highp_timer&) = delete;
2021-06-25 18:29:16 +08:00
highp_timer(highp_timer&&) = delete;
2020-11-16 14:47:43 +08:00
void expires_from_now(const std::chrono::microseconds& duration)
{
this->duration_ = duration;
this->expire_time_ = steady_clock_t::now() + this->duration_;
}
void expires_from_now() { this->expire_time_ = steady_clock_t::now() + this->duration_; }
// Wait timer timeout once.
2021-01-19 15:00:08 +08:00
void async_wait_once(io_service& service, timerv_cb_t cb)
2020-11-16 14:47:43 +08:00
{
#if YASIO__HAS_CXX14
2021-01-19 15:00:08 +08:00
this->async_wait(service, [cb = std::move(cb)](io_service& service) {
2020-11-16 14:47:43 +08:00
#else
2021-01-19 15:00:08 +08:00
this->async_wait(service, [cb](io_service& service) {
2020-11-16 14:47:43 +08:00
#endif
2021-01-19 15:00:08 +08:00
cb(service);
2020-11-16 14:47:43 +08:00
return true;
});
}
// Wait timer timeout
// @retval of timer_cb_t:
// true: wait once
// false: wait again after expired
2021-01-14 15:30:18 +08:00
YASIO__DECL void async_wait(io_service& service, timer_cb_t);
2020-11-16 14:47:43 +08:00
// Cancel the timer
2021-01-14 15:30:18 +08:00
YASIO__DECL void cancel(io_service& service);
2020-11-16 14:47:43 +08:00
// Check if timer is expired?
bool expired() const { return wait_duration().count() <= 0; }
// Gets wait duration of timer.
std::chrono::microseconds wait_duration() const { return std::chrono::duration_cast<std::chrono::microseconds>(this->expire_time_ - steady_clock_t::now()); }
std::chrono::microseconds duration_ = {};
std::chrono::time_point<steady_clock_t> expire_time_ = {};
};
2021-05-18 00:25:11 +08:00
struct YASIO_API io_base {
2020-12-08 19:10:58 +08:00
enum class state : uint8_t
2020-11-16 14:47:43 +08:00
{
CLOSED,
2021-11-19 18:29:37 +08:00
RESOLVING, // for client only
2021-11-08 14:43:24 +08:00
CONNECTING, // for client only
2021-11-07 11:40:07 +08:00
OPENED,
2020-11-16 14:47:43 +08:00
};
2020-12-08 19:10:58 +08:00
enum class error_stage : uint8_t
{
NONE,
READ,
WRITE,
2021-05-18 00:25:11 +08:00
OPEN_SOCKET,
BIND_SOCKET,
LISTEN_SOCKET
2020-12-08 19:10:58 +08:00
};
2021-05-18 00:25:11 +08:00
io_base() : error_(0), state_(state::CLOSED), opmask_(0)
{
static unsigned int s_object_id = 0;
this->id_ = ++s_object_id;
}
2020-11-16 14:47:43 +08:00
virtual ~io_base() {}
2021-05-18 00:25:11 +08:00
2020-12-08 19:10:58 +08:00
void set_last_errno(int error, error_stage stage = error_stage::NONE)
{
error_ = error;
error_stage_ = stage;
}
2020-11-16 14:47:43 +08:00
2021-05-18 00:25:11 +08:00
#if !defined(YASIO_MINIFY_EVENT)
// usedata
union {
void* ptr;
int ival;
} ud_{};
#endif
2021-11-19 18:29:37 +08:00
xxsocket_ptr socket_;
2020-11-16 14:47:43 +08:00
int error_; // socket error(>= -1), application error(< -1)
2020-12-08 19:10:58 +08:00
// 0: none, 1: read, 2: write
error_stage error_stage_ = error_stage::NONE;
2020-11-16 14:47:43 +08:00
2020-12-08 19:10:58 +08:00
// mark whether pollout event registerred.
bool pollout_registerred_ = false;
2020-11-16 14:47:43 +08:00
std::atomic<state> state_;
2020-12-08 19:10:58 +08:00
uint8_t opmask_;
2021-05-18 00:25:11 +08:00
unsigned int id_;
public:
unsigned int id() const { return id_; }
2020-11-16 14:47:43 +08:00
};
2021-05-18 00:25:11 +08:00
class YASIO_API io_channel : public io_base {
2020-11-16 14:47:43 +08:00
friend class io_service;
friend class io_transport;
friend class io_transport_tcp;
friend class io_transport_ssl;
friend class io_transport_udp;
friend class io_transport_kcp;
public:
2021-10-29 17:54:49 +08:00
io_service& get_service() const { return service_; }
2023-01-04 23:20:07 +08:00
#if defined(YASIO_SSL_BACKEND)
YASIO__DECL SSL_CTX* get_ssl_context(bool client) const;
#endif
2020-11-16 14:47:43 +08:00
int index() const { return index_; }
u_short remote_port() const { return remote_port_; }
2020-11-24 16:52:03 +08:00
YASIO__DECL std::string format_destination() const;
2020-11-16 14:47:43 +08:00
2021-05-18 00:25:11 +08:00
long long bytes_transferred() const { return bytes_transferred_; }
unsigned int connect_id() const { return connect_id_; }
#if !defined(YASIO_NO_USER_TIMER)
highp_timer& get_user_timer() { return this->user_timer_; }
#endif
2020-11-16 14:47:43 +08:00
protected:
2021-10-29 17:54:49 +08:00
YASIO__DECL void enable_multicast(const char* addr, int loopback);
YASIO__DECL void disable_multicast();
YASIO__DECL void join_multicast_group();
2020-11-16 14:47:43 +08:00
YASIO__DECL int configure_multicast_group(bool onoff);
2021-10-29 17:54:49 +08:00
// For log macro only
YASIO__DECL const print_fn2_t& __get_cprint() const;
2020-11-16 14:47:43 +08:00
private:
YASIO__DECL io_channel(io_service& service, int index);
2020-12-15 18:58:04 +08:00
void set_address(cxx17::string_view host, u_short port)
2020-11-16 14:47:43 +08:00
{
set_host(host);
set_port(port);
}
2020-12-15 18:58:04 +08:00
YASIO__DECL void set_host(cxx17::string_view host);
2020-11-16 14:47:43 +08:00
YASIO__DECL void set_port(u_short port);
2021-11-07 11:40:07 +08:00
void clear_mutable_flags() { properties_ &= 0x00ffffff; }
2020-11-16 14:47:43 +08:00
// -1 indicate failed, connection will be closed
2021-01-18 15:39:38 +08:00
YASIO__DECL int __builtin_decode_len(void* d, int n);
2020-11-16 14:47:43 +08:00
2021-01-14 15:30:18 +08:00
io_service& service_;
2020-11-16 14:47:43 +08:00
/* Since v3.33.0 mask,kind,flags,private_flags are stored to this field
** bit[1-8] mask & kinds
** bit[9-16] flags
** bit[17-24] byte1 of private flags
2021-11-07 11:40:07 +08:00
** bit[25~32] byte2 of private flags (mutable)
2020-11-16 14:47:43 +08:00
*/
uint32_t properties_ = 0;
/*
** !!! tcp/udp client only, if not zero, will use it as fixed port,
** !!! otherwise system will generate a random port for local socket.
*/
u_short local_port_ = 0;
/*
** !!! tcp/udp client, the port to connect
** !!! tcp/udp server, the port to listening
*/
u_short remote_port_ = 0;
2021-12-14 14:05:22 +08:00
// The last query success time in microseconds for dns cache support
highp_time_t query_success_time_ = 0;
2020-11-16 14:47:43 +08:00
2021-12-14 14:05:22 +08:00
#if defined(YASIO_ENABLE_ARES_PROFILER)
highp_time_t query_start_time_;
#endif
2021-12-13 23:05:00 +08:00
2020-11-16 14:47:43 +08:00
int index_;
int socktype_ = 0;
// The timer for check resolve & connect timeout
highp_timer timer_;
#if !defined(YASIO_NO_USER_TIMER)
// The timer for user
highp_timer user_timer_;
#endif
2021-01-18 15:39:38 +08:00
// The stream mode application protocol (based on tcp/udp/kcp) unpack params
2020-11-16 14:47:43 +08:00
struct __unnamed01 {
int max_frame_length = YASIO_SZ(10, M); // 10MBytes
2021-01-18 15:39:38 +08:00
int length_field_offset = -1; // -1: directly, >= 0: store as 1~4bytes integer
2020-11-16 14:47:43 +08:00
int length_field_length = 4; // 1,2,3,4
int length_adjustment = 0;
int initial_bytes_to_strip = 0;
2022-01-24 16:39:18 +08:00
int no_bswap = 0;
2021-01-18 15:39:38 +08:00
} uparams_;
2020-11-16 14:47:43 +08:00
decode_len_fn_t decode_len_;
/*
!!! for tcp/udp client to bind local specific network adapter, empty for any
*/
std::string local_host_;
/*
!!! for tcp/udp client to connect remote host.
!!! for tcp/udp server to bind local specific network adapter, empty for any
!!! for multicast, it's used as multicast address,
doesn't connect even through recvfrom on packet from remote
*/
std::string remote_host_;
std::vector<ip::endpoint> remote_eps_;
2021-10-29 17:54:49 +08:00
ip::endpoint multiaddr_, multiif_;
2020-11-16 14:47:43 +08:00
// Current it's only for UDP
2023-01-04 23:20:07 +08:00
sbyte_buffer buffer_;
2020-11-16 14:47:43 +08:00
2021-05-18 00:25:11 +08:00
// The bytes transferred from socket low layer, currently, only works for client channel
long long bytes_transferred_ = 0;
unsigned int connect_id_ = 0;
2020-11-16 14:47:43 +08:00
#if defined(YASIO_HAVE_KCP)
int kcp_conv_ = 0;
#endif
};
2020-11-24 16:52:03 +08:00
// for tcp transport only
2021-05-18 00:25:11 +08:00
class YASIO_API io_send_op {
2020-11-16 14:47:43 +08:00
public:
2023-01-04 23:20:07 +08:00
io_send_op(sbyte_buffer&& buffer, completion_cb_t&& handler) : offset_(0), buffer_(std::move(buffer)), handler_(std::move(handler)) {}
2020-11-16 14:47:43 +08:00
virtual ~io_send_op() {}
2023-01-04 23:20:07 +08:00
size_t offset_; // read pos from sending buffer
sbyte_buffer buffer_; // sending data buffer
2020-11-16 14:47:43 +08:00
completion_cb_t handler_;
2023-01-04 23:20:07 +08:00
YASIO__DECL virtual int perform(transport_handle_t transport, const void* buf, int n, int& error);
2020-11-16 14:47:43 +08:00
#if !defined(YASIO_DISABLE_OBJECT_POOL)
DEFINE_CONCURRENT_OBJECT_POOL_ALLOCATION(io_send_op, 512)
#endif
};
2020-11-24 16:52:03 +08:00
// for udp transport only
2021-05-18 00:25:11 +08:00
class YASIO_API io_sendto_op : public io_send_op {
2020-11-16 14:47:43 +08:00
public:
2023-01-04 23:20:07 +08:00
io_sendto_op(sbyte_buffer&& buffer, completion_cb_t&& handler, const ip::endpoint& destination)
2020-11-16 14:47:43 +08:00
: io_send_op(std::move(buffer), std::move(handler)), destination_(destination)
{}
2023-01-04 23:20:07 +08:00
YASIO__DECL int perform(transport_handle_t transport, const void* buf, int n, int& error) override;
2020-11-16 14:47:43 +08:00
#if !defined(YASIO_DISABLE_OBJECT_POOL)
DEFINE_CONCURRENT_OBJECT_POOL_ALLOCATION(io_sendto_op, 512)
#endif
ip::endpoint destination_;
};
class io_transport : public io_base {
friend class io_service;
friend class io_send_op;
friend class io_sendto_op;
friend class io_event;
io_transport(const io_transport&) = delete;
public:
2021-05-18 00:25:11 +08:00
int cindex() const { return ctx_->index_; }
2020-11-16 14:47:43 +08:00
ip::endpoint local_endpoint() const { return socket_->local_endpoint(); }
virtual ip::endpoint remote_endpoint() const { return socket_->peer_endpoint(); }
io_channel* get_context() const { return ctx_; }
2021-11-19 18:29:37 +08:00
virtual ~io_transport()
{
ctx_ = nullptr;
send_queue_.clear();
}
2020-11-16 14:47:43 +08:00
protected:
io_service& get_service() const { return ctx_->get_service(); }
2021-11-19 18:29:37 +08:00
bool is_open() const { return state_ == state::OPENED && socket_ && socket_->is_open(); }
2023-01-04 23:20:07 +08:00
sbyte_buffer fetch_packet()
2020-11-16 14:47:43 +08:00
{
expected_size_ = -1;
return std::move(expected_packet_);
}
2021-05-18 00:25:11 +08:00
// For log macro only
YASIO__DECL const print_fn2_t& __get_cprint() const;
2020-11-16 14:47:43 +08:00
// Call at user thread
2023-01-04 23:20:07 +08:00
YASIO__DECL virtual int write(sbyte_buffer&&, completion_cb_t&&);
2020-11-16 14:47:43 +08:00
// Call at user thread
2023-01-04 23:20:07 +08:00
virtual int write_to(sbyte_buffer&&, const ip::endpoint&, completion_cb_t&&)
2020-11-16 14:47:43 +08:00
{
YASIO_LOG("[warning] io_transport doesn't support 'write_to' operation!");
return 0;
}
2023-01-04 23:20:07 +08:00
YASIO__DECL int call_read(void* data, int size, int revent, int& error);
2020-11-16 14:47:43 +08:00
YASIO__DECL int call_write(io_send_op*, int& error);
YASIO__DECL void complete_op(io_send_op*, int error);
// Call at io_service
YASIO__DECL virtual int do_read(int revent, int& error, highp_time_t& wait_duration);
// Call at io_service, try flush pending packet
YASIO__DECL virtual bool do_write(highp_time_t& wait_duration);
// Sets the underlying layer socket io primitives.
YASIO__DECL virtual void set_primitives();
2021-11-19 18:29:37 +08:00
YASIO__DECL io_transport(io_channel* ctx, xxsocket_ptr&& s);
2020-11-16 14:47:43 +08:00
2021-11-19 18:29:37 +08:00
bool is_valid() const { return ctx_ != nullptr; }
2020-11-16 14:47:43 +08:00
char buffer_[YASIO_INET_BUFFER_SIZE]; // recv buffer, 64K
2021-01-18 15:39:38 +08:00
int offset_ = 0; // recv buffer offset
2020-11-16 14:47:43 +08:00
int expected_size_ = -1;
2023-01-04 23:20:07 +08:00
sbyte_buffer expected_packet_;
2020-11-16 14:47:43 +08:00
io_channel* ctx_;
2023-01-04 23:20:07 +08:00
std::function<int(const void*, int, const ip::endpoint*, int&)> write_cb_;
std::function<int(void*, int, int, int&)> read_cb_;
2020-11-16 14:47:43 +08:00
privacy::concurrent_queue<send_op_ptr> send_queue_;
};
2021-05-18 00:25:11 +08:00
class YASIO_API io_transport_tcp : public io_transport {
2020-11-16 14:47:43 +08:00
friend class io_service;
public:
2021-11-19 18:29:37 +08:00
io_transport_tcp(io_channel* ctx, xxsocket_ptr&& s);
2020-11-16 14:47:43 +08:00
};
2021-01-14 15:30:18 +08:00
#if defined(YASIO_SSL_BACKEND)
2020-11-16 14:47:43 +08:00
class io_transport_ssl : public io_transport_tcp {
public:
2021-11-19 18:29:37 +08:00
YASIO__DECL io_transport_ssl(io_channel* ctx, xxsocket_ptr&& s);
2020-11-16 14:47:43 +08:00
YASIO__DECL void set_primitives() override;
2023-01-04 23:20:07 +08:00
YASIO__DECL void do_ssl_shutdown();
2020-11-16 14:47:43 +08:00
protected:
2023-01-04 23:20:07 +08:00
YASIO__DECL int do_ssl_handshake(int& error); // always invoke at do_read
SSL* ssl_ = nullptr;
2020-11-16 14:47:43 +08:00
};
#else
class io_transport_ssl {};
#endif
2021-05-18 00:25:11 +08:00
class YASIO_API io_transport_udp : public io_transport {
2020-11-16 14:47:43 +08:00
friend class io_service;
public:
2021-11-19 18:29:37 +08:00
YASIO__DECL io_transport_udp(io_channel* ctx, xxsocket_ptr&& s);
2020-11-16 14:47:43 +08:00
YASIO__DECL ~io_transport_udp();
YASIO__DECL ip::endpoint remote_endpoint() const override;
protected:
2021-10-29 17:54:49 +08:00
YASIO__DECL void connect();
YASIO__DECL void disconnect();
2020-11-16 14:47:43 +08:00
2023-01-04 23:20:07 +08:00
YASIO__DECL int write(sbyte_buffer&&, completion_cb_t&&) override;
YASIO__DECL int write_to(sbyte_buffer&&, const ip::endpoint&, completion_cb_t&&) override;
2020-11-16 14:47:43 +08:00
YASIO__DECL void set_primitives() override;
// ensure destination for sendto valid, if not, assign from ctx_->remote_eps_[0]
YASIO__DECL const ip::endpoint& ensure_destination() const;
// configure remote with specific endpoint
2021-10-29 17:54:49 +08:00
YASIO__DECL void confgure_remote(const ip::endpoint& peer);
2020-11-16 14:47:43 +08:00
// process received data from low level
YASIO__DECL virtual int handle_input(const char* data, int bytes_transferred, int& error, highp_time_t& wait_duration);
2020-11-16 14:47:43 +08:00
2020-11-24 16:52:03 +08:00
ip::endpoint peer_; // for recv only, unstable
mutable ip::endpoint destination_; // for sendto only, stable
2020-11-16 14:47:43 +08:00
bool connected_ = false;
};
#if defined(YASIO_HAVE_KCP)
class io_transport_kcp : public io_transport_udp {
public:
2021-11-19 18:29:37 +08:00
YASIO__DECL io_transport_kcp(io_channel* ctx, xxsocket_ptr&& s);
2020-11-16 14:47:43 +08:00
YASIO__DECL ~io_transport_kcp();
ikcpcb* internal_object() { return kcp_; }
protected:
2023-01-04 23:20:07 +08:00
YASIO__DECL int write(sbyte_buffer&&, completion_cb_t&&) override;
2020-11-16 14:47:43 +08:00
YASIO__DECL int do_read(int revent, int& error, highp_time_t& wait_duration) override;
YASIO__DECL bool do_write(highp_time_t& wait_duration) override;
YASIO__DECL int handle_input(const char* buf, int len, int& error, highp_time_t& wait_duration) override;
YASIO__DECL void check_timeout(highp_time_t& wait_duration) const;
2023-01-04 23:20:07 +08:00
sbyte_buffer rawbuf_; // the low level raw buffer
2020-11-16 14:47:43 +08:00
ikcpcb* kcp_;
std::recursive_mutex send_mtx_;
};
#else
class io_transport_kcp {};
#endif
2023-01-04 23:20:07 +08:00
using io_packet = sbyte_buffer;
2021-05-18 00:25:11 +08:00
#if !defined(YASIO_USE_SHARED_PACKET)
using packet_t = io_packet;
inline packet_t wrap_packet(io_packet& raw_packet) { return std::move(raw_packet); }
inline bool is_packet_empty(packet_t& pkt) { return pkt.empty(); }
inline io_packet& forward_packet(packet_t& pkt) { return pkt; }
inline io_packet&& forward_packet(packet_t&& pkt) { return std::move(pkt); }
inline io_packet::pointer packet_data(packet_t& pkt) { return pkt.data(); }
inline io_packet::size_type packet_len(packet_t& pkt) { return pkt.size(); }
#else
using packet_t = std::shared_ptr<io_packet>;
inline packet_t wrap_packet(io_packet& raw_packet) { return std::make_shared<io_packet>(std::move(raw_packet)); }
inline bool is_packet_empty(packet_t& pkt) { return !pkt; }
inline io_packet& forward_packet(packet_t& pkt) { return *pkt; }
inline io_packet&& forward_packet(packet_t&& pkt) { return std::move(*pkt); }
inline io_packet::pointer packet_data(packet_t& pkt) { return pkt->data(); }
inline io_packet::size_type packet_len(packet_t& pkt) { return pkt->size(); }
#endif
2023-01-01 23:09:17 +08:00
class io_packet_view {
public:
io_packet_view() = default;
io_packet_view(char* d, int n) : data_(d), size_(n) {}
char* data() { return this->data_; }
const char* data() const { return this->data_; }
size_t size() const { return this->size_; }
private:
char* data_ = nullptr;
size_t size_ = 0;
};
2021-05-18 00:25:11 +08:00
/*
* Notes: store some properties of event source to make sure user can safe get them deferred
*/
2020-11-16 14:47:43 +08:00
class io_event final {
public:
2021-05-18 00:25:11 +08:00
io_event(int cidx, int kind, int status, io_channel* source /*not nullable*/, int passive = 0)
2021-06-22 19:52:44 +08:00
: kind_(kind), writable_(0), passive_(passive), status_(status), cindex_(cidx), source_id_(source->id_), source_(source)
2021-05-18 00:25:11 +08:00
{
2020-11-16 14:47:43 +08:00
#if !defined(YASIO_MINIFY_EVENT)
2021-05-18 00:25:11 +08:00
source_ud_ = source_->ud_.ptr;
2020-11-16 14:47:43 +08:00
#endif
2021-05-18 00:25:11 +08:00
}
2021-06-14 22:54:50 +08:00
io_event(int cidx, int kind, int status, io_transport* source /*not nullable*/)
2021-06-22 19:52:44 +08:00
: kind_(kind), writable_(1), passive_(0), status_(status), cindex_(cidx), source_id_(source->id_), source_(source)
2021-05-18 00:25:11 +08:00
{
2020-11-16 14:47:43 +08:00
#if !defined(YASIO_MINIFY_EVENT)
2021-05-18 00:25:11 +08:00
source_ud_ = source_->ud_.ptr;
2020-11-16 14:47:43 +08:00
#endif
2021-05-18 00:25:11 +08:00
}
io_event(int cidx, io_packet&& pkt, io_transport* source /*not nullable*/)
2021-06-22 19:52:44 +08:00
: kind_(YEK_ON_PACKET), writable_(1), passive_(0), status_(0), cindex_(cidx), source_id_(source->id_), source_(source), packet_(wrap_packet(pkt))
2021-05-18 00:25:11 +08:00
{
2020-11-16 14:47:43 +08:00
#if !defined(YASIO_MINIFY_EVENT)
2021-05-18 00:25:11 +08:00
source_ud_ = source_->ud_.ptr;
2023-01-01 23:09:17 +08:00
#endif
}
io_event(int cidx, io_packet_view pkt, io_transport* source /*not nullable*/)
: kind_(YEK_ON_PACKET), writable_(1), passive_(0), status_(0), cindex_(cidx), source_id_(source->id_), source_(source), packet_view_(pkt)
{
#if !defined(YASIO_MINIFY_EVENT)
source_ud_ = source_->ud_.ptr;
2020-11-16 14:47:43 +08:00
#endif
2021-05-18 00:25:11 +08:00
}
io_event(const io_event&) = delete;
io_event(io_event&& rhs) = delete;
2020-11-16 14:47:43 +08:00
~io_event() {}
2021-05-18 00:25:11 +08:00
public:
2020-11-16 14:47:43 +08:00
int cindex() const { return cindex_; }
2021-05-18 00:25:11 +08:00
int status() const { return status_; }
2020-11-16 14:47:43 +08:00
int kind() const { return kind_; }
2021-05-18 00:25:11 +08:00
// whether the event triggered by server channel
int passive() const { return passive_; }
packet_t& packet() { return packet_; }
2023-01-01 23:09:17 +08:00
io_packet_view packet_view() const { return packet_view_; }
2021-05-18 00:25:11 +08:00
/*[nullable]*/ transport_handle_t transport() const { return writable_ ? static_cast<transport_handle_t>(source_) : nullptr; }
io_base* source() const { return source_; }
unsigned int source_id() const { return source_id_; }
2020-11-16 14:47:43 +08:00
#if !defined(YASIO_MINIFY_EVENT)
/* Gets to transport user data when process this event */
2021-10-23 21:51:50 +08:00
template <typename _Uty = void*>
_Uty transport_ud() const
{
return (_Uty)(uintptr_t)source_ud_;
}
2020-11-16 14:47:43 +08:00
/* Sets trasnport user data when process this event */
2021-10-23 21:51:50 +08:00
template <typename _Uty = void*>
void transport_ud(_Uty uval)
2020-11-16 14:47:43 +08:00
{
2021-05-18 00:25:11 +08:00
source_ud_ = (void*)(uintptr_t)uval;
auto t = this->transport();
if (t)
t->ud_.ptr = (void*)(uintptr_t)uval;
2020-11-16 14:47:43 +08:00
}
highp_time_t timestamp() const { return timestamp_; }
#endif
#if !defined(YASIO_DISABLE_OBJECT_POOL)
2023-01-01 23:09:17 +08:00
DEFINE_CONCURRENT_OBJECT_POOL_ALLOCATION(io_event, 128)
2020-11-16 14:47:43 +08:00
#endif
private:
2021-05-18 00:25:11 +08:00
unsigned int kind_ : 30;
unsigned int writable_ : 1;
unsigned int passive_ : 1;
2020-11-16 14:47:43 +08:00
int status_;
2021-05-18 00:25:11 +08:00
int cindex_;
unsigned int source_id_;
io_base* source_;
packet_t packet_;
2023-01-01 23:09:17 +08:00
io_packet_view packet_view_;
2020-11-16 14:47:43 +08:00
#if !defined(YASIO_MINIFY_EVENT)
2021-05-18 00:25:11 +08:00
void* source_ud_;
highp_time_t timestamp_ = highp_clock();
2020-11-16 14:47:43 +08:00
#endif
};
2021-05-18 00:25:11 +08:00
class YASIO_API io_service // lgtm [cpp/class-many-fields]
2020-11-16 14:47:43 +08:00
{
friend class highp_timer;
friend class io_transport;
friend class io_transport_tcp;
friend class io_transport_udp;
friend class io_transport_kcp;
2023-01-04 23:20:07 +08:00
#if defined(YASIO_SSL_BACKEND)
friend class io_transport_ssl;
#endif
2020-11-16 14:47:43 +08:00
friend class io_channel;
public:
enum class state
{
UNINITIALIZED,
IDLE,
RUNNING,
2021-11-07 11:40:07 +08:00
AT_EXITING,
2020-11-16 14:47:43 +08:00
};
/*
2021-11-03 16:53:39 +08:00
** summary: init global state with custom print function, you must ensure thread safe of it.
** remark:
2020-11-16 14:47:43 +08:00
** a. this function is not required, if you don't want print init log to custom console.
2020-12-08 20:36:55 +08:00
** b. this function only works once
2020-11-16 14:47:43 +08:00
** c. you should call once before call any 'io_servic::start'
*/
YASIO__DECL static void init_globals(const yasio::inet::print_fn2_t&);
2020-12-08 20:36:55 +08:00
YASIO__DECL static void cleanup_globals();
2020-11-16 14:47:43 +08:00
2021-01-14 15:30:18 +08:00
// the additional API to get rtt of tcp transport
2021-05-18 00:25:11 +08:00
YASIO__DECL static unsigned int tcp_rtt(transport_handle_t);
2021-01-14 15:30:18 +08:00
2020-11-16 14:47:43 +08:00
public:
YASIO__DECL io_service();
YASIO__DECL io_service(int channel_count);
YASIO__DECL io_service(const io_hostent& channel_eps);
YASIO__DECL io_service(const std::vector<io_hostent>& channel_eps);
YASIO__DECL io_service(const io_hostent* channel_eps, int channel_count);
YASIO__DECL ~io_service();
YASIO__DECL void start(event_cb_t cb);
2021-11-03 16:53:39 +08:00
/* summary: stop the io_service
**
** remark:
** a. IF caller thread isn't service worker thread, the service state will be IDLE.
** b. IF caller thread is service worker thread, the service state will be STOPPING,
** then you needs to invoke this API again at other thread
** c. Strong recommend invoke this API at your event dispatch thread.
*/
2020-11-16 14:47:43 +08:00
YASIO__DECL void stop();
bool is_running() const { return this->state_ == io_service::state::RUNNING; }
2021-11-07 11:40:07 +08:00
bool is_stopping() const { return !!this->stop_flag_; }
2020-11-16 14:47:43 +08:00
// should call at the thread who care about async io
// events(CONNECT_RESPONSE,CONNECTION_LOST,PACKET), such cocos2d-x opengl or
// any other game engines' render thread.
YASIO__DECL void dispatch(int max_count = 128);
// set option, see enum YOPT_XXX
YASIO__DECL void set_option(int opt, ...);
YASIO__DECL void set_option_internal(int opt, va_list args);
// open a channel, default: YCK_TCP_CLIENT
2021-11-08 14:43:24 +08:00
YASIO__DECL bool open(size_t index, int kind = YCK_TCP_CLIENT);
2020-11-16 14:47:43 +08:00
// check whether the channel is open
YASIO__DECL bool is_open(int index) const;
// check whether the transport is open
YASIO__DECL bool is_open(transport_handle_t) const;
// close transport
YASIO__DECL void close(transport_handle_t);
// close channel
YASIO__DECL void close(int index);
/*
2021-11-03 16:53:39 +08:00
** summary: Write data to a TCP or connected UDP transport with last peer address
** retval: < 0: failed
** params:
2020-11-16 14:47:43 +08:00
** 'thandle': the transport to write, could be tcp/udp/kcp
** 'buf': the data to write
** 'len': the data len
** 'handler': send finish callback, only works for TCP transport
2021-11-03 16:53:39 +08:00
** remark:
2020-11-16 14:47:43 +08:00
** + TCP/UDP: Use queue to store user message, flush at io_service thread
** + KCP: Use queue provided by kcp internal, flush at io_service thread
*/
int write(transport_handle_t thandle, const void* buf, size_t len, completion_cb_t completion_handler = nullptr)
{
2023-01-04 23:20:07 +08:00
return write(thandle, sbyte_buffer{(const char*)buf, (const char*)buf + len, std::true_type{}}, std::move(completion_handler));
2020-11-16 14:47:43 +08:00
}
2023-01-04 23:20:07 +08:00
YASIO__DECL int write(transport_handle_t thandle, sbyte_buffer buffer, completion_cb_t completion_handler = nullptr);
2020-11-16 14:47:43 +08:00
/*
** Summary: Write data to unconnected UDP transport with specified address.
2021-11-03 16:53:39 +08:00
** retval: < 0: failed
** remark: This function only for UDP like transport (UDP or KCP)
2020-11-16 14:47:43 +08:00
** + UDP: Use queue to store user message, flush at io_service thread
** + KCP: Use the queue provided by kcp internal, flush at io_service thread
*/
int write_to(transport_handle_t thandle, const void* buf, size_t len, const ip::endpoint& to, completion_cb_t completion_handler = nullptr)
{
2023-01-04 23:20:07 +08:00
return write_to(thandle, sbyte_buffer{(const char*)buf, (const char*)buf + len, std::true_type{}}, to, std::move(completion_handler));
2020-11-16 14:47:43 +08:00
}
2023-01-04 23:20:07 +08:00
YASIO__DECL int write_to(transport_handle_t thandle, sbyte_buffer buffer, const ip::endpoint& to, completion_cb_t completion_handler = nullptr);
2020-11-16 14:47:43 +08:00
// The highp_timer support, !important, the callback is called on the thread of io_service
YASIO__DECL highp_timer_ptr schedule(const std::chrono::microseconds& duration, timer_cb_t);
YASIO__DECL int resolve(std::vector<ip::endpoint>& endpoints, const char* hostname, unsigned short port = 0);
// Gets channel by index
YASIO__DECL io_channel* channel_at(size_t index) const;
2023-01-10 22:19:30 +08:00
YASIO__DECL static const char* strerror(int error);
2020-11-16 14:47:43 +08:00
private:
2021-11-23 12:53:13 +08:00
YASIO__DECL void do_stop(uint8_t flags);
2020-11-16 14:47:43 +08:00
YASIO__DECL void schedule_timer(highp_timer*, timer_cb_t&&);
YASIO__DECL void remove_timer(highp_timer*);
std::vector<timer_impl_t>::iterator find_timer(highp_timer* key)
{
return yasio__find_if(timer_queue_, [=](const timer_impl_t& timer) { return timer.first == key; });
}
void sort_timers()
{
std::sort(this->timer_queue_.begin(), this->timer_queue_.end(),
[](const timer_impl_t& lhs, const timer_impl_t& rhs) { return lhs.first->wait_duration() > rhs.first->wait_duration(); });
}
2021-12-14 14:05:22 +08:00
// Start a async domain name query
YASIO__DECL void start_query(io_channel*);
2020-11-16 14:47:43 +08:00
2021-11-03 16:53:39 +08:00
YASIO__DECL void initialize(const io_hostent* channel_eps /* could be nullptr */, int channel_count);
YASIO__DECL void finalize();
2020-11-16 14:47:43 +08:00
2021-11-03 16:53:39 +08:00
// Try to dispose thread and other resources, service state will be IDLE when succeed
2020-11-16 14:47:43 +08:00
YASIO__DECL void handle_stop();
2021-11-08 14:43:24 +08:00
YASIO__DECL bool open_internal(io_channel*);
2020-11-16 14:47:43 +08:00
2023-01-01 17:36:38 +08:00
YASIO__DECL void process_transports(fd_set_adapter& fd_set);
YASIO__DECL void process_channels(fd_set_adapter& fd_set);
2020-11-16 14:47:43 +08:00
YASIO__DECL void process_timers();
YASIO__DECL void interrupt();
YASIO__DECL highp_time_t get_timeout(highp_time_t usec);
2021-11-07 11:40:07 +08:00
YASIO__DECL int do_resolve(io_channel* ctx);
YASIO__DECL void do_connect(io_channel*);
2023-01-01 17:36:38 +08:00
YASIO__DECL void do_connect_completion(io_channel*, fd_set_adapter& fd_set);
2020-11-16 14:47:43 +08:00
2021-01-14 15:30:18 +08:00
#if defined(YASIO_SSL_BACKEND)
2023-01-04 23:20:07 +08:00
YASIO__DECL SSL_CTX* init_ssl_context(ssl_role role);
YASIO__DECL void cleanup_ssl_context(ssl_role role);
2020-11-16 14:47:43 +08:00
#endif
#if defined(YASIO_HAVE_CARES)
2021-07-08 00:07:04 +08:00
YASIO__DECL static void ares_getaddrinfo_cb(void* arg, int status, int timeouts, ares_addrinfo* answerlist);
2021-12-13 23:05:00 +08:00
YASIO__DECL void ares_work_started();
YASIO__DECL void ares_work_finished();
2023-01-04 23:20:07 +08:00
YASIO__DECL int do_ares_fds(socket_native_type* socks, fd_set_adapter& fd_set, timeval& waitd_tv);
YASIO__DECL void do_ares_process_fds(socket_native_type* socks, int count, fd_set_adapter& fd_set);
2020-11-16 14:47:43 +08:00
YASIO__DECL void recreate_ares_channel();
YASIO__DECL void config_ares_name_servers();
YASIO__DECL void destroy_ares_channel();
#endif
2021-11-19 18:29:37 +08:00
void handle_connect_succeed(io_channel* ctx, xxsocket_ptr s) { handle_connect_succeed(allocate_transport(ctx, std::move(s))); }
2020-11-16 14:47:43 +08:00
YASIO__DECL void handle_connect_succeed(transport_handle_t);
YASIO__DECL void handle_connect_failed(io_channel*, int ec);
2023-01-04 23:20:07 +08:00
YASIO__DECL void active_transport(transport_handle_t);
2020-11-16 14:47:43 +08:00
2021-11-19 18:29:37 +08:00
YASIO__DECL transport_handle_t allocate_transport(io_channel*, xxsocket_ptr&&);
2020-11-16 14:47:43 +08:00
YASIO__DECL void deallocate_transport(transport_handle_t);
2023-01-01 17:36:38 +08:00
YASIO__DECL void register_descriptor(const socket_native_type fd, int events);
YASIO__DECL void deregister_descriptor(const socket_native_type fd, int events);
2020-11-16 14:47:43 +08:00
// The major non-blocking event-loop
YASIO__DECL void run(void);
2023-01-01 17:36:38 +08:00
YASIO__DECL bool do_read(transport_handle_t, fd_set_adapter& fd_set);
2020-11-16 14:47:43 +08:00
bool do_write(transport_handle_t transport) { return transport->do_write(this->wait_duration_); }
YASIO__DECL void unpack(transport_handle_t, int bytes_expected, int bytes_transferred, int bytes_to_strip);
2021-11-07 11:40:07 +08:00
YASIO__DECL bool cleanup_channel(io_channel* channel, bool clear_mask = true);
YASIO__DECL bool cleanup_io(io_base* obj, bool clear_mask = true);
2020-11-16 14:47:43 +08:00
YASIO__DECL void handle_close(transport_handle_t);
2023-01-01 23:09:17 +08:00
template <typename... _Types>
inline void fire_event(_Types&&... args)
{
auto event = cxx14::make_unique<io_event>(std::forward<_Types>(args)...);
if (options_.deferred_event_ && !options_.forward_event_)
{
if (options_.on_defer_event_ && !options_.on_defer_event_(event))
return;
events_.emplace(std::move(event));
}
else
options_.on_event_(std::move(event));
}
2020-11-16 14:47:43 +08:00
// new/delete client socket connection channel
// please call this at initialization, don't new channel at runtime
// dynmaically: because this API is not thread safe.
YASIO__DECL void create_channels(const io_hostent* eps, int count);
2021-11-03 16:53:39 +08:00
YASIO__DECL void destroy_channels(); // destroy all channels
YASIO__DECL void clear_transports(); // clear all transports
2020-11-16 14:47:43 +08:00
YASIO__DECL bool close_internal(io_channel*);
// supporting server
2021-11-07 11:40:07 +08:00
YASIO__DECL void do_accept(io_channel*);
2023-01-01 17:36:38 +08:00
YASIO__DECL void do_accept_completion(io_channel*, fd_set_adapter& fd_set);
2020-11-16 14:47:43 +08:00
/*
2021-11-03 16:53:39 +08:00
** summary: For udp-server only, make dgram handle to communicate with client
2020-11-16 14:47:43 +08:00
*/
YASIO__DECL transport_handle_t do_dgram_accept(io_channel*, const ip::endpoint& peer, int& error);
int local_address_family() const { return ((ipsv_ & ipsv_ipv4) || !ipsv_) ? AF_INET : AF_INET6; }
YASIO__DECL void update_dns_status();
2021-12-14 14:05:22 +08:00
bool address_expired(io_channel* ctx) const { return (highp_clock() - ctx->query_success_time_) > options_.dns_cache_timeout_; }
2021-05-18 00:25:11 +08:00
/* For log macro only */
inline const print_fn2_t& __get_cprint() const { return options_.print_; }
2020-11-16 14:47:43 +08:00
private:
state state_ = state::UNINITIALIZED; // The service state
std::thread worker_;
std::thread::id worker_id_;
privacy::concurrent_queue<event_ptr, true> events_;
std::vector<io_channel*> channels_;
std::recursive_mutex channel_ops_mtx_;
std::vector<io_channel*> channel_ops_;
std::vector<transport_handle_t> transports_;
std::vector<transport_handle_t> tpool_;
// select interrupter
select_interrupter interrupter_;
// timer support timer_pair
std::vector<timer_impl_t> timer_queue_;
std::recursive_mutex timer_queue_mtx_;
// the next wait duration for socket.select
highp_time_t wait_duration_;
2023-01-01 17:36:38 +08:00
fd_set_adapter fd_set_;
2020-11-16 14:47:43 +08:00
// options
struct __unnamed_options {
highp_time_t connect_timeout_ = 10LL * std::micro::den;
highp_time_t dns_cache_timeout_ = 600LL * std::micro::den;
highp_time_t dns_queries_timeout_ = 5LL * std::micro::den;
int dns_queries_tries_ = 5;
bool dns_dirty_ = false;
2020-11-16 14:47:43 +08:00
bool deferred_event_ = true;
2020-12-21 18:12:13 +08:00
defer_event_cb_t on_defer_event_;
2020-11-16 14:47:43 +08:00
2023-01-01 23:09:17 +08:00
bool forward_event_ = false; // since v3.39.7
2020-11-16 14:47:43 +08:00
// tcp keepalive settings
struct __unnamed01 {
int onoff = 0;
int idle = 7200;
int interval = 75;
int probs = 10;
} tcp_keepalive_;
2020-12-08 19:10:58 +08:00
bool no_new_thread_ = false;
2020-11-16 14:47:43 +08:00
// The resolve function
resolv_fn_t resolv_;
// the event callback
event_cb_t on_event_;
// The custom debug print function
print_fn2_t print_;
2021-01-14 15:30:18 +08:00
#if defined(YASIO_SSL_BACKEND)
2023-01-04 23:20:07 +08:00
// SSL client, the full path cacert(.pem) file for ssl verifaction
2021-01-14 15:30:18 +08:00
std::string cafile_;
2023-01-04 23:20:07 +08:00
// SSL server
std::string crtfile_;
std::string keyfile_;
2020-11-16 14:47:43 +08:00
#endif
2021-10-23 21:51:50 +08:00
#if defined(YASIO_HAVE_CARES)
std::string name_servers_;
#endif
2020-11-16 14:47:43 +08:00
} options_;
// The ip stack version supported by localhost
u_short ipsv_ = 0;
2021-11-07 11:40:07 +08:00
// The stop flag to notify all transports needs close
uint8_t stop_flag_ = 0;
2021-01-14 15:30:18 +08:00
#if defined(YASIO_SSL_BACKEND)
2023-01-04 23:20:07 +08:00
SSL_CTX* ssl_roles_[2];
2020-11-16 14:47:43 +08:00
#endif
#if defined(YASIO_HAVE_CARES)
ares_channel ares_ = nullptr; // the ares handle for non blocking io dns resolve support
int ares_outstanding_work_ = 0;
#else
// we need life_token + life_mutex
struct life_token {};
std::shared_ptr<life_token> life_token_;
std::shared_ptr<cxx17::shared_mutex> life_mutex_;
#endif
}; // io_service
} // namespace inet
2022-10-13 22:00:06 +08:00
#if !YASIO__HAS_CXX11
2021-06-14 22:54:50 +08:00
using namespace yasio::inet;
#endif
2020-11-16 14:47:43 +08:00
} /* namespace yasio */
#define yasio_shared_service yasio::gc::singleton<yasio::inet::io_service>::instance
#if defined(YASIO_HEADER_ONLY)
# include "yasio/yasio.cpp" // lgtm [cpp/include-non-header]
#endif
#endif