axmol/external/yasio/xxsocket.hpp

1215 lines
40 KiB
C++
Raw Normal View History

2020-11-16 14:47:43 +08:00
//////////////////////////////////////////////////////////////////////////////////////////
// A cross platform socket APIs, support ios & android & wp8 & window store universal app
//
//////////////////////////////////////////////////////////////////////////////////////////
/*
The MIT License (MIT)
Copyright (c) 2012-2020 HALX99
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>
#include "yasio/detail/config.hpp"
2020-12-15 18:58:04 +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
#ifdef _WIN32
# if !defined(WIN32_LEAN_AND_MEAN)
# define WIN32_LEAN_AND_MEAN
# endif
# include <WinSock2.h>
# include <Windows.h>
# if defined(_WIN32) && !defined(_WINSTORE)
# include <Mswsock.h>
# include <Mstcpip.h>
# endif
# include <Ws2tcpip.h>
# if defined(YASIO_NT_COMPAT_GAI)
# include <Wspiapi.h>
# endif
# if YASIO__HAS_UDS
# include <afunix.h>
# endif
typedef SOCKET socket_native_type;
typedef int socklen_t;
# define poll WSAPoll
# pragma comment(lib, "ws2_32.lib")
# undef gai_strerror
#else
# include <unistd.h>
# include <signal.h>
# include <sys/ioctl.h>
# include <netdb.h>
# include <sys/types.h>
# include <sys/poll.h>
# if defined(__linux__)
# include <sys/epoll.h>
# endif
# include <sys/select.h>
# include <sys/socket.h>
# include <sys/un.h>
# include <netinet/in.h>
# include <netinet/tcp.h>
# include <net/if.h>
# include <arpa/inet.h>
# if !defined(SD_RECEIVE)
# define SD_RECEIVE SHUT_RD
# endif
# if !defined(SD_SEND)
# define SD_SEND SHUT_WR
# endif
# if !defined(SD_BOTH)
# define SD_BOTH SHUT_RDWR
# endif
# if !defined(closesocket)
# define closesocket close
# endif
# if !defined(ioctlsocket)
# define ioctlsocket ioctl
# endif
# if defined(__linux__)
# define SO_NOSIGPIPE MSG_NOSIGNAL
# endif
typedef int socket_native_type;
# undef socket
#endif
#define SD_NONE -1
#include <fcntl.h> // common platform header
// redefine socket error code for posix api
#ifdef _WIN32
# undef EWOULDBLOCK
# undef EINPROGRESS
# undef EALREADY
# undef ENOTSOCK
# undef EDESTADDRREQ
# undef EMSGSIZE
# undef EPROTOTYPE
# undef ENOPROTOOPT
# undef EPROTONOSUPPORT
# undef ESOCKTNOSUPPORT
# undef EOPNOTSUPP
# undef EPFNOSUPPORT
# undef EAFNOSUPPORT
# undef EADDRINUSE
# undef EADDRNOTAVAIL
# undef ENETDOWN
# undef ENETUNREACH
# undef ENETRESET
# undef ECONNABORTED
# undef ECONNRESET
# undef ENOBUFS
# undef EISCONN
# undef ENOTCONN
# undef ESHUTDOWN
# undef ETOOMANYREFS
# undef ETIMEDOUT
# undef ECONNREFUSED
# undef ELOOP
# undef ENAMETOOLONG
# undef EHOSTDOWN
# undef EHOSTUNREACH
# undef ENOTEMPTY
# undef EPROCLIM
# undef EUSERS
# undef EDQUOT
# undef ESTALE
# undef EREMOTE
# undef EBADF
# undef EFAULT
# undef EAGAIN
# define EWOULDBLOCK WSAEWOULDBLOCK
# define EINPROGRESS WSAEINPROGRESS
# define EALREADY WSAEALREADY
# define ENOTSOCK WSAENOTSOCK
# define EDESTADDRREQ WSAEDESTADDRREQ
# define EMSGSIZE WSAEMSGSIZE
# define EPROTOTYPE WSAEPROTOTYPE
# define ENOPROTOOPT WSAENOPROTOOPT
# define EPROTONOSUPPORT WSAEPROTONOSUPPORT
# define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT
# define EOPNOTSUPP WSAEOPNOTSUPP
# define EPFNOSUPPORT WSAEPFNOSUPPORT
# define EAFNOSUPPORT WSAEAFNOSUPPORT
# define EADDRINUSE WSAEADDRINUSE
# define EADDRNOTAVAIL WSAEADDRNOTAVAIL
# define ENETDOWN WSAENETDOWN
# define ENETUNREACH WSAENETUNREACH
# define ENETRESET WSAENETRESET
# define ECONNABORTED WSAECONNABORTED
# define ECONNRESET WSAECONNRESET
# define ENOBUFS WSAENOBUFS
# define EISCONN WSAEISCONN
# define ENOTCONN WSAENOTCONN
# define ESHUTDOWN WSAESHUTDOWN
# define ETOOMANYREFS WSAETOOMANYREFS
# define ETIMEDOUT WSAETIMEDOUT
# define ECONNREFUSED WSAECONNREFUSED
# define ELOOP WSAELOOP
# define ENAMETOOLONG WSAENAMETOOLONG
# define EHOSTDOWN WSAEHOSTDOWN
# define EHOSTUNREACH WSAEHOSTUNREACH
# define ENOTEMPTY WSAENOTEMPTY
# define EPROCLIM WSAEPROCLIM
# define EUSERS WSAEUSERS
# define EDQUOT WSAEDQUOT
# define ESTALE WSAESTALE
# define EREMOTE WSAEREMOTE
# define EBADF WSAEBADF
# define EFAULT WSAEFAULT
# define EAGAIN WSATRY_AGAIN
#endif
#if !defined(MAXNS)
# define MAXNS 3
#endif
#define IN_MAX_ADDRSTRLEN INET6_ADDRSTRLEN
#if !defined(_WS2IPDEF_)
inline bool IN4_IS_ADDR_LOOPBACK(const in_addr* a)
{
return ((a->s_addr & 0xff) == 0x7f); // 127/8
}
inline bool IN4_IS_ADDR_LINKLOCAL(const in_addr* a)
{
return ((a->s_addr & 0xffff) == 0xfea9); // 169.254/16
}
inline bool IN6_IS_ADDR_GLOBAL(const in6_addr* a)
{
//
// Check the format prefix and exclude addresses
// whose high 4 bits are all zero or all one.
// This is a cheap way of excluding v4-compatible,
// v4-mapped, loopback, multicast, link-local, site-local.
//
unsigned int High = (a->s6_addr[0] & 0xf0);
return ((High != 0) && (High != 0xf0));
}
#endif
// shoulde close connection condition when retval of recv < 0
2020-11-24 16:52:03 +08:00
#define YASIO__RECV_FAIL(ec) ((ec) != EAGAIN && (ec) != EWOULDBLOCK && (ec) != EINTR)
2020-11-16 14:47:43 +08:00
// shoulde close connection condition when retval of send < 0
2020-11-24 16:52:03 +08:00
#define YASIO__SEND_FAIL(ec) ((ec) != EAGAIN && (ec) != EWOULDBLOCK && (ec) != EINTR && (ec) != ENOBUFS && (ec) != EPERM)
2020-11-16 14:47:43 +08:00
#define YASIO_ADDR_ANY(af) (af == AF_INET ? "0.0.0.0" : "::")
namespace yasio
{
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;
YASIO__NS_INLINE namespace ip
{
#pragma pack(push, 1)
// 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)
namespace compat
{
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);
} // namespace compat
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); };
struct endpoint {
public:
endpoint(void) { 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); }
explicit endpoint(const char* addr, unsigned short port = 0) { as_in(addr, port); }
explicit endpoint(uint32_t addr, unsigned short port = 0) { as_in(addr, port); }
endpoint(int family, const void* addr, unsigned short port = 0) { as_in(family, addr, port); }
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)
{
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)
{
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;
#if defined(YASIO_ENABLE_UDS) && YASIO__HAS_UDS
case AF_UNIX:
as_un(((sockaddr_un*)addr)->sun_path);
break;
#endif
}
return *this;
}
endpoint& as_in(int family, const void* addr_in, u_short port)
{
this->zeroset();
this->af(family);
this->port(port);
switch (family)
{
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;
}
endpoint& as_in(const char* addr, unsigned short port)
{
this->zeroset();
/*
* Windows XP no inet_pton or inet_ntop
*/
if (strchr(addr, ':') == nullptr)
{ // ipv4
if (compat::inet_pton(AF_INET, addr, &this->in4_.sin_addr) == 1)
{
this->in4_.sin_family = AF_INET;
this->in4_.sin_port = htons(port);
this->len(sizeof(sockaddr_in));
}
}
else
{ // ipv6
if (compat::inet_pton(AF_INET6, addr, &this->in6_.sin6_addr) == 1)
{
this->in6_.sin6_family = AF_INET6;
this->in6_.sin6_port = htons(port);
this->len(sizeof(sockaddr_in6));
}
}
return *this;
}
endpoint& as_in(uint32_t addr, u_short port)
{
this->zeroset();
this->af(AF_INET);
this->addr_v4(addr);
this->port(port);
this->len(sizeof(sockaddr_in));
return *this;
}
#if defined(YASIO_ENABLE_UDS) && YASIO__HAS_UDS
endpoint& as_un(const char* name)
{
int n = snprintf(un_.sun_path, sizeof(un_.sun_path) - 1, "%s", name);
if (n > 0)
{
un_.sun_family = AF_UNIX;
this->len(offsetof(struct sockaddr_un, sun_path) + n + 1);
}
else
{
un_.sun_family = AF_UNSPEC;
this->len(0);
}
return *this;
}
#endif
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;
}
void zeroset() { ::memset(this, 0x0, sizeof(*this)); }
void af(int v) { sa_.sa_family = v; }
int af() const { return sa_.sa_family; }
void ip(const char* addr)
{
/*
* Windows XP no inet_pton or inet_ntop
*/
if (strchr(addr, ':') == nullptr)
{ // ipv4
this->in4_.sin_family = AF_INET;
compat::inet_pton(AF_INET, addr, &this->in4_.sin_addr);
}
else
{ // ipv6
this->in6_.sin6_family = AF_INET6;
compat::inet_pton(AF_INET6, addr, &this->in6_.sin6_addr);
}
}
std::string ip() const
{
std::string ipstring(IN_MAX_ADDRSTRLEN - 1, '\0');
auto str = inaddr_to_string(
&ipstring.front(), [](const in_addr*) { return true; }, [](const in6_addr*) { return true; });
ipstring.resize(str ? strlen(str) : 0);
return ipstring;
}
// to_string with port, can simply add prefix "http::" or "https://" for url
std::string to_string() const
{
std::string addr(IN_MAX_ADDRSTRLEN + sizeof("65535") + 2, '[');
size_t n = 0;
switch (sa_.sa_family)
{
case AF_INET:
n = strlen(compat::inet_ntop(AF_INET, &in4_.sin_addr, &addr.front(), static_cast<socklen_t>(addr.length())));
n += sprintf(&addr.front() + n, ":%u", this->port());
break;
case AF_INET6:
n = strlen(compat::inet_ntop(AF_INET6, &in6_.sin6_addr, &addr.front() + 1, static_cast<socklen_t>(addr.length() - 1)));
n += sprintf(&addr.front() + n, "]:%u", this->port());
break;
#if defined(YASIO_ENABLE_UDS) && YASIO__HAS_UDS
case AF_UNIX:
n = this->len();
addr.assign(un_.sun_path, n);
break;
#endif
}
addr.resize(n);
return addr;
}
unsigned short port(void) const { return ntohs(in4_.sin_port); }
void port(unsigned short value) { in4_.sin_port = htons(value); }
void addr_v4(uint32_t addr) { in4_.sin_addr.s_addr = htonl(addr); }
uint32_t addr_v4() const { return ntohl(in4_.sin_addr.s_addr); }
/*
%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"};
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));
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)
{
sprintf(snum, "%u", addr_bytes[idx]);
s.replace(offst, _N0, snum);
}
}
return s;
}
YASIO_OBSOLETE_DEPRECATE(endpoint::format_v4)
std::string to_strf_v4(const char* format) { return format_v4(format); }
// in_addr(ip) to string with pred
template <typename _Pred4, typename _Pred6> const char* inaddr_to_string(char* str /*[IN_MAX_ADDRSTRLEN]*/, _Pred4&& pred4, _Pred6&& pred6) const
{
switch (af())
{
case AF_INET:
if (pred4(&in4_.sin_addr))
return compat::inet_ntop(AF_INET, &in4_.sin_addr, str, INET_ADDRSTRLEN);
break;
case AF_INET6:
if (pred6(&in6_.sin6_addr))
return compat::inet_ntop(AF_INET6, &in6_.sin6_addr, str, INET6_ADDRSTRLEN);
break;
}
return nullptr;
}
// in_addr(ip) to csv without loopback or linklocal address
void inaddr_to_csv_nl(std::string& csv)
{
char str[INET6_ADDRSTRLEN] = {0};
if (inaddr_to_string(str, is_global_in4_addr, is_global_in6_addr))
{
csv += str;
csv += ',';
}
}
// the in_addr(from sockaddr) to csv string helper function without loopback or linklocal
// address
static void inaddr_to_csv_nl(const sockaddr* addr, std::string& csv) { endpoint(addr).inaddr_to_csv_nl(csv); }
// the in_addr/in6_addr to csv string helper function without loopback or linklocal address
// the inaddr should be union of in_addr,in6_addr or ensure it's memory enough when
// family=AF_INET6
static void inaddr_to_csv_nl(int family, const void* inaddr, std::string& csv) { endpoint(family, inaddr).inaddr_to_csv_nl(csv); }
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
}
union {
sockaddr sa_;
sockaddr_in in4_;
sockaddr_in6 in6_;
#if defined(YASIO_ENABLE_UDS) && YASIO__HAS_UDS
sockaddr_un un_;
#endif
};
#if !YASIO__HAS_SA_LEN
uint8_t len_;
#endif
};
// supported internet protocol flags
enum : u_short
{
ipsv_unavailable = 0,
ipsv_ipv4 = 1,
ipsv_ipv6 = 2,
ipsv_dual_stack = ipsv_ipv4 | ipsv_ipv6,
};
} // namespace ip
#if !YASIO__HAS_NS_INLINE
using namespace yasio::inet::ip;
#endif
/*
** CLASS xxsocket: a posix socket wrapper
*/
class xxsocket {
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.
YASIO__DECL int pserv(const char* addr, u_short port);
public:
// Construct a empty socket object
YASIO__DECL xxsocket(void);
// 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);
YASIO__DECL ~xxsocket(void);
// 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 **/
YASIO__DECL bool is_open(void) const;
/** Gets the socket fd value **/
YASIO__DECL socket_native_type native_handle(void) const;
YASIO__DECL socket_native_type detach(void);
/* @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
** @params:
** addrlen: Usually is [sizeof (sockaddr)]
**
** @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
*/
YASIO__DECL xxsocket accept(socklen_t addrlen = sizeof(sockaddr));
/* @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
*/
YASIO__DECL int accept_n(socket_native_type& new_sock);
/* @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)
**
*/
YASIO__DECL int disconnect();
YASIO__DECL static int disconnect(socket_native_type s);
/* @brief: nonblock send
** @params: omit
**
** @returns:
** If no error occurs, send returns the total number of bytes sent,
** Oterwise, If retval <=0, mean error occured, and should close socket.
*/
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:
** If no error occurs, send returns the total number of bytes recvived,
** Oterwise, If retval <=0, mean error occured, and should close socket.
*/
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:
*/
YASIO__DECL endpoint local_endpoint(void) const;
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"
*/
YASIO__DECL endpoint peer_endpoint(void) const;
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
*/
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)
{
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
*/
template <typename _Ty> inline _Ty get_optval(int level, int optname) const
{
_Ty optval = {};
get_optval(this->fd, level, optname, optval);
return optval;
}
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)
{
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
**
**
**
*/
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)
{
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
*/
YASIO__DECL static int select(socket_native_type s, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, std::chrono::microseconds wtimeout);
/* @brief: check is a client socket alive
** @params :
** see MSDN or man page
** @returns: If no error occurs, alive returns true. Otherwise, a value of false is
** returned
*/
YASIO__DECL bool alive(void) const;
/* @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.
*/
YASIO__DECL uint32_t tcp_rtt() const;
YASIO__DECL static uint32_t tcp_rtt(socket_native_type s);
YASIO__DECL operator socket_native_type(void) const;
/// <summary>
/// this function just for windows platform
/// </summary>
YASIO__DECL static void init_ws32_lib(void);
YASIO__DECL static int get_last_errno(void);
YASIO__DECL static void set_last_errno(int error);
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
*/
YASIO__DECL static int getipsv(void);
/*
** @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
namespace net = inet;
} // 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