axmol/thirdparty/yasio/xxsocket.hpp

1149 lines
37 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)
2021-01-14 15:30:18 +08:00
Copyright (c) 2012-2021 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__XXSOCKET_HPP
#define YASIO__XXSOCKET_HPP
#include <stddef.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <sstream>
#include <vector>
#include <chrono>
#include <functional>
2021-10-26 13:26:36 +08:00
#include <memory>
2021-10-09 13:48:56 +08:00
#include "yasio/detail/socket.hpp"
2021-10-23 21:51:50 +08:00
#include "yasio/detail/logging.hpp"
2020-11-16 14:47:43 +08:00
#if defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable : 4996)
#endif
namespace yasio
{
2021-06-14 22:54:50 +08:00
YASIO__NS_INLINE
2020-11-16 14:47:43 +08:00
namespace inet
{
// #define _make_dotted_decimal(b1,b2,b3,b4) ( ( ((uint32_t)(b4) << 24) & 0xff000000 ) | (
// ((uint32_t)(b3) << 16) & 0x00ff0000 ) | ( ((uint32_t)(b2) << 8) & 0x0000ff00 ) | ( (uint32_t)(b1)
// & 0x000000ff ) )
static const socket_native_type invalid_socket = (socket_native_type)-1;
2021-01-14 15:30:18 +08:00
YASIO__NS_INLINE
namespace ip
2020-11-16 14:47:43 +08:00
{
#pragma pack(push, 1)
2021-01-14 15:30:18 +08:00
// ip packet
struct ip_header {
// header size; 5+
unsigned char header_length : 4;
// IP version: 0100/0x04(IPv4), 0110/0x05(IPv6)
unsigned char version : 4;
// type of service:
union {
unsigned char value;
struct {
unsigned char priority : 3;
unsigned char D : 1; // delay: 0(normal), 1(as little as possible)
unsigned char T : 1; // throughput: 0(normal), 1(as big as possible)
unsigned char R : 1; // reliability: 0(normal), 1(as big as possible)
unsigned char C : 1; // transmission cost: 0(normal), 1(as little as possible)
unsigned char reserved : 1; // always be zero
} detail;
} TOS;
// total size, header + data; MAX length is: 65535
unsigned short total_length;
// identifier: all split small packet set as the same value.
unsigned short identifier;
// flags and frag
unsigned short flags : 3;
unsigned short frag : 13;
// time of living, decreased by route, if zero, this packet will by dropped
// avoid foward looply.
unsigned char TTL;
// protocol
// 1: ICMP
// 2: IGMP
// 6: TCP
// 0x11/17: UDP
// 0x58/88: IGRP
// 0x59/89: OSPF
unsigned char protocol; // TCP / UDP / Other
// check header of IP-PACKET 's correctness.
unsigned short checksum;
typedef union {
unsigned int value;
struct {
unsigned int B1 : 8, B2 : 8, B3 : 8, B4 : 8;
} detail;
} dotted_decimal_t;
// source ip address
dotted_decimal_t src_ip;
// destination ip address
dotted_decimal_t dst_ip;
};
struct psd_header {
unsigned long src_addr;
unsigned long dst_addr;
char mbz;
char protocol;
unsigned short tcp_length;
};
struct tcp_header {
unsigned short src_port; // lgtm [cpp/class-many-fields]
unsigned short dst_port;
unsigned int seqno;
unsigned int ackno;
unsigned char header_length : 4;
unsigned char reserved : 4;
unsigned char flg_fin : 1, flg_syn : 1, flg_rst : 1, flg_psh : 1, flg_ack : 1, flg_urg : 1, flg_reserved : 2;
unsigned short win_length;
unsigned short checksum;
unsigned short urp;
};
struct udp_header {
unsigned short src_port;
unsigned short dst_port;
unsigned short length;
unsigned short checksum;
};
struct icmp_header {
unsigned char type; // 8bit type
unsigned char code; // 8bit code
unsigned short checksum; // 16bit check sum
unsigned short id; // identifier: usually use process id
unsigned short seqno; // message sequence NO.
};
struct eth_header {
unsigned dst_eth[6];
unsigned src_eth[6];
unsigned eth_type;
};
struct arp_header {
unsigned short arp_hw; // format of hardware address
unsigned short arp_pro; // format of protocol address
unsigned char arp_hlen; // length of hardware address
unsigned char arp_plen; // length of protocol address
unsigned short arp_op; // arp operation
unsigned char arp_oha[6]; // sender hardware address
unsigned long arp_opa; // sender protocol address
unsigned char arp_tha; // target hardware address
unsigned long arp_tpa; // target protocol address;
};
struct arp_packet {
eth_header ethhdr;
arp_header arphdr;
};
#pragma pack(pop)
2020-11-16 14:47:43 +08:00
2021-01-14 15:30:18 +08:00
namespace compat
{
#if YASIO__HAS_NTOP
using ::inet_ntop;
using ::inet_pton;
#else
YASIO__DECL const char* inet_ntop(int af, const void* src, char* dst, socklen_t);
YASIO__DECL int inet_pton(int af, const char* src, void* dst);
#endif
} // namespace compat
2020-11-16 14:47:43 +08:00
2021-01-14 15:30:18 +08:00
inline bool is_global_in4_addr(const in_addr* addr) { return !IN4_IS_ADDR_LOOPBACK(addr) && !IN4_IS_ADDR_LINKLOCAL(addr); };
inline bool is_global_in6_addr(const in6_addr* addr) { return !!IN6_IS_ADDR_GLOBAL(addr); };
2020-11-16 14:47:43 +08:00
2021-01-14 15:30:18 +08:00
struct endpoint {
public:
2021-10-25 15:21:18 +08:00
enum
{
fmt_no_local = 1,
2021-10-27 22:24:58 +08:00
fmt_no_port = 1 << 1,
fmt_no_port_0 = 1 << 2,
fmt_no_un_path = 1 << 3,
fmt_default = fmt_no_port_0 | fmt_no_un_path,
2021-10-25 15:21:18 +08:00
};
#if defined(YASIO_ENABLE_UDS) && YASIO__HAS_UDS
2021-10-29 17:54:49 +08:00
static const size_t max_fmt_len = (std::max)(IN_MAX_ADDRSTRLEN + 2 /*[]*/ + sizeof("65535") /*:port*/, sizeof(sockaddr_un::sun_path));
2021-10-25 15:21:18 +08:00
#else
2021-10-29 17:54:49 +08:00
static const size_t max_fmt_len = IN_MAX_ADDRSTRLEN + 2 /*[]*/ + sizeof("65535") /*:port*/;
2021-10-25 15:21:18 +08:00
#endif
2021-01-14 15:30:18 +08:00
endpoint() { this->zeroset(); }
endpoint(const endpoint& rhs) { this->as_is(rhs); }
explicit endpoint(const addrinfo* info) { as_is(info); }
explicit endpoint(const sockaddr* info) { as_is(info); }
2021-10-27 22:24:58 +08:00
explicit endpoint(const char* str_ep) { as_is(str_ep); }
endpoint(const char* addr, unsigned short port) { as_in(addr, port); }
endpoint(uint32_t addr, unsigned short port) { as_in(addr, port); }
endpoint(int family, const void* addr, unsigned short port) { as_in(family, addr, port); }
2021-01-14 15:30:18 +08:00
explicit operator bool() const { return this->af() != AF_UNSPEC; }
endpoint& operator=(const endpoint& rhs) { return this->as_is(rhs); }
endpoint& as_is(const endpoint& rhs)
2020-11-16 14:47:43 +08:00
{
2021-01-14 15:30:18 +08:00
this->zeroset();
memcpy(this, &rhs, sizeof(rhs));
return *this;
}
endpoint& as_is(const addrinfo* info) { return this->as_is_raw(info->ai_addr, info->ai_addrlen); }
endpoint& as_is(const sockaddr* addr)
{
this->zeroset();
switch (addr->sa_family)
2020-11-16 14:47:43 +08:00
{
2021-01-14 15:30:18 +08:00
case AF_INET:
::memcpy(&in4_, addr, sizeof(sockaddr_in));
this->len(sizeof(sockaddr_in));
break;
case AF_INET6:
::memcpy(&in6_, addr, sizeof(sockaddr_in6));
this->len(sizeof(sockaddr_in6));
break;
2020-11-16 14:47:43 +08:00
#if defined(YASIO_ENABLE_UDS) && YASIO__HAS_UDS
2021-01-14 15:30:18 +08:00
case AF_UNIX:
as_un(((sockaddr_un*)addr)->sun_path);
break;
2020-11-16 14:47:43 +08:00
#endif
}
2021-01-14 15:30:18 +08:00
return *this;
}
2021-10-27 22:24:58 +08:00
/*
* IPv4: 127.0.0.1:2022
* IPv6: [fe80::1]:2022
*/
endpoint& as_is(const char* str_ep)
{
2021-10-29 17:54:49 +08:00
char addr_part[IN_MAX_ADDRSTRLEN];
2021-10-27 22:24:58 +08:00
if (str_ep[0] == '[')
{ // ipv6
2021-10-29 17:54:49 +08:00
const char* ip = str_ep + 1;
auto rbracket = strchr(ip, ']');
2021-10-27 22:24:58 +08:00
if (rbracket && rbracket[1] == ':')
{
2021-10-29 17:54:49 +08:00
auto zone_value = parse_in6_zone(ip, addr_part, sizeof(addr_part), rbracket);
2021-11-04 23:10:12 +08:00
as_in6(ip, static_cast<u_short>(atoi(rbracket + 2)), zone_value);
2021-10-27 22:24:58 +08:00
}
}
else
{ // ipv4
auto colon = strchr(str_ep, ':');
if (colon)
{
2021-10-29 17:54:49 +08:00
auto n = colon - str_ep;
memcpy(addr_part, str_ep, n);
addr_part[n] = '\0';
2021-11-04 23:10:12 +08:00
as_in4(addr_part, static_cast<u_short>(atoi(colon + 1)));
2021-10-27 22:24:58 +08:00
}
}
return *this;
}
2021-01-14 15:30:18 +08:00
endpoint& as_in(int family, const void* addr_in, u_short port)
{
this->zeroset();
this->af(family);
this->port(port);
switch (family)
2020-11-16 14:47:43 +08:00
{
2021-01-14 15:30:18 +08:00
case AF_INET:
::memcpy(&in4_.sin_addr, addr_in, sizeof(in_addr));
this->len(sizeof(sockaddr_in));
break;
case AF_INET6:
::memcpy(&in6_.sin6_addr, addr_in, sizeof(in6_addr));
this->len(sizeof(sockaddr_in6));
break;
}
return *this;
}
2021-10-29 17:54:49 +08:00
endpoint& as_in(const char* ip, unsigned short port)
2021-01-14 15:30:18 +08:00
{
this->zeroset();
2021-10-29 17:54:49 +08:00
if (strchr(ip, ':'))
2021-10-27 22:24:58 +08:00
{ // ipv6
2021-10-29 17:54:49 +08:00
char addr_part[IN_MAX_ADDRSTRLEN];
auto zone_value = parse_in6_zone(ip, addr_part, sizeof(addr_part), nullptr);
as_in6(ip, port, zone_value);
2020-11-16 14:47:43 +08:00
}
2021-01-14 15:30:18 +08:00
else
2021-10-27 22:24:58 +08:00
{ // ipv4
2021-10-29 17:54:49 +08:00
as_in4(ip, port);
2020-11-16 14:47:43 +08:00
}
2021-01-14 15:30:18 +08:00
return *this;
}
2021-10-29 17:54:49 +08:00
void as_in6(const char* ip, unsigned short port, unsigned int scope_id)
2021-10-27 22:24:58 +08:00
{
2021-10-29 17:54:49 +08:00
if (compat::inet_pton(AF_INET6, ip, &this->in6_.sin6_addr) == 1)
2021-10-27 22:24:58 +08:00
{
2021-10-29 17:54:49 +08:00
this->in6_.sin6_family = AF_INET6;
this->in6_.sin6_port = host_to_network(port);
this->in6_.sin6_scope_id = scope_id;
2021-10-27 22:24:58 +08:00
this->len(sizeof(sockaddr_in6));
}
}
2021-10-29 17:54:49 +08:00
void as_in4(const char* ip, unsigned short port)
2021-10-27 22:24:58 +08:00
{
2021-10-29 17:54:49 +08:00
if (compat::inet_pton(AF_INET, ip, &this->in4_.sin_addr) == 1)
2021-10-27 22:24:58 +08:00
{
this->in4_.sin_family = AF_INET;
this->in4_.sin_port = host_to_network(port);
this->len(sizeof(sockaddr_in));
}
}
2021-01-14 15:30:18 +08:00
endpoint& as_in(uint32_t addr, u_short port)
{
this->zeroset();
this->addr_v4(addr);
this->port(port);
return *this;
}
2020-11-16 14:47:43 +08:00
#if defined(YASIO_ENABLE_UDS) && YASIO__HAS_UDS
2021-01-14 15:30:18 +08:00
endpoint& as_un(const char* name)
{
int n = snprintf(un_.sun_path, sizeof(un_.sun_path) - 1, "%s", name);
if (n > 0)
2020-11-16 14:47:43 +08:00
{
2021-01-14 15:30:18 +08:00
un_.sun_family = AF_UNIX;
this->len(offsetof(struct sockaddr_un, sun_path) + n + 1);
2020-11-16 14:47:43 +08:00
}
2021-01-14 15:30:18 +08:00
else
2020-11-16 14:47:43 +08:00
{
2021-01-14 15:30:18 +08:00
un_.sun_family = AF_UNSPEC;
this->len(0);
2020-11-16 14:47:43 +08:00
}
2021-01-14 15:30:18 +08:00
return *this;
}
#endif
2020-11-16 14:47:43 +08:00
2021-01-14 15:30:18 +08:00
endpoint& as_is_raw(const void* ai_addr, size_t ai_addrlen)
{
this->zeroset();
::memcpy(this, ai_addr, ai_addrlen);
this->len(ai_addrlen);
return *this;
}
2020-11-16 14:47:43 +08:00
2021-01-14 15:30:18 +08:00
void zeroset() { ::memset(this, 0x0, sizeof(*this)); }
2020-11-16 14:47:43 +08:00
2021-11-04 23:10:12 +08:00
void af(int v) { sa_.sa_family = static_cast<u_short>(v); }
2021-01-14 15:30:18 +08:00
int af() const { return sa_.sa_family; }
void ip(const char* addr)
{
2021-10-27 22:24:58 +08:00
if (strchr(addr, ':'))
2021-01-14 15:30:18 +08:00
{ // ipv6
this->in6_.sin6_family = AF_INET6;
compat::inet_pton(AF_INET6, addr, &this->in6_.sin6_addr);
2021-10-25 15:21:18 +08:00
this->len(sizeof(sockaddr_in6));
2021-01-14 15:30:18 +08:00
}
2021-10-27 22:24:58 +08:00
else
{ // ipv4
this->in4_.sin_family = AF_INET;
compat::inet_pton(AF_INET, addr, &this->in4_.sin_addr);
this->len(sizeof(sockaddr_in));
}
2021-01-14 15:30:18 +08:00
}
2021-10-25 15:21:18 +08:00
std::string ip() const { return this->to_string(fmt_default | fmt_no_port); }
std::string to_string(int flags = fmt_default) const
2021-01-14 15:30:18 +08:00
{
2021-10-25 15:21:18 +08:00
std::string ret;
this->format_to(ret, flags);
return ret;
2021-01-14 15:30:18 +08:00
}
2020-11-16 14:47:43 +08:00
2021-10-27 22:24:58 +08:00
unsigned short port() const { return network_to_host(in4_.sin_port); }
void port(unsigned short value) { in4_.sin_port = host_to_network(value); }
2020-11-16 14:47:43 +08:00
2021-10-29 17:54:49 +08:00
// for ipv6 only
unsigned int scope_id() const { return af() == AF_INET6 ? static_cast<unsigned int>(this->in6_.sin6_scope_id) : 0u; }
void scope_id(unsigned int v)
{
if (af() == AF_INET6)
this->in6_.sin6_scope_id = v;
}
2021-10-26 13:26:36 +08:00
void addr_v4(uint32_t addr)
{
this->af(AF_INET);
2021-10-27 22:24:58 +08:00
in4_.sin_addr.s_addr = host_to_network(addr);
2021-10-26 13:26:36 +08:00
this->len(sizeof(sockaddr_in));
}
2021-10-27 22:24:58 +08:00
uint32_t addr_v4() const { return af() == AF_INET ? network_to_host(in4_.sin_addr.s_addr) : 0u; }
2021-10-25 15:21:18 +08:00
// check does endpoint is global address, not linklocal or loopback
bool is_global() const
{
switch (af())
2021-01-14 15:30:18 +08:00
{
case AF_INET:
2021-10-25 15:21:18 +08:00
return is_global_in4_addr(&in4_.sin_addr);
2021-01-14 15:30:18 +08:00
case AF_INET6:
2021-10-25 15:21:18 +08:00
return is_global_in6_addr(&in6_.sin6_addr);
2021-01-14 15:30:18 +08:00
}
2021-10-25 15:21:18 +08:00
return false;
}
2020-11-16 14:47:43 +08:00
2021-10-25 15:21:18 +08:00
void len(size_t n)
{
#if !YASIO__HAS_SA_LEN
len_ = static_cast<uint8_t>(n);
#else
sa_.sa_len = static_cast<uint8_t>(n);
#endif
}
socklen_t len() const
{
#if !YASIO__HAS_SA_LEN
return len_;
#else
return sa_.sa_len;
#endif
2021-01-14 15:30:18 +08:00
}
2020-11-16 14:47:43 +08:00
2021-10-25 15:21:18 +08:00
// format to buffer
size_t format_to(std::string& buf, int flags = 0) const
{
2021-10-29 17:54:49 +08:00
char str[endpoint::max_fmt_len];
size_t n = this->format_to(str, endpoint::max_fmt_len, flags);
2021-10-25 15:21:18 +08:00
if (n > 0)
{
buf.append(str, n);
return n;
}
return 0;
}
2020-11-16 14:47:43 +08:00
2021-10-25 15:21:18 +08:00
/* format to
*
* @params:
* buf: the buffer to output
2021-10-29 17:54:49 +08:00
* buf_len: the buffer len, must be at least endpoint::max_fmt_len
2021-10-25 15:21:18 +08:00
* @returns:
* the number of character written to the buf without null-termianted charactor
*
* @remark:
* The buffer result should be
* ipv4: xxx.xxx.xxx.xxx<:port> 127.0.0.1:2021
* ipv6: [xxx....xxx]<:port> [fe80::1]:53
*/
size_t format_to(char* buf, size_t buf_len, int flags) const
{
if (!(fmt_no_local & flags) || is_global())
{
size_t n = 0;
switch (af())
{
case AF_INET:
n = strlen(compat::inet_ntop(AF_INET, &in4_.sin_addr, buf, buf_len));
break;
case AF_INET6:
buf[n++] = '[';
n += strlen(compat::inet_ntop(AF_INET6, &in6_.sin6_addr, buf + n, buf_len - n));
buf[n++] = ']';
break;
#if defined(YASIO_ENABLE_UDS) && YASIO__HAS_UDS
case AF_UNIX:
if (!(flags & fmt_no_un_path))
{
n = (std::min)(static_cast<size_t>(this->len()), buf_len);
memcpy(buf, un_.sun_path, n);
}
return n;
#endif
}
if (n > 0)
{
if (!(flags & fmt_no_port))
{
u_short p = this->port();
if (!(flags & fmt_no_port_0) || p != 0)
n += sprintf(buf + n, ":%u", (unsigned int)p);
}
}
return n;
}
return 0;
}
2020-11-16 14:47:43 +08:00
2021-10-25 15:21:18 +08:00
/* format ipv4 only
2021-01-14 15:30:18 +08:00
%N: s_net 127
%H: s_host 0
%L: s_lh 0
%M: s_impno 1
%l: low byte of port
%h: high byte of port
*/
std::string format_v4(const char* foramt)
{
static const char* const _SIN_FORMATS[] = {"%N", "%H", "%L", "%M", "%l", "%h"};
2020-11-16 14:47:43 +08:00
2021-01-14 15:30:18 +08:00
unsigned char addr_bytes[sizeof(in4_.sin_addr.s_addr) + sizeof(u_short)];
memcpy(addr_bytes, &in4_.sin_addr.s_addr, sizeof(in4_.sin_addr.s_addr));
memcpy(addr_bytes + sizeof(in4_.sin_addr.s_addr), &in4_.sin_port, sizeof(in4_.sin_port));
2020-11-16 14:47:43 +08:00
2021-01-14 15:30:18 +08:00
char snum[sizeof("255")] = {0};
const size_t _N0 = sizeof("%N") - 1;
std::string s = foramt;
for (size_t idx = 0; idx < YASIO_ARRAYSIZE(_SIN_FORMATS); ++idx)
{
auto fmt = _SIN_FORMATS[idx];
auto offst = s.find(fmt);
if (offst != std::string::npos)
2020-11-16 14:47:43 +08:00
{
2021-01-14 15:30:18 +08:00
sprintf(snum, "%u", addr_bytes[idx]);
s.replace(offst, _N0, snum);
2020-11-16 14:47:43 +08:00
}
}
2021-01-14 15:30:18 +08:00
return s;
}
2020-11-16 14:47:43 +08:00
2021-10-26 13:26:36 +08:00
sockaddr* operator&() { return &sa_; }
const sockaddr* operator&() const { return &sa_; }
2021-01-14 15:30:18 +08:00
union {
sockaddr sa_;
sockaddr_in in4_;
sockaddr_in6 in6_;
2020-11-16 14:47:43 +08:00
#if defined(YASIO_ENABLE_UDS) && YASIO__HAS_UDS
2021-01-14 15:30:18 +08:00
sockaddr_un un_;
2020-11-16 14:47:43 +08:00
#endif
2021-01-14 15:30:18 +08:00
};
2020-11-16 14:47:43 +08:00
#if !YASIO__HAS_SA_LEN
2021-01-14 15:30:18 +08:00
uint8_t len_;
2020-11-16 14:47:43 +08:00
#endif
2021-10-29 17:54:49 +08:00
private:
unsigned int parse_in6_zone(const char*& ip, char* addr_part, size_t addr_part_maxn, const char* endptr)
{
unsigned int zone_value = 0;
auto zoneptr = strchr(ip, '%');
if (zoneptr)
{
#if defined(_WIN32)
zone_value = atoi(zoneptr + 1);
#else
zone_value = ::if_nametoindex(zoneptr + 1);
#endif
endptr = zoneptr;
}
if (endptr)
{
size_t n = (std::min)(static_cast<size_t>(endptr - ip), addr_part_maxn - 1);
memcpy(addr_part, ip, n);
addr_part[n] = '\0';
ip = addr_part;
}
return zone_value;
}
2021-01-14 15:30:18 +08:00
};
2020-11-16 14:47:43 +08:00
2021-01-14 15:30:18 +08:00
// supported internet protocol flags
enum : u_short
{
ipsv_unavailable = 0,
ipsv_ipv4 = 1,
ipsv_ipv6 = 2,
ipsv_dual_stack = ipsv_ipv4 | ipsv_ipv6,
};
2020-11-16 14:47:43 +08:00
} // namespace ip
#if !YASIO__HAS_NS_INLINE
using namespace yasio::inet::ip;
#endif
/*
** CLASS xxsocket: a posix socket wrapper
*/
2021-05-18 00:25:11 +08:00
class YASIO_API xxsocket {
2020-11-16 14:47:43 +08:00
public: /// portable connect APIs
// easy to connect a server ipv4 or ipv6 with local ip protocol version detect
// for support ipv6 ONLY network.
YASIO__DECL int xpconnect(const char* hostname, u_short port, u_short local_port = 0);
YASIO__DECL int xpconnect_n(const char* hostname, u_short port, const std::chrono::microseconds& wtimeout, u_short local_port = 0);
// easy to connect a server ipv4 or ipv6.
YASIO__DECL int pconnect(const char* hostname, u_short port, u_short local_port = 0);
YASIO__DECL int pconnect_n(const char* hostname, u_short port, const std::chrono::microseconds& wtimeout, u_short local_port = 0);
YASIO__DECL int pconnect_n(const char* hostname, u_short port, u_short local_port = 0);
// easy to connect a server ipv4 or ipv6.
YASIO__DECL int pconnect(const endpoint& ep, u_short local_port = 0);
YASIO__DECL int pconnect_n(const endpoint& ep, const std::chrono::microseconds& wtimeout, u_short local_port = 0);
YASIO__DECL int pconnect_n(const endpoint& ep, u_short local_port = 0);
// easy to create a tcp ipv4 or ipv6 server socket.
2021-01-14 15:30:18 +08:00
YASIO__DECL int pserve(const char* addr, u_short port);
YASIO__DECL int pserve(const endpoint& ep);
2020-11-16 14:47:43 +08:00
public:
// Construct a empty socket object
2021-01-14 15:30:18 +08:00
YASIO__DECL xxsocket();
2020-11-16 14:47:43 +08:00
// Construct with a exist socket handle
YASIO__DECL xxsocket(socket_native_type handle);
// Disable copy constructor
YASIO__DECL xxsocket(const xxsocket&) = delete;
// Construct with a exist socket, it will replace the source
YASIO__DECL xxsocket(xxsocket&&);
YASIO__DECL xxsocket& operator=(socket_native_type handle);
// Disable copy assign operator
YASIO__DECL xxsocket& operator=(const xxsocket&) = delete;
// Construct with a exist socket, it will replace the source
YASIO__DECL xxsocket& operator=(xxsocket&&);
// See also as function: open
YASIO__DECL xxsocket(int af, int type, int protocol);
2021-01-14 15:30:18 +08:00
YASIO__DECL ~xxsocket();
2020-11-16 14:47:43 +08:00
// swap with other when this fd is closed.
YASIO__DECL xxsocket& swap(xxsocket& who);
/* @brief: Open new socket
** @params:
** af : Usually is [AF_INET]
** type : [SOCK_STREAM-->TCP] and [SOCK_DGRAM-->UDP]
** protocol: Usually is [0]
** @returns: false: check reason by errno
*/
YASIO__DECL bool open(int af = AF_INET, int type = SOCK_STREAM, int protocol = 0);
YASIO__DECL bool reopen(int af = AF_INET, int type = SOCK_STREAM, int protocol = 0);
#ifdef _WIN32
YASIO__DECL bool open_ex(int af = AF_INET, int type = SOCK_STREAM, int protocol = 0);
YASIO__DECL static bool accept_ex(SOCKET sockfd_listened, SOCKET sockfd_prepared, PVOID lpOutputBuffer, DWORD dwReceiveDataLength, DWORD dwLocalAddressLength,
DWORD dwRemoteAddressLength, LPDWORD lpdwBytesReceived, LPOVERLAPPED lpOverlapped);
YASIO__DECL static bool connect_ex(SOCKET s, const struct sockaddr* name, int namelen, PVOID lpSendBuffer, DWORD dwSendDataLength, LPDWORD lpdwBytesSent,
LPOVERLAPPED lpOverlapped);
YASIO__DECL static void translate_sockaddrs(PVOID lpOutputBuffer, DWORD dwReceiveDataLength, DWORD dwLocalAddressLength, DWORD dwRemoteAddressLength,
sockaddr** LocalSockaddr, LPINT LocalSockaddrLength, sockaddr** RemoteSockaddr, LPINT RemoteSockaddrLength);
#endif
/** Is this socket opened **/
2021-01-14 15:30:18 +08:00
YASIO__DECL bool is_open() const;
2020-11-16 14:47:43 +08:00
/** Gets the socket fd value **/
2021-01-14 15:30:18 +08:00
YASIO__DECL socket_native_type native_handle() const;
2020-11-16 14:47:43 +08:00
2021-01-14 15:30:18 +08:00
/** Release ownership of the underlying native socket handle **/
YASIO__DECL socket_native_type release_handle();
2020-11-16 14:47:43 +08:00
/* @brief: Set this socket io mode to nonblocking
** @params:
**
** @returns: [0] succeed, otherwise, a value of SOCKET_ERROR is returned.
*/
YASIO__DECL int set_nonblocking(bool nonblocking) const;
YASIO__DECL static int set_nonblocking(socket_native_type s, bool nonblocking);
/* @brief: Test whether the socket has nonblocking flag
** @params:
**
** @returns: [1] yes. [0] no
** @pitfall: for wsock2, will return [-1] when it's a unconnected SOCK_STREAM
*/
YASIO__DECL int test_nonblocking() const;
YASIO__DECL static int test_nonblocking(socket_native_type s);
/* @brief: Associates a local address with this socket
** @params:
** addr: four point address, if set "0.0.0.0" ipv4, "::" ipv6, the socket will listen at
*any.
** port: @$$#s
** @returns:
** If no error occurs, bind returns [0]. Otherwise, it returns SOCKET_ERROR
*/
YASIO__DECL int bind(const char* addr, unsigned short port) const;
YASIO__DECL int bind(const endpoint&) const;
YASIO__DECL int bind_any(bool ipv6 = false) const;
/* @brief: Places this socket in a state in which it is listening for an incoming connection
** @params:
** Ommit
**
** @returns:
** If no error occurs, bind returns [0]. Otherwise, it returns SOCKET_ERROR
*/
YASIO__DECL int listen(int backlog = SOMAXCONN) const;
/* @brief: Permits an incoming connection attempt on this socket
** @returns:
** If no error occurs, accept returns a new socket on which
** the actual connection is made.
** Otherwise, a value of [invalid_socket] is returned
*/
2021-01-14 15:30:18 +08:00
YASIO__DECL xxsocket accept() const;
2020-11-16 14:47:43 +08:00
/* @brief: Permits an incoming connection attempt on this socket
** @params:
** @returns:
** If no error occurs, return 0, and the new_sock will be the actual connection is made.
** Otherwise, a EWOULDBLOCK,EAGAIN or other value is returned
*/
2021-01-14 15:30:18 +08:00
YASIO__DECL int accept_n(socket_native_type& new_sock) const;
2020-11-16 14:47:43 +08:00
/* @brief: Establishes a connection to a specified this socket
** @params:
** addr: Usually is a IPV4 address
** port: Server Listenning Port
**
** @returns:
** If no error occurs, returns [0].
** Otherwise, it returns SOCKET_ERROR
*/
YASIO__DECL int connect(const char* addr, u_short port);
YASIO__DECL int connect(const endpoint& ep);
YASIO__DECL static int connect(socket_native_type s, const char* addr, u_short port);
YASIO__DECL static int connect(socket_native_type s, const endpoint& ep);
/* @brief: Establishes a connection to a specified this socket with nonblocking
** @params:
** timeout: connection timeout, millseconds
**
** @returns: [0].succeed, [-1].failed
** @remark: Because on win32, there is no way to test whether the socket is non-blocking,
** so, after this function called, the socket will be always set to blocking mode.
*/
YASIO__DECL int connect_n(const char* addr, u_short port, const std::chrono::microseconds& wtimeout);
YASIO__DECL int connect_n(const endpoint& ep, const std::chrono::microseconds& wtimeout);
YASIO__DECL static int connect_n(socket_native_type s, const endpoint& ep, const std::chrono::microseconds& wtimeout);
/* @brief: Establishes a connection to a specified this socket with nonblocking
** @params:
**
** @returns: [0].succeed, [-1].failed
** @remark: this function will return immediately, for tcp, you should detect whether the
** handshake complete by handle_write_ready.
*/
YASIO__DECL int connect_n(const endpoint& ep);
YASIO__DECL static int connect_n(socket_native_type s, const endpoint& ep);
/* @brief: Disconnect a connectionless socket (such as SOCK_DGRAM)
**
*/
2021-01-14 15:30:18 +08:00
YASIO__DECL int disconnect() const;
2020-11-16 14:47:43 +08:00
YASIO__DECL static int disconnect(socket_native_type s);
/* @brief: nonblock send
** @params: omit
**
** @returns:
2021-01-14 15:30:18 +08:00
** If no error occurs, retval == len,
** Oterwise, If retval < len && not_recv_error(get_last_errno()), should close socket.
2020-11-16 14:47:43 +08:00
*/
YASIO__DECL int send_n(const void* buf, int len, const std::chrono::microseconds& wtimeout, int flags = 0);
YASIO__DECL static int send_n(socket_native_type s, const void* buf, int len, std::chrono::microseconds wtimeout, int flags = 0);
/* @brief: nonblock recv
** @params:
** The timeout is in microseconds
** @returns:
2021-01-14 15:30:18 +08:00
** If no error occurs, retval == len,
** Oterwise, If retval < len && not_recv_error(get_last_errno()), should close socket.
2020-11-16 14:47:43 +08:00
*/
YASIO__DECL int recv_n(void* buf, int len, const std::chrono::microseconds& wtimeout, int flags = 0) const;
YASIO__DECL static int recv_n(socket_native_type s, void* buf, int len, std::chrono::microseconds wtimeout, int flags = 0);
/* @brief: Sends data on this connected socket
** @params: omit
**
** @returns:
** If no error occurs, send returns the total number of bytes sent,
** which can be less than the number requested to be sent in the len parameter.
** Otherwise, a value of SOCKET_ERROR is returned.
*/
YASIO__DECL int send(const void* buf, int len, int flags = 0) const;
YASIO__DECL static int send(socket_native_type fd, const void* buf, int len, int flags = 0);
/* @brief: Receives data from this connected socket or a bound connectionless socket.
** @params: omit
**
** @returns:
** If no error occurs, recv returns the number of bytes received and
** the buffer pointed to by the buf parameter will contain this data received.
** If the connection has been gracefully closed, the return value is [0].
*/
YASIO__DECL int recv(void* buf, int len, int flags = 0) const;
YASIO__DECL static int recv(socket_native_type s, void* buf, int len, int flags);
/* @brief: Sends data on this connected socket
** @params: omit
**
** @returns:
** If no error occurs, send returns the total number of bytes sent,
** which can be less than the number requested to be sent in the len parameter.
** Otherwise, a value of SOCKET_ERROR is returned.
*/
YASIO__DECL int sendto(const void* buf, int len, const endpoint& to, int flags = 0) const;
/* @brief: Receives a datagram and stores the source address
** @params: omit
**
** @returns:
** If no error occurs, recv returns the number of bytes received and
** the buffer pointed to by the buf parameter will contain this data received.
** If the connection has been gracefully closed, the return value is [0].
*/
YASIO__DECL int recvfrom(void* buf, int len, endpoint& peer, int flags = 0) const;
YASIO__DECL int handle_write_ready(const std::chrono::microseconds& wtimeout) const;
YASIO__DECL static int handle_write_ready(socket_native_type s, const std::chrono::microseconds& wtimeout);
YASIO__DECL int handle_read_ready(const std::chrono::microseconds& wtimeout) const;
YASIO__DECL static int handle_read_ready(socket_native_type s, const std::chrono::microseconds& wtimeout);
/* @brief: Get local address info
** @params : None
**
** @returns:
*/
2021-01-14 15:30:18 +08:00
YASIO__DECL endpoint local_endpoint() const;
2020-11-16 14:47:43 +08:00
YASIO__DECL static endpoint local_endpoint(socket_native_type);
/* @brief: Get peer address info
** @params : None
**
** @returns:
* @remark: if this a listening socket fd, will return "0.0.0.0:0"
*/
2021-01-14 15:30:18 +08:00
YASIO__DECL endpoint peer_endpoint() const;
2020-11-16 14:47:43 +08:00
YASIO__DECL static endpoint peer_endpoint(socket_native_type);
/* @brief: Configure TCP keepalive
** @params : flag: 1.on, 0.off
** idle: time(secs) to send keepalive when no data interaction
** interval: keepalive send interval(secs)
** probes: count to try when no response
**
** @returns: [0].successfully
** [<0].one or more errors occured
*/
YASIO__DECL int set_keepalive(int flag = 1, int idle = 7200, int interval = 75, int probes = 10);
YASIO__DECL static int set_keepalive(socket_native_type s, int flag, int idle, int interval, int probes);
YASIO__DECL void reuse_address(bool reuse);
YASIO__DECL void exclusive_address(bool exclusive);
/* @brief: Sets the socket option
** @params :
** level: The level at which the option is defined (for example, SOL_SOCKET).
** optname: The socket option for which the value is to be set (for example, SO_BROADCAST).
** The optname parameter must be a socket option defined within the specified level,
** or behavior is undefined.
** optval: The option value.
** @examples:
** set_optval(SOL_SOCKET, SO_SNDBUF, 4096);
** set_optval(SOL_SOCKET, SO_RCVBUF, 4096);
**
** @remark: for more detail, please see:
** windows: https://docs.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-setsockopt
** linux: https://linux.die.net/man/3/setsockopt
**
** @returns: If no error occurs, set_optval returns zero. Otherwise, a value of SOCKET_ERROR is
** returned
*/
2021-10-25 15:21:18 +08:00
template <typename _Ty>
inline int set_optval(int level, int optname, const _Ty& optval)
{
return set_optval(this->fd, level, optname, optval);
}
template <typename _Ty>
inline static int set_optval(socket_native_type sockfd, int level, int optname, const _Ty& optval)
2020-11-16 14:47:43 +08:00
{
return set_optval(sockfd, level, optname, &optval, static_cast<socklen_t>(sizeof(_Ty)));
}
int set_optval(int level, int optname, const void* optval, socklen_t optlen) { return set_optval(this->fd, level, optname, optval, optlen); }
static int set_optval(socket_native_type sockfd, int level, int optname, const void* optval, socklen_t optlen)
{
return ::setsockopt(sockfd, level, optname, static_cast<const char*>(optval), optlen);
}
/* @brief: Retrieves a socket option
** @params :
** level: The level at which the option is defined. Example: SOL_SOCKET.
** optname: The socket option for which the value is to be retrieved.
** Example: SO_ACCEPTCONN. The optname value must be a socket option defined within the
** specified level, or behavior is undefined.
** optval: A variable to the buffer in which the value for the requested option is to be
** returned.
**
** @returns: If no error occurs, get_optval returns zero. Otherwise, a value of SOCKET_ERROR is
*returned
*/
2021-10-25 15:21:18 +08:00
template <typename _Ty>
inline _Ty get_optval(int level, int optname) const
2020-11-16 14:47:43 +08:00
{
_Ty optval = {};
get_optval(this->fd, level, optname, optval);
return optval;
}
2021-10-25 15:21:18 +08:00
template <typename _Ty>
inline int get_optval(int level, int optname, _Ty& optval) const
{
return get_optval(this->fd, level, optname, optval);
}
template <typename _Ty>
inline static int get_optval(socket_native_type sockfd, int level, int optname, _Ty& optval)
2020-11-16 14:47:43 +08:00
{
socklen_t optlen = static_cast<socklen_t>(sizeof(_Ty));
return get_optval(sockfd, level, optname, &optval, &optlen);
}
static int get_optval(socket_native_type sockfd, int level, int optname, void* optval, socklen_t* optlen)
{
return ::getsockopt(sockfd, level, optname, static_cast<char*>(optval), optlen);
}
/* @brief: control the socket
** @params :
** see MSDN or man page
** @returns: If no error occurs, ioctl returns zero. Otherwise, a value of SOCKET_ERROR is
** returned
**
**
**
*/
2021-10-25 15:21:18 +08:00
template <typename _Ty>
inline int ioctl(long cmd, const _Ty& value) const
{
return xxsocket::ioctl(this->fd, cmd, value);
}
template <typename _Ty>
inline static int ioctl(socket_native_type s, long cmd, const _Ty& value)
2020-11-16 14:47:43 +08:00
{
u_long argp = static_cast<u_long>(value);
return ::ioctlsocket(s, cmd, &argp);
}
/* @brief: wrapper system select, hide signal EINTR
** @params:
** s: the socket fd, it's different with system
** see MSDN or man page
** @returns: If no error occurs, returns >= 0. Otherwise, a value of -1 is
** returned
*/
2021-01-14 15:30:18 +08:00
int select(fd_set* readfds, fd_set* writefds, fd_set* exceptfds, const std::chrono::microseconds& wtimeout) const
{
return xxsocket::select(this->fd, readfds, writefds, exceptfds, wtimeout);
}
2020-11-16 14:47:43 +08:00
YASIO__DECL static int select(socket_native_type s, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, std::chrono::microseconds wtimeout);
/* @brief: Disables sends or receives on this socket
** @params:
** how: [SD_SEND] or [SD_RECEIVE] or [SD_BOTH]
**
** @returns: [0] succeed, otherwise, a value of SOCKET_ERROR is returned.
*/
YASIO__DECL int shutdown(int how = SD_BOTH) const;
/* @brief: close sends
** @params:
** shut_how: [SD_SEND] or [SD_RECEIVE] or [SD_BOTH] or [SD_NONE]
**
** @returns: [0] succeed, otherwise, a value of SOCKET_ERROR is returned.
*/
YASIO__DECL void close(int shut_how = SD_BOTH);
/* @brief: Retrive tcp socket rtt in microseconds
** @params:
** non
**
** @returns: [0] succeed, otherwise, a value of SOCKET_ERROR is returned.
*/
2021-05-18 00:25:11 +08:00
YASIO__DECL unsigned int tcp_rtt() const;
YASIO__DECL static unsigned int tcp_rtt(socket_native_type s);
2020-11-16 14:47:43 +08:00
2021-01-14 15:30:18 +08:00
YASIO__DECL operator socket_native_type() const;
2020-11-16 14:47:43 +08:00
/// <summary>
/// this function just for windows platform
/// </summary>
2021-01-14 15:30:18 +08:00
YASIO__DECL static void init_ws32_lib();
2020-11-16 14:47:43 +08:00
2021-01-14 15:30:18 +08:00
YASIO__DECL static int get_last_errno();
2020-11-16 14:47:43 +08:00
YASIO__DECL static void set_last_errno(int error);
2021-01-14 15:30:18 +08:00
YASIO__DECL static bool not_send_error(int error);
YASIO__DECL static bool not_recv_error(int error);
2020-11-16 14:47:43 +08:00
YASIO__DECL static const char* strerror(int error);
YASIO__DECL static const char* gai_strerror(int error);
/// <summary>
/// Resolve all as ipv4 or ipv6 endpoints
/// </summary>
YASIO__DECL static int resolve(std::vector<endpoint>& endpoints, const char* hostname, unsigned short port = 0, int socktype = SOCK_STREAM);
/// <summary>
/// Resolve as ipv4 address only.
/// </summary>
YASIO__DECL static int resolve_v4(std::vector<endpoint>& endpoints, const char* hostname, unsigned short port = 0, int socktype = SOCK_STREAM);
/// <summary>
/// Resolve as ipv6 address only.
/// </summary>
YASIO__DECL static int resolve_v6(std::vector<endpoint>& endpoints, const char* hostname, unsigned short port = 0, int socktype = SOCK_STREAM);
/// <summary>
/// Resolve as ipv4 address only and convert to V4MAPPED format.
/// </summary>
YASIO__DECL static int resolve_v4to6(std::vector<endpoint>& endpoints, const char* hostname, unsigned short port = 0, int socktype = SOCK_STREAM);
/// <summary>
/// Force resolve all addres to ipv6 endpoints, IP4 with AI_V4MAPPED
/// </summary>
YASIO__DECL static int resolve_tov6(std::vector<endpoint>& endpoints, const char* hostname, unsigned short port = 0, int socktype = SOCK_STREAM);
/// <summary>
/// Resolve as ipv4 or ipv6 endpoints with callback
/// </summary>
template <typename _Fty>
inline static int resolve_i(const _Fty& callback, const char* hostname, unsigned short port = 0, int af = 0, int flags = 0, int socktype = SOCK_STREAM)
{
addrinfo hint;
memset(&hint, 0x0, sizeof(hint));
hint.ai_flags = flags;
hint.ai_family = af;
hint.ai_socktype = socktype;
addrinfo* answerlist = nullptr;
char buffer[sizeof "65535"] = {'\0'};
const char* service = nullptr;
if (port > 0)
{
sprintf(buffer, "%u", port); // It's enough for unsigned short, so use sprintf ok.
service = buffer;
}
int error = getaddrinfo(hostname, service, &hint, &answerlist);
if (nullptr == answerlist)
return error;
for (auto ai = answerlist; ai != nullptr; ai = ai->ai_next)
{
if (ai->ai_family == AF_INET6 || ai->ai_family == AF_INET)
{
if (callback(endpoint(ai)))
break;
}
}
freeaddrinfo(answerlist);
return error;
}
/*
** @brief:: Gets supported internet protocol versions of localhost
** @returns:
** ipsv_unavailable: The network unavailable.
** ipsv_ipv4: Support ipv4 only.
** ipsv_ipv6: Support ipv6 only.
** ipsv_dual_stack:
** Support ipv4 or ipv6, but for multi network adapters device, you should always
** use ipv4 preferred, such as smart phone with wifi & cellular network. The smart phone's os
** will choose wifi when it is available to avoid consume user's cash, when the cellular
*support
** ipv6/ipv4 but the wifi only support ipv4, then use ipv6 will cause network issue.
** For more detail, see: https://github.com/halx99/yasio/issues/130
*/
2021-01-14 15:30:18 +08:00
YASIO__DECL static int getipsv();
2020-11-16 14:47:43 +08:00
/*
** @brief: Traverse local device network adapter address with valid ip
** @params:
** handler: prototype is [](const ip::endpoint& ep)->bool
*/
YASIO__DECL static void traverse_local_address(std::function<bool(const ip::endpoint&)> handler);
protected:
YASIO__DECL static void reregister_descriptor(socket_native_type s, fd_set* fds);
private:
socket_native_type fd;
}; // namespace inet
} // namespace inet
2021-06-14 22:54:50 +08:00
#if !YASIO__HAS_NS_INLINE
using namespace yasio::inet;
#endif
2020-11-16 14:47:43 +08:00
} // namespace yasio
namespace std
{ // VS2013 the operator must be at namespace std
inline bool operator<(const yasio::inet::ip::endpoint& lhs, const yasio::inet::ip::endpoint& rhs)
{ // apply operator < to operands
if (lhs.af() == AF_INET)
return (static_cast<uint64_t>(lhs.in4_.sin_addr.s_addr) + lhs.in4_.sin_port) < (static_cast<uint64_t>(rhs.in4_.sin_addr.s_addr) + rhs.in4_.sin_port);
return ::memcmp(&lhs, &rhs, sizeof(rhs)) < 0;
}
inline bool operator==(const yasio::inet::ip::endpoint& lhs, const yasio::inet::ip::endpoint& rhs)
{ // apply operator == to operands
return !(lhs < rhs) && !(rhs < lhs);
}
} // namespace std
#if defined(_MSC_VER)
# pragma warning(pop)
#endif
#if defined(YASIO_HEADER_ONLY)
# include "yasio/xxsocket.cpp" // lgtm [cpp/include-non-header]
#endif
#endif