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
|
|
|
|
**
|
2021-11-05 15:42:16 +08:00
|
|
|
** @returns: > 0 succeed, 0 failed.
|
2020-11-16 14:47:43 +08:00
|
|
|
*/
|
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
|