GIT repositories

Index page of all the GIT repositories that are clonable form this server via HTTPS. Übersichtsseite aller GIT-Repositories, die von diesem Server aus über git clone (HTTPS) erreichbar sind.

Services

A bunch of service scripts to convert, analyse and generate data. Ein paar Services zum Konvertieren, Analysieren und Generieren von Daten.

GNU octave web interface

A web interface for GNU Octave, which allows to run scientific calculations from netbooks, tables or smartphones. The interface provides a web form generator for Octave script parameters with pre-validation, automatic script list generation, as well presenting of output text, figures and files in a output HTML page. Ein Webinterface für GNU-Octave, mit dem wissenschaftliche Berechnungen von Netbooks, Tablets oder Smartphones aus durchgeführt werden können. Die Schnittstelle beinhaltet einen Formulargenerator für Octave-Scriptparameter, mit Einheiten und Einfabevalidierung. Textausgabe, Abbildungen und generierte Dateien werden abgefangen und in einer HTML-Seite dem Nutzer als Ergebnis zur Verfügung gestellt.

C++ Klassen-Templates für TCP/UDP Sockets

C++ TCP/UDP socket class templates

Die hier beschriebenen Klassen-Templates (mit Spezialisierungen) dienen dazu, TCP, UDP, und mit der Klasse basic_socket<..> auch andererweitig Socket-Kommunikation zu implementieren. Nur diese Headerdatei und die Threading-Klasse sw::thread in thread.hh (hier) wird benötigt, es gibt keine Bibliothek oder C++ Implementierugnsdatei. Beim Linken müssen -lpthread (Linux/UNIX/Mac) bzw. ws2_32.lib (Windows) eingebunden werden. So, die Klassen in dieser Datei socket.hh sind:

  • sw::sock::ip_address (template sw::templates::basic_ip_address<..>): ist eine Klasse, mit der IP-Addressen und deren Binärdaten behandelt werden. Sie enthält Konvertierungen von und zu string, Host-Lookup, Service-Lookup (d.h. Ports für Servicenamen wie "http", "ftp", usw. und umgekehrt), und weitere Hilfsfunktionen/-methoden. IP V4 und V6 ist implementiert. Durch Ableiten können weitere Typen/Familien hinzugefügt werden.

  • sw::templates::basic_socket<typename Address_type, unsigned Timeout=100>: Die Basis aller folgenden Socketklassen. Hier werden in erster Linie die Unterschiede der verschiedenen Plattformen differenziert und die "Basics" für Sockets zur Verfügung gestellt. Der Socket wird im Destruktor automatisch geschlossen. Anhand der IP-Addresse werden IPV4 oder V6/dual stack Sockets erstellt. Andere Arten wie Bluetooth können durch Ableiten behandelt werden.

  • sw::sock::udp_socket (template sw::templates::basic_udp_socket<..>): Spezialisiert basic_socket für UDP-Verwendung und schränkt den Zugriff auf Methoden, die für UDP nicht zutreffend sind, ein (z.B. listen(), connect(), send(), recv() machen bei UDP keinen Sinn und werden daher private überladen). Mit der Methode create() wird ein neuer UDP socket initialisiert, ohne ihn an einen Port zu binden. Mit bind() wird ein neuer UDP socket erstellt und direkt an eine IP/Port gebunden (d.h. create() ist bei bind() implizit dabei). Die Pakete werden mit sendto() verschickt und recvfrom() empfangen. Dabei ist die IP Adresse ein ip_address-Objekt. Um auf neue Daten zu warten kann wait(bool *rx, bool *tx, timout) verwendet werden. Diese Methode ist ein Wrapper für ::poll() bzw. ::select().

  • sw::sock::tcp_socket (template sw::templates::basic_tcp_socket<..>): Basis für alle TCP folgenden TCP Sockets: client, server und acceptor. Hier werden UDP-Methoden ausgeblendet und auch Threading hinzugefügt, d.h. TCP Sockets laufen in einem eigenen Thread. wait() muss hier nicht mehr explizit aufgerufen werden, sondern überladene "Callback-Methoden" übernehmen diese Aufgabe.

  • sw::sock::tcp_client (template sw::templates::basic_tcp_client<..>): ist von tcp_socket abgeleitet und blendet Server-Methoden aus.

  • sw::sock::tcp_acceptor (template sw::templates::basic_tcp_acceptor<..>): Leider ist mir kein besserer Begriff als "acceptor" eingefallen, ist das serverseitige Gegenstück zum Client. Objekte dieser Klasse übernehmen die eigentliche "Payload-Kommunikation" mit dem Client und werden vom Server dynamisch erstellt und implizit mit ::accept() zugewiesen.

  • sw::sock::tcp_server<...>: Der Server-Socket selbst dient nur der Annahme von eingehenden Verbindungen (listen). Für jede neue Verbindung wird vom Server einen neuen Acceptor erstellt und zugewiesen (::accept()). tcp_server ist immer ein Template und erhält als Typ die jeweilig verwendete Akzeptorklasse.

Bei allen Klassen wurde bewußt auf die Verwendung von Exceptions verzichtet, in der Thread-Funktion der Thread-Klasse (siehe hier) werden lediglich unbehandelte Exceptions abgefangen, um den Thread danach sicher zu schließen und dessen Resourcen freizugeben. Ansonsten werden Bool'sche Rückgabewerte verwendet, um auf Fehler hinzudeuten, und mit error() und error_text() können die Fehlerdetails geprüft werden. Eine Erneuerung auf c++11 steht noch aus.

The class templates described here are useful for implementing TCP/UDP (and other) socket communication. There are no c++ implementation file dependencies, only this file socket.hh and the threading class sw::thread in thread.hh (here) are required. Link with: -lpthread (Linux/UNIX/Mac), ws2_32.lib (Windows). Alright, the class templates / specialisations in this file are:

  • sw::sock::ip_address (template sw::templates::basic_ip_address<..>): a class for IP address handling. It contains conversions from and to string, host lookup, service lookup (e.g. port for "http", "ftp" etc), and other auxiliary functions/methods. IP V4 and V6 is implemented, other types/families can be added by deriving this class.

  • sw::templates::basic_socket<typename Address_type, unsigned Timeout=100>: The base class for all subsequent socket classes. The major purpose of it is to handle and homogenise the differences of the supported platforms, and wrap the basic socket functionality of the "low level" socket functions. Depending on the IP address type, it automatically creates IPV4 or V6/dual stack sockets. Other socket types, such as Bluetooth, can be handled by deriving from this class.

  • sw::sock::udp_socket (template sw::templates::basic_udp_socket<..>): Specialised basic_socket for UDP usage, and restricts the access to methods that are not applicable for UDP sockets (e.g. listen(), connect(), send(), recv()). With the method create() you initialise a new UDP socket without binding it to an IP/port. With bind(), a new socket is created and bound (note: other than the low level function, this bind implicitly creates a socket). Packets are sent and received using sendto() and recvfrom(), where the IP address (,which has always to be specified,) is an ip_address object for convenience. To wait for received data use the wait(bool *rx, bool *tx, timout) methods, which is basically a wrapper around ::poll() (::select() on Windows).

  • sw::sock::tcp_socket (template sw::templates::basic_tcp_socket<..>): Base class for all subsequent TCP sockets: client, server and acceptor. Here the UDP methods are masked private, and threading is added to this class. Instead of explicitly calling the basic_socket's wait(), there are callback methods to determine when communication takes place (on_connected(), on_closed(), on_sent(), on_received(), etc.).

  • sw::sock::tcp_client (template sw::templates::basic_tcp_client<..>): Is derived from tcp_socket and simply masks server methods like bind() and accept().

  • sw::sock::tcp_acceptor (template sw::templates::basic_tcp_acceptor<..>): The "acceptor" (unfortunately I didn't find a better term), is the server side socket that actually transceives payload from/to the client. The server class dynamically creates acceptors for the individual client connection requests.

  • sw::sock::tcp_server<...>: The server socket itself is only listening, and accepting / redirecting incoming connections to individually instantiated acceptor objects. The overloadable callback methods differ from the client, instead of void on_connected() there is bool on_request(...), where you can also refuse to accept connection requests.

The implementation of all these classes is intentionally based on Boolean success values rather than on thowing exceptions. Except in the thread function of the threading class (see hier), no exceptions are caught. The Boolean return values indicate errors, and the details can be fetched with error() and error_text(). C++11 refurbishment pending.

Dateien

Files

socket.hh thread.hh Examples: tcp_client.cc tcp_server.cc udp_server.cc socket_ip_address.cc

(Examples at the end of this page)

Quelltext

Source code

/**
 * @package de.atwillys.cc.swl
 * @license BSD (simplified)
 * @author Stefan Wilhelm (stfwi)
 *
 * @socket.hh
 * @ccflags
 * @ldflags -lpthread
 * @platform linux, bsd, windows
 * @standard >= c++98
 *
 * -----------------------------------------------------------------------------
 *
 * Template based socket handling, contains classes that you can extend to implement:
 *
 *  - TCP sockets:
 *      - class sw::sock::tcp_client    : Client class, connects to servers
 *      - class sw::sock::tcp_acceptor  : Server class dealing with the requests
 *      - template class sw::sock::tcp_server<acceptor_type>: Listening server
 *
 *  - UDP sockets:
 *      - class sw::sock::udp_socket    : UDP socket class
 *
 * All you actually have to to is to overload callback methods. The rest of the socket
 * handling is pretty much done in the base classes.
 *
 * REQUIRES header file "sw/thread.hh"
 *
 * -----------------------------------------------------------------------------
 * +++ BSD license header +++
 * Copyright (c) 2008-2014, Stefan Wilhelm (stfwi, <cerbero s@atwilly s.de>)
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met: (1) Redistributions
 * of source code must retain the above copyright notice, this list of conditions
 * and the following disclaimer. (2) Redistributions in binary form must reproduce
 * the above copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the distribution.
 * (3) Neither the name of atwillys.de nor the names of its contributors may be
 * used to endorse or promote products derived from this software without specific
 * prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS
 * AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 * -----------------------------------------------------------------------------
 */
#ifndef SW_SOCKET_HH
#define SW_SOCKET_HH
 
// <editor-fold desc="includes" defaultstate="collapsed">
#ifdef __MSC_VER
#ifndef OS_WIN
#define OS_WIN
#endif
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
typedef unsigned long sa_family_t;
#define swsck SOCKET
#define swsck_inv (INVALID_SOCKET)
#define swsck_err (SOCKET_ERROR)
#define swsck_eno ((long)(::WSAGetLastError()))
#define swsck_eintr (WSAEINTR)
#define swsck_nexist (WSAENOTSOCK)
#define swsck_timeout (WSAETIMEDOUT)
#define swsck_eagain (WSAEWOULDBLOCK)
#define swsck_default_timeout (1000)
typedef int socklen_t;
typedef unsigned long sa_family_t;
typedef USHORT in_port_t;
#define MSG_DONTWAIT 0
namespace sw { namespace detail {
template <typename wserr_t>
class winsock_init {
public:
  winsock_init() { err = ::WSAStartup(MAKEWORD(2, 2), &wsadata); }
  virtual ~winsock_init() { ::WSACleanup(); }
  typename wserr_t err;
  WSADATA wsadata;
  static winsock_init _wsinit;
};
//template <typename wserr_t> winsock_init<wserr_t>::_wsinit;
}}
namespace sw { namespace sock {
  typedef sw::detail::winsock_init<int> winsock_initializer;
}}
#else
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <sys/poll.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
#include <cstdlib>
#define swsck int
#define swsck_inv (-1)
#define swsck_err (-1)
#define swsck_eno (errno)
#define swsck_eintr (EINTR)
#define swsck_nexist (ENOTSOCK)
#define swsck_timeout (ETIMEDOUT)
#define swsck_eagain (EWOULDBLOCK)
#define swsck_default_timeout (100)
#endif
#include "thread.hh"
#include <string>
#include <sstream>
#include <algorithm>
#include <exception>
#include <limits>
#include <vector>
 
#ifdef SOCK_DEBUG
#include <cstdio>
#include <stdarg.h>
template <typename char_t> static void sck_ctrace(const char_t* ptrn, ...)
{ va_list args; va_start(args, ptrn); vfprintf(stderr, ptrn, args);
  fprintf(stderr, "\n"); fflush(stderr); va_end(args); }
#else
template <typename char_t> static inline void sck_ctrace(const char_t*, ...) {;}
#endif
#undef ctrace
#define ctrace sck_ctrace
#undef ctracem
#define ctracem sck_ctrace
// </editor-fold>
 
// <editor-fold desc="basic_ip_address" defaultstate="collapsed">
/**
 * class basic_ip_address
 * Organizes basic operations with IP addresses, ports,
 * cast from and to inet_addr and string. Parses addresses with ports, e.g.
 * http://127.0.0.1, ftp://localhost, 192.168.0.1:23, google.de:80
 */
namespace sw { namespace detail {
template <typename char_type=char>
class basic_ip_address
{
public:
 
  typedef sockaddr_storage address_type;  // socket storage
  typedef socklen_t addresslen_type;      // socket data length
  typedef in_port_t port_type;            // inet port type
  typedef char_type char_t;               // string character type
  typedef std::string str_t;              // string type
  typedef typename str_t::size_type sz_t; // string size type
 
  /**
   * Socket family type abstraction, values correspond to AF_UNSPEC/AF_INET/AF_INET6
   */
  enum family_sel { family_any=AF_UNSPEC, family_ipv4=AF_INET, family_ipv6=AF_INET6 };
 
  /**
   * Socket type type abstraction, values correspond to SOCK_STREAM/SOCK_DGRAM.
   */
  enum type_sel   { type_any=0, type_tcp=SOCK_STREAM, type_udp=SOCK_DGRAM };
 
  /**
   * Intermediate storage data type for address lookups.
   */
  struct hostlookup_data_t {
    str_t saddr, shost; enum family_sel family; addresslen_type size; address_type adr;
  };
  /**
   * Intermediate list type for address lookups.
   */
  typedef std::vector<hostlookup_data_t> hostlookup_list_t;
 
public:
 
  /**
   * Standard constructor, initial invalid address
   **/
  inline basic_ip_address() : size_(0), mask_(0)
  { clear(); }
 
  /**
   * Constructor (copy constructor)
   **/
  inline basic_ip_address(const basic_ip_address & ip) : address_(ip.address_), size_(ip.size_),
          mask_(ip.mask_)
  { ; }
 
  /**
   * Constructor from local byte order address and port.
   * Note: Only IPV6 and IPV6 addresses are assigned, other address families have to be
   *       explicitly set using the methods address(const address_type&) and size(socklen_t).
   **/
  inline basic_ip_address(address_type adr, port_type port) : address_(adr), size_(0), mask_(0)
  {
    if(address_.ss_family == AF_INET6) size_ = sizeof(struct sockaddr_in6);
    if(address_.ss_family == AF_INET) size_ = sizeof(struct sockaddr_in);
    this->port(port);
  }
 
  /**
   * Constructor from dotted string, optional with port ( "127.0.0.1:21" or "127.0.0.1")
   * If no port is defined, the port is unchanged = 0
   **/
  basic_ip_address(const char *s)
  { str(s); }
 
  /**
   * Constructor from dotted string, optional with port ( "127.0.0.1:21" or "127.0.0.1")
   * If no port is defined, the port is unchanged = 0
   **/
  basic_ip_address(const str_t & s)
  { str(s); }
 
  /**
   * Constructor from ip v4 inet address type
   **/
  basic_ip_address(struct sockaddr_in sa)
  { operator =(sa); }
 
  /**
   * Constructor from ip v6 inet address type
   **/
  basic_ip_address(struct sockaddr_in6 sa)
  { operator =(sa); }
 
  /**
   * Destructor
   **/
  virtual ~basic_ip_address()
  { }
 
public:
 
  /**
   * Invalidates the address
   */
  inline void clear()
  { memset(&address_, 0, sizeof(address_type)); mask_=0; size_=0; }
 
  /**
   * Returns if the address is set
   */
  inline bool valid() const throw ()
  { return address_.ss_family != 0 && size_ != 0; }
 
  /**
   * Returns if the currently stored address is IP V4
   * @return bool
   */
  inline bool is_v4() const throw()
  { return address_.ss_family == AF_INET; }
 
  /**
   * Returns if the currently stored address is IP V6
   * @return bool
   */
  inline bool is_v6() const throw()
  { return address_.ss_family == AF_INET6; }
 
  /**
   * Returns the address (storage type)
   */
  inline const address_type & address() const throw ()
  { return address_; }
 
  /**
   * Clears the object and sets the address (storage type). You have to set the data size
   * yourself if the family is not AF_INET or AF_INET6, otherwise the object is !valid().
   **/
  void address(const address_type & adr) throw ()
  {
    clear();
    if(adr.ss_family == AF_INET) size_ = sizeof(sockaddr_in);
    else if(adr.ss_family == AF_INET6) size_ = sizeof(sockaddr_in6);
    address_ = adr;
  }
 
  /**
   * Rreturns the address family
   **/
  inline sa_family_t family() const throw ()
  { return address_.ss_family; }
 
  /**
   * Sets the address family. Most assignment methods of this object set the family implicitly.
   * This function is required when defining addresses that are not AF_INET or AF_INET6.
   **/
  inline void family(sa_family_t family__) throw ()
  { address_.ss_family = family__; }
 
  /**
   * Returns the data size of the address structure dependent on the family set
   * (sockaddr_in or sockaddr_in6).
   * @return socklen_t
   */
  socklen_t size() const throw()
  { return size_; }
 
  /**
   * Sets the data size of the address structure (sockaddr_in, sockaddr_in6 ...).
   * Note: If you set it, it must be correct. Subsequent operations rely on it.
   * @return socklen_t
   */
  inline void size(socklen_t sz) throw()
  { size_ = sz; }
 
  /**
   * Returns the port (only for AF_INET or AF_INET6, otherwise 0) or 0 if not set yet.
   */
  port_type port() const throw ()
  {
    if(address_.ss_family == AF_INET) return ntohs(((sockaddr_in*)&address_)->sin_port);
    if(address_.ss_family == AF_INET6) return ntohs(((sockaddr_in6*)&address_)->sin6_port);
    return 0;
  }
 
  /**
   * Sets the port (only for AF_INET or AF_INET6). For other families you must set the
   * port directly in the corresponding sockaddr_### structure and assign the storage type
   * to this address.
   */
  bool port(port_type port__) throw ()
  {
    if(address_.ss_family == AF_INET)  {
      ((sockaddr_in*)&address_)->sin_port = htons(port__);
      return true;
    } else if(address_.ss_family == AF_INET6) {
      ((sockaddr_in6*)&address_)->sin6_port = htons(port__);
      return true;
    }
    return false;
  }
 
  /**
   * Set the subnet mask bit count, e.g. 24 for a /24 network.
   * Note: Has currently only informational purpose and has no effect in any methods.
   * @param unsigned char msk
   * @return bool
   */
  bool subnet_mask(unsigned char msk) throw()
  {
    mask_ = 0;
    if(address_.ss_family == AF_INET && msk > 32) return false;
    if(address_.ss_family == AF_INET6 && msk > 128) return false;
    mask_ = msk;
    return true;
  }
 
  /**
   * Returns the subnet mask bit count (e.g. 24 for a /24 network), which may be set explicitly
   * or parsed from string assignments.
   * Note: Has currently only informational purpose and has no effect in any methods.
   * @return unsigned char
   */
  inline unsigned char subnet_mask() const throw()
  { return mask_; }
 
  /**
   * String representation of the address (without port, service etc - only the plain address)
   * @return str_t
   */
  str_t str() const
  {
    char s[INET6_ADDRSTRLEN+2];
    s[sizeof(s)-1] = '\0';
    if(address_.ss_family == AF_INET6) {
      return !::inet_ntop(AF_INET6, &(((struct sockaddr_in6*)&address_)->sin6_addr), s, sizeof(s)) ? str_t() : s;
    } else if(address_.ss_family == AF_INET) {
      return !::inet_ntop(AF_INET, &(((struct sockaddr_in*)&address_)->sin_addr), s, sizeof(s)) ? str_t() : s;
    } else {
      return str_t();
    }
  }
 
  /**
   * Set resource path with ip, port and path from string.
   * Note: This method does not perform host lookups.
   * - 192.168.0.1    (v4 address)
   * - a::bcd0        (v6 address)
   * - 127.0.0.1:1234 (v4 address : port)
   * - [::1]:1234     (v6 address : port)
   * - [::1/64]:1234  (v6 address / mask : port)
   */
  bool str(str_t adr)
  {
    clear();
    if(adr.length() < 2) return false; // shortest: "::"
    str_t port, subn;
    sz_t p;
    if((p = adr.find('[')) != str_t::npos) { // [ v6 address </subn?> ] <:port?>
      sz_t p1 = adr.find(']');
      if(p != 0 || p1==str_t::npos || p1 < p) return false; // if [ is must be at the beginning
      if(p1 > 0 && (p1 < adr.length()-1)) {
        port = adr.substr(p1+1); // port may be empty or still contain ":"
      }
      adr.resize(p1);
      adr = adr.substr(p+1); // [ ] stripped, address may contain net mask
    } else if(adr.find(']') != str_t::npos) {
      return false; // ] without [
    }
    if((p = adr.find('/')) != str_t::npos) { // <v4/v6 address> </subn?> <:port?>
      if(p >= adr.length()-1) return false; // / is last character
      subn = adr.substr(p+1);
      sz_t p1 = subn.find(':');
      if(p1 != str_t::npos) {
        if(!port.empty()) return false; // [address/sub:port]:port not allowed
        if(p1==0 || p1 >= subn.length()-1) return false; // : is first or last char
        port = subn.substr(p1+1);
        subn.resize(p1);
      }
      adr.resize(p);  // <v4/v6 address>
    } else if((p=adr.find(':')) != str_t::npos) { // check if v4 with port
      if(adr.rfind(':') == p) { // only one ":" ->v4 is in the string (v6 has at least 2)
        if(!port.empty()) return false; // not possible
        if(p==0 || p >= subn.length()-1) return false; // : is first or last char
        port = adr.substr(p+1); // port may be empty or still contain :
        adr.resize(p);
      }
    }
    if(!port.empty() && port[0] == ':') { // Remove potential leading colon
      port = port.substr(1);
    }
    if(!port.empty() && (unsigned)std::count_if(port.begin(), port.end(), ::isdigit) < port.length()) {
      return false; // port contains non-digits
    } else if(subn.length() > 3) {
      return false; // even v6 subnet spec has only 3 digits
    } else  if(!subn.empty() && (unsigned)std::count_if(subn.begin(),subn.end(),::isdigit)<subn.length()) {
      return false; // subnet contains non-digits
    }
    // adr is now a plain v4, v6 or invalid address
    if(adr.find(':') != str_t::npos) {
      // assume v6
      struct sockaddr_in6 addr;
      memset(&addr, 0, sizeof(struct sockaddr_in6));
      addr.sin6_family = AF_INET6;
      addr.sin6_port = htons(0);
      if(::inet_pton(AF_INET6, adr.c_str(), &(addr.sin6_addr)) != 1) return false;
      size_ = sizeof(struct sockaddr_in6);
      address_.ss_family = AF_INET6;
      ((struct sockaddr_in6*)&address_)->sin6_addr = addr.sin6_addr;
      //((struct sockaddr_in6)&address_)->sin6_scope_id = ;
      //((struct sockaddr_in6)&address_)->sin6_flowinfo = ;
    } else {
      // assume v4
      struct sockaddr_in addr;
      memset(&addr, 0, sizeof(struct sockaddr_in));
      addr.sin_family = AF_INET;
      addr.sin_port = htons(0);
      if(::inet_pton(AF_INET, adr.c_str(), &(addr.sin_addr)) != 1) return false;
      size_ = sizeof(struct sockaddr_in);
      address_.ss_family = AF_INET;
      ((struct sockaddr_in*)&address_)->sin_addr = addr.sin_addr;
    }
    if(!subn.empty() && !subnet_mask((unsigned char)(::atol(subn.c_str()) & 0xff))) return false;
    if(!port.empty()) basic_ip_address::port((port_type)(::atol(port.c_str()))); // port check, only digits
    return true;
  }
 
public:
 
  /**
   * Copy assignment
   * @param const basic_ip_address & ip
   */
  inline void operator=(const basic_ip_address & ip) throw ()
  { address_ = ip.address_; size_ = ip.size_; mask_ = ip.mask_; }
 
  /**
   * IPV4 address copy assignment. Does not modify port or mask.
   * @param const sockaddr_in & adr
   */
  inline void operator=(const sockaddr_in & adr) throw ()
  { *((sockaddr_in*)&address_)=adr; address_.ss_family=AF_INET; size_=sizeof(struct sockaddr_in); }
 
  /**
   * IPV6 address copy assignment. Does not modify port or mask.
   * @param const sockaddr_in6 & adr
   */
  inline void operator=(const sockaddr_in6 & adr) throw ()
  { *((sockaddr_in6*)&address_)=adr; address_.ss_family=AF_INET6; size_=sizeof(struct sockaddr_in6);}
 
  /**
   * String assignment
   * @param const str_t & ip
   */
  inline void operator=(const str_t & ip)
  { str(ip); }
 
  /**
   * Sets new address from C string (optional with port). e.g. "127.0.0.1" or "127.0.0.1:21" are valid arguments
   * @param const char* ip
   **/
  inline void operator=(const char* ip)
  { str(ip); }
 
  /**
   * Type cast to sockaddr_in structure.
   * Note: If the address is not IPV4, these data is not valid. There is no conversion.
   * @return const struct sockaddr_in & ()
   **/
  operator const struct sockaddr_in & () const throw ()
  { return *((sockaddr_in*) &address_); };
 
  /**
   * Type cast to sockaddr_in6 structure
   * Note: If the address is not IPV6, these data is not valid. There is no conversion.
   * @return const struct sockaddr_in6 &
   **/
  operator const struct sockaddr_in6 & () const throw ()
  { return *((sockaddr_in6*) &address_); };
 
public:
 
  /**
   * Parse an address and additional data from a resource uri, e.g.
   *
   * - http://user@host:port/path,
   * - ftp://[::1]/path
   * - user@192.168.0.1
   *
   * First argument is your input string, second the address, the rest are the parsed output chunks.
   * Returns success.
   *
   * Note: This is a function intended to be used for ipv4/ipv6 clients.
   * Note: This function is not suitable to be used with subnet mask specifications (e.g. ab::c:d/64)
   *
   * @param str_t res_addr
   * @param basic_ip_address & ip
   * @param str_t & service
   * @param str_t & user
   * @param str_t & host
   * @param str_t & path
   * @param bool lookup_host=false
   * @param enum family_sel family=family_any
   * @param enum type_sel type=type_tcp
   * @return bool
   */
  static bool parse_resource_address(str_t res_addr, basic_ip_address & ip,
    str_t & service, str_t & user, str_t & host, str_t & path, bool lookup_host=false,
    enum family_sel family=family_any, enum type_sel type=type_tcp)
  {
    for(str_t::const_iterator it = res_addr.begin(); it != res_addr.end(); ++it) {
      if(!isprint(*it)) return false;
    }
    sz_t pss, ps;
    if(res_addr.empty()) return false;
    pss = res_addr.find("://"); ps = res_addr.find('/');
    if(pss != 0 && pss != str_t::npos && ps > pss) {
      service = res_addr.substr(0, pss);
      res_addr = res_addr.substr(pss+3);
    }
    if((ps = res_addr.find('/')) != str_t::npos) {
      if(ps == 0) return false; // only path or generally invalid, could cause an exception
      path = res_addr.substr(ps);
      res_addr.resize(res_addr.length() - path.length());
    }
    if((pss = res_addr.find('@')) != str_t::npos) {
      if(pss == 0) return false; // @ first char --> empty user
      user = res_addr.substr(0, pss);
      res_addr = res_addr.substr(pss+1);
    }
    str_t port;
    if(res_addr.find('[') != str_t::npos) {
      if((pss = res_addr.find("]:")) != str_t::npos) {
        if(pss >= res_addr.length()-2) return false;
        port = res_addr.substr(pss+2);
        res_addr.resize(pss+1); // including the ]
        if(std::count_if(port.begin(), port.end(), ::isdigit) < (int)port.length()) return false;
      }
    } else {
      if((pss = res_addr.find(":")) != str_t::npos) {
        if(pss >= res_addr.length()-1) return false;
        port = res_addr.substr(pss+1);
        res_addr.resize(pss);
        if(std::count_if(port.begin(), port.end(), ::isdigit) < (int)port.length()) return false;
      }
    }
    host = res_addr;
    ip.str(res_addr);
    if(!ip.valid() && lookup_host) {
      hostlookup_list_t lst = host_lookup(res_addr, service, family, type);
      for(typename hostlookup_list_t::const_iterator it=lst.begin(); it!=lst.end(); ++it) {
        if(it->family == family_ipv4) {
          ip = *((struct sockaddr_in*)&it->adr);
          break;
        } else if(it->family == family_ipv6) {
          ip = *((struct sockaddr_in6*)&it->adr);
          break;
        } else {
          continue; // Currently other families not handled
        }
      }
    }
    if(port.length() > 0) ip.port((port_type)(::atol(port.c_str())));
    if(ip.port() == 0 && !service.empty()) ip.port(port_by_service(service));
    return false;
  }
 
  /**
   * Performs a canonical name host lookup using ::getaddrinfo and returns a vector of the
   * resulting data.
   * @param host
   * @param service
   * @param family
   * @param type
   * @return hostlookup_list_t
   */
  static hostlookup_list_t host_lookup(str_t host, str_t service=str_t(),
          enum family_sel family=family_any, enum type_sel type=type_any)
  {
    hostlookup_list_t lst;
    if(host.empty()) return lst;
    struct addrinfo *ai=0, *aii=0, hints;
    char str_addr[INET6_ADDRSTRLEN+1];
    memset(str_addr, 0, sizeof(str_addr));
    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_family = family;
    hints.ai_socktype = type;
    hints.ai_flags = AI_CANONNAME;
    if(::getaddrinfo(host.c_str(), (service.empty() ? NULL : service.c_str() ), &hints, &aii)
    || !aii) return lst;
    unsigned n_entries = 0;
    for(ai=aii; ai; ai=ai->ai_next) ++n_entries;
    if(n_entries > 0) {
      try {
        lst.reserve(n_entries);
        for(ai=aii; ai; ai=ai->ai_next) {
          str_addr[0] = 0;
          struct hostlookup_data_t data;
          if(!ai->ai_addrlen || ai->ai_addrlen > sizeof(address_type)) continue;
          if(ai->ai_family == AF_INET6) {
            if(ai->ai_addr->sa_family != AF_INET6 || ai->ai_addrlen != sizeof(struct sockaddr_in6)
            || !::inet_ntop(AF_INET6, &((struct sockaddr_in6*)ai->ai_addr)->sin6_addr, str_addr,
               ai->ai_addrlen)) continue;
          } else if(ai->ai_family == AF_INET) {
            if(ai->ai_addr->sa_family != AF_INET || ai->ai_addrlen != sizeof(struct sockaddr_in)
            || !::inet_ntop(AF_INET, &((struct sockaddr_in*)ai->ai_addr)->sin_addr, str_addr,
               ai->ai_addrlen)) continue;
          } else {
            continue;
          }
          data.saddr = str_addr;
          data.family = (enum family_sel)ai->ai_family;
          data.size = ai->ai_addrlen;
          data.shost = ai->ai_canonname ? ai->ai_canonname : "";
          memcpy(&data.adr, ai->ai_addr, ai->ai_addrlen);
          lst.push_back(data);
        }
      } catch(const std::exception & e) {
        if(aii) freeaddrinfo(aii);
        throw e;
      } catch(...) {
        ctrace("basic_ip_address::host_lookup(): unexpected exception");
        lst.clear();
      }
    }
    if(aii) freeaddrinfo(aii);
    return lst;
  }
 
  /**
   * Returns a host name from a given address or an empty string if not found.
   * @param const basic_ip_address & ip
   * @return str_t
   */
  static str_t hostname_by_address(const basic_ip_address & ip)
  {
    struct hostent *sh = 0;
    if(ip.family() == AF_INET) {
      sh = ::gethostbyaddr(&((struct sockaddr_in const*)&ip.address())->sin_addr,
              sizeof(struct sockaddr_in), AF_INET);
    } else if(ip.family() == AF_INET6) {
      sh = ::gethostbyaddr(&((struct sockaddr_in6 const*)&ip.address())->sin6_addr,
              sizeof(struct sockaddr_in6), AF_INET6);
    }
    return (!sh || !sh->h_name) ? str_t() : str_t(sh->h_name);
  }
 
  /**
   * Returns a service name from a given port, such as "ftp", "https".
   * @return
   */
  static str_t service_by_port(port_type port__) throw ()
  {
    struct servent* sa = ::getservbyport(htons(port__), NULL);
    return (!sa || !sa->s_name) ? str_t() : str_t(sa->s_name);
  }
 
  /**
   * Returns the port number for a service specification, such as "ftp", "https".
   * @param const str_t & service
   * @return port_type
   */
  static port_type port_by_service(const str_t & service__) throw ()
  {
    struct servent* sa = ::getservbyname(service__.c_str(), NULL);
    return (!sa || !sa->s_name) ? 0 : ntohs(sa->s_port);
  }
 
  /**
   * Returns the own host name
   **/
  static str_t my_hostname()
  {
    char s[128]; s[sizeof(s)-1] = '\0';
    return (::gethostname(s, sizeof(s)-1) < 0) ? str_t() : str_t(s);
  }
 
private:
 
  address_type address_;
  addresslen_type size_;
  unsigned char mask_;
};
}}
// </editor-fold>
 
// <editor-fold desc="basic_socket" defaultstate="collapsed">
/**
 * class basic_socket
 * Basic platform independent socket wrapper with small changes:
 * - On object construction, a socket (id/handle) is not directly opened.
 *   Use the create() method to open the socket.
 * - Method connected() returns if the socket is ok.
 * - recv() reads until (method argument) buffer is full, all data of socket
 *   buffer read or error.
 * - send() sends until all data sent or error.
 * - automatically close() on destruct (if not closed()
 * - sets socket in nonblocking mode in listen() and accept()
 * - methods return bool instead of socket error types (because socket handle/id
 *   is a instance variable). Errors are saved in a instance variable and can be
 *   read using error().
 * - Method wait() can be used to wait for data (FD_SET ...) with timeout.
 */
namespace sw { namespace detail {
template <typename Address_type=basic_ip_address<>, unsigned Timeout=100>
class basic_socket {
public:
 
  typedef Address_type ip_address_t;
  typedef std::string str_t;
  typedef char char_t;
 
public:
 
  /**
   * Standard constructor. Returns closed/uninitialized socket.
   **/
  basic_socket() throw () : id_(swsck_inv), errno_(0), timeout_(Timeout),
    checksent_(false), listens_(false)
  { ; }
 
  /**
   * Constrictor from socket identifier/handle in win32.
   **/
  basic_socket(swsck id) throw () : id_(id), errno_(0), timeout_(Timeout),
    checksent_(false), listens_(false)
  { ; }
 
  /**
   * Constrictor from ip address
   **/
  basic_socket(const ip_address_t & adr) throw () : id_(swsck_inv), ip_(adr),
  errno_(0), timeout_(Timeout), checksent_(false), listens_(false)
  { ; }
 
  /**
   * Constrictor from socket identifier/handle in win32 and ip address
   **/
  basic_socket(swsck id, const ip_address_t & adr) throw () : id_(id), ip_(adr),
  errno_(0), timeout_(Timeout), checksent_(false), listens_(false)
  { ; }
 
  /**
   * Constrictor basic_ip_address string definition
   **/
  basic_socket(const char* adr) throw () :
  id_(swsck_inv), ip_(adr), errno_(0), timeout_(Timeout),
  checksent_(false), listens_(false)
  { ; }
 
  /**
   * Destructor, closes the socket if it is not yet closed.
   **/
  virtual ~basic_socket()
  {
    ctracem("basic_socket::~basic_socket()");
    basic_socket::close();
  }
 
public:
 
  /**
   * Returns if the socket is closed
   * @return bool
   **/
  inline bool closed() const throw ()
  { return id_ == swsck_inv; }
 
  /**
   * Returns the socket id
   * @return swsck
   */
  inline swsck socket_id() const throw()
  { return id_; }
 
  /**
   * Returns the basic_ip_address
   * @return const ip_address_t &
   **/
  inline const ip_address_t & ip() const
  { return ip_; }
 
  /**
   * Sets the new basic_ip_address
   * @param const ip_address_t & adr
   **/
  inline void ip(const ip_address_t & adr)
  { ip_ = adr; }
 
  /**
   * Sets the new basic_ip_address
   * @param const char* adr
   **/
  inline void ip(const char* adr)
  { if(adr) ip_ = adr; }
 
  /**
   * Returns the current socket error.
   * @return long
   **/
  inline long error() const throw ()
  { return errno_; }
 
 /**
   * Sets the new socket error
   * The error instance variable is mutable, so the method can be const.
   * This is required because some const methods must set the error.
   * @param long newerror
   **/
  inline void error(long newerror)
  {
    ctracem("basic_socket::error(SET)");
    errno_ = newerror;
    if (newerror == -1) return;
    if (newerror == 0) { errtext_ = ""; return; }
    errtext_ = syserr_text(newerror);
  }
 
  /**
   * Returns a string representation of the error
   * @return const str_t &
   */
  inline const str_t & error_text() const
  { return errtext_; }
 
  /**
   * Set an error text
   * @param const char* text
   */
  inline void error_text(const char* text, bool overwrite=false)
  {
    if(!overwrite && !errtext_.empty()) return;
    errno_ = -1; errtext_ = text;
  }
 
  /**
   * Get actual timeout value in ms for wait method
   * @return unsigned
   **/
  inline unsigned timeout_ms() const throw ()
  { return timeout_; }
 
  /**
   * Set timeout value in ms for wait method
   * @param unsigned newtimeout
   **/
  inline void timeout_ms(unsigned newtimeout) throw ()
  { timeout_ = newtimeout; }
 
  /**
   * Get a socket option
   * @param int level
   * @param int optname
   */
  int option(int level, int optname)
  {
    int i=0;
    socklen_t size = sizeof(i);
    if (::getsockopt(id_, level, optname, (void*) &i, &size) == swsck_err) error(swsck_eno);
    ctracem("basic_socket::option(lvl=%d, opt=%d) = %d", level, optname, i);
    return i;
  }
 
  /**
   * Set a socket option
   * @param int level
   * @param int optname
   * @param int newval
   */
  void option(int level, int optname, int newval)
  {
    ctracem("basic_socket::option(lvl=%d, opt=%d, val=%d)", level, optname, newval);
    int i = newval;
    #ifndef OS_WIN
    analyze_error(::setsockopt(id_, level, optname, (void*) &i, (socklen_t) sizeof(i)));
    #else
    analyze_error(::setsockopt(id_, level, optname, (const char*) &i, (socklen_t) sizeof(i)));
    #endif
  }
 
  /**
   * Get socket send buffer size
   * @return int
   */
  inline int option_sendbuffer_size()
  { return option(SOL_SOCKET, SO_SNDBUF); }
 
  /**
   * Get socket receive buffer size
   * @return int
   */
  inline int option_recvbuffer_size()
  { return option(SOL_SOCKET, SO_RCVBUF); }
 
  /**
   * Returns if the socket is in error state. Automatically resets error.
   * @return bool
   */
  inline bool option_error()
  { if (option(SOL_SOCKET, SO_ERROR) != 0) { close(); return true; } return false; }
 
  /**
   * Set if the socket can send with delay
   * @param bool enabled
   */
  inline void nodelay(bool enabled)
  { option(IPPROTO_TCP, TCP_NODELAY, enabled ? 1 : 0); }
 
  /**
   * Get if the socket can send with delay
   * @return bool
   */
  inline bool nodelay()
  { return option(IPPROTO_TCP, TCP_NODELAY) != 0; }
 
  /**
   * Set if socket automatically sends keep alive packets
   * @param bool enabled
   */
  inline void keepalive(bool enabled)
  { option(SOL_SOCKET, SO_KEEPALIVE, enabled ? 1 : 0); }
 
  /**
   * Get if socket automatically sends keep alive packets
   * @return bool
   */
  inline bool keepalive()
  { return option(SOL_SOCKET, SO_KEEPALIVE) != 0; }
 
  /**
   * Set if socket can resuse the local address (important for servers)
   * @param bool enabled
   */
  inline void reuseaddress(bool enabled)
  { option(SOL_SOCKET, SO_REUSEADDR, enabled ? 1 : 0); }
 
  /**
   * Get if socket can resuse the local address (important for servers)
   * @return bool
   */
  inline bool reuseaddress()
  { return option(SOL_SOCKET, SO_REUSEADDR) != 0; }
 
  /**
   * Set nonblocking mode
   * @param bool enabled
   */
  void nonblocking(bool enabled)
  {
    #ifdef OS_WIN
    u_long d = enabled ? 1:0;
    ioctlsocket(id_, FIONBIO, &d);
    #elif defined(O_NONBLOCK)
    int o = ::fcntl(id_, F_GETFL, 0);
    if(o < 0) return;
    if(enabled) o |= O_NONBLOCK; else o &= (~O_NONBLOCK);
    ::fcntl(id_, F_SETFL, o);
    #elif defined(SO_NONBLOCK)
    long d = enabled ? 1:0;
    ::setsockopt(id_, SOL_SOCKET, SO_NONBLOCK, &d, sizeof(d));
    #endif
  }
 
  /**
   * Get nonblocking mode
   * @return bool
   */
  bool nonblocking()
  {
    #if defined(O_NONBLOCK)
    int o = ::fcntl(id_, F_GETFL, 0);
    return (o > 0) && (o & O_NONBLOCK) != 0;
    #elif defined(SO_NONBLOCK)
    long d;
    ::getsockopt(id_, SOL_SOCKET, SO_NONBLOCK, &d, sizeof(d));
    return d != 0;
    #endif
    return false;
  }
 
  /**
   * Creates the socket, default TCP socket
   * @param int type=SOCK_STREAM
   * @param int family=0
   * @param int protocol=0
   * @return bool
   **/
  bool create(int type=SOCK_STREAM, int family=0, int protocol=0)
  {
    ctracem("basic_socket::create()");
    if(!family) {
      if(!ip_.valid()) {
        error_text("basic_socket::create(): Cannot create socket from invalid ip", true);
        return false;
      }
      family = ip_.family();
    }
    id_ = ::socket(family, type, protocol);
    if(id_ == swsck_inv) {
      analyze_error(swsck_err);
      error_text("basic_socket::create(): Failed to create socket");
      return false;
    }
    return true;
  }
 
  /**
   * TCP connect. If the method fails, the error can be fetched using the
   * error() method.
   * @return bool
   **/
  bool connect()
  {
    ctracem("basic_socket::connect()");
    if(!ip_.valid()) return false;
    typename ip_address_t::address_type adr = ip_.address();
    analyze_error(::connect(id_, (sockaddr*)(&adr), (socklen_t) ip_.size()));
    if (error()) { close(); return false; }
    nonblocking(false);
    return true;
  }
 
  /**
   * Binds a server socket to port/address. If the method fails, the error
   * can be fetched using the error() method.
   * @return bool
   **/
  bool bind()
  {
    ctracem("basic_socket::bind()");
    if(!ip_.valid()) return false;
    typename ip_address_t::address_type adr = ip_.address();
    analyze_error(::bind(id_, (sockaddr*)(&adr), (socklen_t) ip_.size()));
    if (error()) { close(); return false; }
    return true;
  }
 
  /**
   * Starts listening. Returns success. If the method fails, the
   * error can be fetched using the error() method.
   * Annotation: this method sets automatically the nonblocking mode
   * for the acceptor. This is not implemented in api ::listen.
   * @return bool
   **/
  bool listen(int max_listen_pending = 1)
  {
    ctracem("basic_socket::listen()");
    analyze_error(::listen(id_, max_listen_pending));
    if (error()) { close(); return false; }
    nonblocking(true);
    listens_ = true;
    return true;
  }
 
  /**
   * TCP accept. Returns the accepting socket. If the method fails, the
   * returned socket is invalid (method connected()==false).
   * Annotation: this method sets automatically the nonblocking mode
   * for the acceptor. This is not implemented in api ::accept.
   * @param basic_socket & acc
   * @return bool
   **/
  bool accept(basic_socket & acc)
  {
    ctracem("basic_socket::accept()");
    if(!acc.closed()) acc.close();
    acc.ip_.clear();
    ip_address_t tip;
    typename ip_address_t::address_type sa;
    typename ip_address_t::addresslen_type lsa = sizeof (sa);
    swsck id = ::accept(id_, (sockaddr*) (&sa), &lsa);
    if(id == swsck_inv || !sa.ss_family || !lsa) return false;
    acc.id_ = id;
    if(sa.ss_family == AF_INET) {
      acc.ip_ = *((sockaddr_in*)&sa);
    } else if(sa.ss_family == AF_INET6) {
      acc.ip_ = *((sockaddr_in6*)&sa);
    } else {
      acc.ip_.address(sa);
      acc.ip_.size(lsa);
    }
    nonblocking(false);
    return true;
  }
 
  /**
   * Closes a socket. Before the method checks if the socket is already closed.
   **/
  void close() throw ()
  {
    if(id_ == swsck_inv) return;
    ctracem("basic_socket::close()");
    swsck iid = id_;
    id_ = swsck_inv;
    #ifndef OS_WIN
    ::close(iid);
    #else
    shutdown(iid, SD_BOTH); closesocket(iid);
    #endif
  }
 
  /**
   * Waits until something has changed in the socket, either received new data,
   * send ready or error. Returns false if timeout, true if somthing changed.
   * @param bool *is_recv = NULL
   * @param bool *is_sent = NULL
   * @param int timeout_ms=100
   * @return bool
   **/
  bool wait(bool *is_recv = NULL, bool *is_sent = NULL, int timeout_ms=100)
  {
    if(closed() || ((!is_recv) && (!is_sent))) return false;
    if(is_recv) *is_recv = false;
    if(is_sent) *is_sent = false;
    if(!checksent_) is_sent=NULL;
    checksent_ = false;
    #ifndef OS_WIN
    struct pollfd fd;
    fd.fd = id_;
    fd.events = (is_recv ? POLLIN:0) | ((checksent_ && is_sent) ? POLLOUT:0);
    fd.revents = 0;
    checksent_ = false;
    if(!fd.events) return false;
    int r = ::poll(&fd, 1, timeout_ms);
    if(r<0) { analyze_error(r); return false; }
    if(!r || !fd.revents) return false;
    if(is_recv && (fd.revents & POLLIN)) *is_recv = true;
    if(is_sent && (fd.revents & POLLOUT)) *is_sent = true;
    #else
    struct timeval to;
    struct fd_set ifd, ofd;
    to.tv_sec = timeout_ms / 1000;
    to.tv_usec= timeout_ms % 1000;
    FD_ZERO(&ifd); FD_ZERO(&ofd);
    if(is_recv) FD_SET(id_, &ifd);
    if(is_sent) FD_SET(id_, &ofd);
    int r = ::select(id_+1, is_recv ? (&ifd):0, is_sent ? (&ofd):0, 0, &to);
    if(r<0) { analyze_error(r); return false; }
    if((!r) || (!FD_ISSET(id_, &ifd) && (!FD_ISSET(id_, &ofd)))) return false;
    if(is_recv && FD_ISSET(id_, &ifd)) *is_recv = true;
    if(is_sent && FD_ISSET(id_, &ofd)) *is_sent = true;
    #endif
    return true;
  }
 
  /**
   * TCP send string
   * @param const str_t & text
   * @return long
   */
  inline long send(const str_t & text)
  { if (text.empty()) return 0; return send(text.data(), text.length()); }
 
  /**
   * TCP send data, returns success and byref the number of data bytes
   * received in the data buffer. On fail the error can be fetched using
   * the error() method.
   * Annotation: this method trys to send all data until the buffer
   * is full or error. This is not implemented in api ::send.
   * @param const void* data
   * @oaram size_t datasize
   * @return long
   **/
  long send(const void* data, size_t datasize)
  {
    if(!data || !datasize) return 0;
    checksent_ = true;
    const char *p = (const char*) data;
    long l = (long) datasize;
    while (l > 0) {
      long r = ::send(id_, p, (unsigned)l, 0);
      if(r > 0)  {
        l -= r;
        p += r;
        continue;
      } else if((r = analyze_error(r)) != swsck_eagain) {
        return r;
      }
      break;
    }
    ctracem("basic_socket::send(const void* data, datasize=%d)", datasize);
    return (long)datasize-l;
  }
 
  /**
   * TCP receive data, returns success and byref the number of data bytes
   * received in the data buffer. On fail the error can be fetched using
   * the error() method.
   * Annotation: this method trys to receive all data until the buffer
   * is full, no more data available or error. This is not implemented
   * in api ::recv.
   * @param void* data
   * @param size_t datasize
   * @return long
   **/
  long recv(void* data, size_t datasize)
  {
    if(!data || ! datasize) {
      ctrace("basic_socket::recv(!data?, !datasize?, !nreceived?)!");
      close();
      return false;
    }
    long l = ::recv(id_, (char*) data, datasize, MSG_DONTWAIT);
    if(l > 0) return l; // we have data
    if(l == 0) { ctracem("basic_socket::recv() 0 bytes --> close"); close(); return 0; } // we don't have data, socket shall be closed
    analyze_error(l); // Closes the socket if needed
    if(error()) ctracem("basic_socket::recv() ERROR");
    return 0; // we don't have data
  }
 
  /**
   * Receive text data to string. More a testing convenience wrapper
   * with low performance. Prefer recv(void* data, ...).
   * @paran str_t & s
   * @return long
   */
  long recv(str_t & s)
  {
    unsigned n = 256;
    s.clear();
    char *buffer;
    try {
      buffer = new char[n]; // no throw convention
    } catch(...) {
      error_text("basic_socket::recv(str_t&) intermediate data memory allocation failed)", true);
      return -1;
    }
    if(buffer) {
      long nr = 0;
      if((nr = recv(buffer, n)) > 0) {
        s.reserve(s.length()+nr);
        for(long i=0; i<nr; ++i) s += buffer[i];
      }
      delete [] buffer;
    } else {
      error_text("basic_socket::recv(str_t&) failed (no buffer allocated)", true);
    }
    return (long) s.size();
  }
 
  /**
   * UDP receive data, returns success and byref the senders basic_ip_address and the
   * number of data bytes received in the data buffer.
   * On fail the error can be fetched using the error() method.
   * @param void* data
   * @param size_t datasize
   * @param ip_address_t & ip
   * @return long
   **/
  long recvfrom(void* data, size_t datasize, ip_address_t & ip)
  {
    typename ip_address_t::address_type sa;
    typename ip_address_t::addresslen_type lsa = sizeof (sa);
    long r = ::recvfrom(id_, (char*) data, datasize, 0, (sockaddr*)&sa, &lsa);
    if(r == swsck_err && analyze_error(r)) return -1;
    if(sa.ss_family == AF_INET) {
      ip = *((sockaddr_in*)&sa);
    } else if(sa.ss_family == AF_INET6) {
      ip = *((sockaddr_in6*)&sa);
    } else {
      ip.address(sa); // fingers crossed that those data are right
      ip.size(lsa);
    }
    return r;
  }
 
  /**
   * UDP send data to a basic_ip_address. On fail the error can be fetched using the error() method.
   * @param const void* data
   * @param size_t datasize
   * @param const ip_address_t & ip
   * @return long
   **/
  long sendto(const void* data, size_t datasize, const ip_address_t & ip)
  {
    if(!ip.valid()) {
      error_text("basic_socket::sendto(): IP address is invalid", true);
      return -1;
    }
    typename ip_address_t::address_type sa = ip.address(); // ip.address() is const
    typename ip_address_t::addresslen_type lsa = ip.size();
    long r = ::sendto(id_, (const char*) data, datasize, 0, (struct sockaddr*) (&sa), lsa);
    if(r == swsck_err && analyze_error(r)) {
      ctrace("basic_socket::sendto(): Failed to send");
      return -1;
    }
    checksent_ = true;
    return r;
  }
 
protected:
 
  /**
   * Analyzes the socket error, if it is fatal closes the socket. Returns 0 on
   * EGAIN/EWOULDBLOCK, EINTR and ETIMEOUT.
   * @param long fn_return
   * @return long
   **/
  long analyze_error(long fn_return) const
  {
    if(fn_return == swsck_err) {
      long e = swsck_eno;
      switch (e) { // list of not fatal errors
        case swsck_eagain: break; // ctracem("basic_socket::analyze_error() WOULDBLOCK"); break;
        case swsck_eintr:  break; // ctracem("basic_socket::analyze_error() EINTR"); break;
        case swsck_timeout: break;// ctracem("basic_socket::analyze_error() TIMEOUT");  break;
        default:
          const_cast<long&>(errno_) = (long)e;
          const_cast<str_t&>(errtext_) = syserr_text(e);
          if(id_ != swsck_inv) {
            #ifdef OS_WIN
            ::shutdown(id_, SD_BOTH); ::closesocket(id_);
            #else
            ::close(id_);
            #endif
            ctrace("basic_socket::analyze_error() '%s' (socket id=%d)", errtext_.c_str(), id_);
          }
          const_cast<swsck&>(id_) = swsck_inv;
      }
      return e;
    }
    return -1;
  }
 
  /**
   * Returns true if the socket is a listening server socket.
   * @return bool
   */
  inline bool listens()
  { return listens_; }
 
  /**
   * Get an error text form system
   * @param long err
   * @return str_t
   */
  static inline str_t syserr_text(long err)
  {
    #ifdef OS_WIN
    char buffer[64];
    if(FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS|FORMAT_MESSAGE_FROM_SYSTEM,
      NULL, (DWORD) err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
      buffer, 63, NULL)) {
        buffer[63] = '\0';
        return str_t(buffer);
    }
    return str_t("unknown error");
    #else
    return ::strerror(err);
    #endif
  }
 
private:
  swsck id_;
  ip_address_t ip_;
  long errno_;
  str_t errtext_;
  unsigned long timeout_;
  bool checksent_;
  bool listens_;
  int max_listen_pending_;
};
}}
// </editor-fold>
 
// <editor-fold desc="basic_udp_socket" defaultstate="collapsed">
/**
 * Implements basic_socket and a self starting thread performing ::select()
 * and calling overloadable callbacks for connected, error, close, receive,
 * send.
 */
namespace sw { namespace detail {
template <typename Address_type=basic_ip_address<> >
class basic_udp_socket : protected basic_socket<Address_type>
{
public:
 
  typedef basic_socket<Address_type> socket_t;
  typedef typename socket_t::ip_address_t ip_address_t;
  typedef typename socket_t::str_t str_t;
 
public:
 
  basic_udp_socket() : socket_t()
  {}
 
  basic_udp_socket(const ip_address_t & adr) : socket_t(adr)
  {}
 
  basic_udp_socket(const char* adr) : socket_t(adr)
  {}
 
  virtual ~basic_udp_socket()
  { socket_t::close(); }
 
public:
 
  inline bool closed() const
  { return socket_t::closed(); }
 
  inline long error() const
  { return socket_t::error(); }
 
  /**
   * Returns a string representation of the error
   * @return const str_t &
   */
  inline const str_t & error_text() const
  { return socket_t::error_text(); }
 
  /**
   * Waits until something has changed in the socket, either received new data,
   * send ready or error. Returns false if timeout, true if somthing changed.
   * @param bool *is_recv = NULL
   * @param bool *is_sent = NULL
   * @param int timeout_ms=100
   **/
  inline bool wait(bool *is_recv = NULL, bool *is_sent = NULL, int timeout_ms=100)
  { return socket_t::wait(is_recv, is_sent, timeout_ms); }
 
public:
 
  /**
   * Create an unbound ("client") UDP socket.
   * @return bool
   */
  bool create()
  {
    if(!socket_t::closed()) {
      socket_t::error_text("basic_udp_socket::create(): Already created", true);
      return false;
    } else if(!socket_t::create(SOCK_DGRAM)) {
      socket_t::error_text("basic_udp_socket::create(): failed to create socket");
      return false;
    } else if(!thread::start()) {
      socket_t::error_text("basic_udp_socket::create(): failed to start thread");
    }
    return true;
  }
 
  /**
   * Create a bound socket to the specified ip address (text must include the port)
   * @param const char* ip
   * @return bool
   */
  inline bool bind(const char* ip)
  { return !ip ? false : basic_udp_socket::bind(ip_address_t(ip)); }
 
  /**
   * Create a bound socket to the specified ip address
   * @param const ip_address_t & ip
   * @return bool
   */
  bool bind(const ip_address_t & ip)
  {
    socket_t::ip(ip);
    if(!socket_t::closed()) {
      socket_t::error_text("basic_udp_socket::bind(): Socket already open", true);
      return false;
    } else if(!ip.valid()) {
      socket_t::error_text("basic_udp_socket::bind(): IP address not valid", true);
    } else if(!ip.port()) {
      socket_t::error_text("basic_udp_socket::bind(): No port specified", true);
      return false;
    } else if(!socket_t::create(SOCK_DGRAM)) {
      socket_t::error_text("basic_udp_socket::bind(): Could not create UDP socket");
      return false;
    } else if(!socket_t::bind()) {
      socket_t::error_text("basic_udp_socket::bind(): Could not bind socket");
      return false;
    }
    return true;
    }
 
  /**
   * Closes the socket
   */
  inline void close()
  { socket_t::close(); }
 
  /**
   * UDP receive data, returns success and byref the senders basic_ip_address and the
   * number of data bytes received in the data buffer.
   * On fail the error can be fetched using the error() method.
   * @param void* data
   * @param size_t datasize
   * @param ip_address_t & ip
   * @return long
   **/
  inline long recvfrom(void* data, size_t datasize, ip_address_t & ip)
  { return socket_t::recvfrom(data, datasize, ip); }
 
  /**
   * UDP send data to a basic_ip_address. On fail the error can be fetched using the error() method.
   * @param const void* data
   * @param size_t datasize
   * @param const ip_address_t & ip
   * @return long
   **/
  inline long sendto(const void* data, size_t datasize, const ip_address_t & ip)
  { return socket_t::sendto(data, datasize, ip); }
 
};
}}
namespace sw { namespace sock {
  typedef sw::detail::basic_udp_socket<> udp_socket;
}}
// </editor-fold>
 
// <editor-fold desc="basic_tcp_socket" defaultstate="collapsed">
/**
 * class basic_tcp_socket
 * Implements basic_socket and a self starting thread performing ::select()
 * and calling overloadable callbacks for connected, error, close, receive,
 * send and accept.
 */
namespace sw { namespace detail {
template <typename Address_type=basic_ip_address<> >
class basic_tcp_socket : protected basic_socket<Address_type>, protected basic_thread<>
{
public:
 
  typedef basic_socket<Address_type> socket_t;
  typedef basic_thread<> thread_t;
  typedef typename socket_t::ip_address_t ip_address_t;
  typedef typename socket_t::str_t str_t;
 
protected:
 
  // Methods to overload
 
  /**
   * First callback ever called in the tcp socket's thread. Called when a client is connected
   * to a server OR when a server listening thread just started.
   */
  virtual void on_connected() { ; }
 
  /**
   * Called when a client or acceptor has received data with payload.
   */
  virtual void on_receive()   { ; }
 
  /**
   * Called when a client or acceptor has sent data with payload.
   */
  virtual void on_sent()      { ; }
 
  /**
   * Called when a server listening socket received a new request.
   */
  virtual void on_accept()    { ; }
 
  /**
   * Called when a an error occurred.
   */
  virtual void on_error(long no, const char* text)  { (void)no; (void)text; }
 
  /**
   * Called immediately after a the socket thread loop is quit because the basic_socket
   * is closed.
   */
 
  virtual void on_close()     { ; }
 
  /**
   * Called frequently when the socket is open (either when data were received or sent, or wait()
   * exited with a select/poll timeout)
   */
  virtual void on_update()    { ; }
 
protected:
 
  /**
   * Constructors / destructor
   */
  basic_tcp_socket() : socket_t(), thread_t(), rx_(false), tx_(false)
  { ; }
 
  basic_tcp_socket(swsck id) : socket_t(id), thread_t(), rx_(false), tx_(false)
  { ; }
 
  basic_tcp_socket(const ip_address_t & adr) : socket_t(adr), thread_t(), rx_(false), tx_(false)
  { ; }
 
  basic_tcp_socket(swsck id, const ip_address_t & adr) : socket_t(id, adr), thread_t(),
    rx_(false), tx_(false)
  { ; }
 
  basic_tcp_socket(const char* adr) : socket_t(adr), thread_t(), rx_(false), tx_(false)
  { ; }
 
  virtual ~basic_tcp_socket()
  { ctracem("basic_tcp_socket::~basic_tcp_socket()"); socket_t::close(); }
 
public:
 
  /**
   * Returns the basic_ip_address
   * @return const ip_address_t &
   **/
  inline const ip_address_t & ip() const
  { return socket_t::ip(); }
 
  /**
   * Sets the new basic_ip_address
   * @param const ip_address_t & adr
   **/
  inline void ip(const ip_address_t & adr)
  { socket_t::ip(adr); }
 
  /**
   * Sets the new basic_ip_address
   * @param const char* adr
   **/
  inline void ip(const char* adr)
  { socket_t::ip(adr); }
 
  /**
   * Returns the current socket error.
   * @return long
   **/
  inline long error() const throw ()
  { return socket_t::error(); }
 
  /**
   * Returns a string representation of the error
   * @return const str_t &
   */
  inline const str_t & error_text() const
  { return socket_t::error_text(); }
 
protected:
 
 /**
   * Sets the new socket error
   * The error instance variable is mutable, so the method can be const.
   * This is required because some const methods must set the error.
   * @param long newerror
   **/
  inline void error(long newerror)
  { return socket_t::error(newerror); }
 
  /**
   * Set an error text
   * @param const char* text
   */
  inline void error_text(const char* text)
  { return socket_t::error_text(text); }
 
  /**
   * Connect (client) by host string
   * @param const char* iptext
   * @return bool
   */
  inline bool connect(const char* iptext)
  { return connect(ip_address_t(iptext)); }
 
  /**
   * Connect (client) by ip address
   * @param const ip_address_t & ip_addr
   * @return bool
   */
  bool connect(const ip_address_t & ip_addr)
  {
    if(!socket_t::closed()) {
      error_text("basic_tcp_socket::connect(): Already connected.");
      return false;
    }
    if(!ip_addr.valid()) {
      error_text("basic_tcp_socket::connect(): IP address no valid.");
      return false;
    }
    socket_t::ip(ip_addr);
    if(!socket_t::create())  {
      if(error_text().empty()) error_text("basic_tcp_socket::connect(): Failed to create socket.");
      return false;
    }
    if(!socket_t::connect()) {
      error_text("basic_tcp_socket::connect(): Failed to connect.");
      return false;
    }
    if(!thread_t::start()) {
      error_text("basic_tcp_socket::connect(): Failed to start thread.");
      socket_t::close();
      return false;
    }
    return true;
  }
 
  /**
   * Listen to an address/port (server)
   * @param const ip_address_t & ip_addr
   * @return bool
   */
  bool listen(const ip_address_t & ip_addr, bool reuse_address=true, bool no_delay=true,
          int max_pending=1)
  {
    if(!socket_t::closed()) {
      error_text("basic_tcp_socket::listen(): Already connected.");
      return false;
    }
    if(ip_addr.port() == 0) {
      error_text("basic_tcp_socket::listen(): Port not specified.");
      return false;
    }
    socket_t::ip(ip_addr);
    if(!socket_t::create()) {
      if(error_text().empty()) error_text("basic_tcp_socket::listen(): Failed to create socket.");
      return false;
    }
    socket_t::reuseaddress(reuse_address);
    socket_t::nodelay(no_delay);
    if(!socket_t::bind()) {
      error_text("basic_tcp_socket::listen(): Failed to bind socket.");
      return false;
    }
    if(!socket_t::listen(max_pending)) {
      error_text("basic_tcp_socket::listen(): Failed to start listening.");
      return false;
    }
    if(!thread_t::start()) {
      error_text("basic_tcp_socket::listen(): Failed to start thread.");
      socket_t::close();
      return false;
    }
    return true;
  }
 
  /**
   * Accept a request with an acceptor socket (server=
   * @param basic_tcp_socket & acc
   * @return bool
   */
  bool accept(basic_tcp_socket & acc)
  {
    if(!socket_t::accept(acc)) return false;
    if(!acc.start()) {
      acc.close();
      acc.stop();
      error_text("basic_tcp_socket::accept(): Failed to start acceptor thread.");
      return false;
    }
    return true;
  }
 
  /**
   * Close connection (server / client)
   */
  void close()
  {
    ctrace("basic_tcp_socket::close()");
    socket_t::close(); // That will break the thread loop
  }
 
  /**
   * Returns if the TCP socket is closed and the thread not running.
   */
  inline bool closed() const
  { return socket_t::closed() && !thread::running(); }
 
  /**
   * Start thread
   */
  inline bool start()
  { return thread::start(); }
 
  /**
   * Returns the id if the tcp_socket's thread
   * @return typename thread::id_type
   */
  inline typename thread::id_type thread_id() const throw()
  { return thread::thread_id(); }
 
private:
 
  /**
   * Thread callback. Exceptions are caught in the thread frame loop, which will proceed
   * with on_stop() {}
   */
  void on_start()
  { this->on_connected(); }
 
  /**
   * Thread callback. Exceptions are caught in the thread frame loop, which will proceed
   * with on_stop() {}
   */
  bool on_loop()
  {
    if(socket_t::wait(&rx_, &tx_)) {
      if(rx_) {
        if (socket_t::listens()) {
          this->on_accept();
        } else {
          this->on_receive();
        }
      }
      if(tx_ && !socket_t::listens()) {
        this->on_sent();
      }
    }
    if(!socket_t::closed()) {
      if(socket_t::error()) {
        socket_t::close();
        this->on_error(socket_t::error(), socket_t::error_text().c_str());
      }
      this->on_update();
    } else {
      this->on_close();
      return false;
    }
    return true;
  }
 
  /**
   * Thread callback after the loop was quit.
   */
  void on_stop()
  { socket_t::close(); } // Ensure the socket is closed as well to meet closed() requirements.
 
private:
 
  bool rx_; // wait() received flag
  bool tx_; // wait() sent flag
};
}}
// </editor-fold>
 
// <editor-fold desc="basic_tcp_client" defaultstate="collapsed">
/**
 * Base class for TCP clients.
 * NOTE:  See basic_tcp_socket for the method details. This class makes some
 *        features private that clients do not have (e.g. listen).
 **/
namespace sw { namespace detail {
template <typename Address_type=basic_ip_address<> >
class basic_tcp_client : public basic_tcp_socket<Address_type>
{
public:
  typedef basic_tcp_socket<Address_type> socket_t;
  typedef typename socket_t::ip_address_t ip_address_t;
  typedef typename socket_t::str_t str_t;
public:
  // Public forwards
  basic_tcp_client() : socket_t()   { }
  virtual ~basic_tcp_client()  { }
  inline void disconnect() { socket_t::close(); }
  inline bool connect(const char* ip)  { return socket_t::connect(ip); }
  inline bool connect(const ip_address_t & ip)  { return socket_t::connect(ip); }
  inline bool connected() { return !socket_t::closed(); }
  inline bool closed() { return socket_t::closed(); }
  inline long error() { return socket_t::error(); }
  inline str_t error_text() { return socket_t::error_text(); }
  inline long send(const void* data, size_t datasize) { return socket_t::send(data, datasize); }
  inline bool send(const str_t & text) { return socket_t::send(text) >=0; }
private:
  // Private blockers
  inline bool listen(const ip_address_t & ip_addr) { (void) ip_addr; return false; }
  inline bool listen() { return false; }
  inline bool accept(socket_t & acc) { (void) acc; return false; }
  inline bool create(int type = SOCK_STREAM) { (void) type; return false; }
  inline bool bind() { return false; }
  virtual void on_accept() { }
};
}}
// </editor-fold>
 
// <editor-fold desc="basic_tcp_acceptor" defaultstate="collapsed">
/**
 * Server class forward
 */
namespace sw { namespace sock {
  template <typename acceptor_type> class tcp_server;
}}
 
/**
 * Base class for TCP server<> acceptors.
 * NOTE:  See basic_tcp_socket for the method details. This class only makes
 *        features private that acceptors do not have (e.g. listen and connect).
 
 **/
namespace sw { namespace detail {
template <typename Address_type=basic_ip_address<> >
class basic_tcp_acceptor : public basic_tcp_socket<Address_type>
{
  template <typename acceptor_type> friend class sw::sock::tcp_server;
public:
  typedef basic_tcp_socket<Address_type> socket_t;
  typedef typename socket_t::ip_address_t ip_address_t;
  basic_tcp_acceptor() : socket_t() { }
  virtual ~basic_tcp_acceptor() { }
private:
  // Private overrides to prevent wrong usage / mistyping
  bool connect(const ip_address_t & ip_addr) { (void) ip_addr; return false; }
  bool connect() { return false; }
  bool listen(const ip_address_t & ip_addr) { (void) ip_addr; return false; }
  bool listen() { return false; }
  bool accept(socket_t & acc) { (void) acc; return false; }
  bool create(int type = SOCK_STREAM) { (void) type; return false; }
  bool bind() { return false; }
  void on_accept() { }
};
}}
// </editor-fold>
 
// <editor-fold desc="tcp_server" defaultstate="collapsed">
/**
 * Server class with auto accept with sock::acceptor derived acceptors.
 */
namespace sw { namespace sock {
template <typename acceptor_type>
class tcp_server : public sw::detail::basic_tcp_socket<>
{
public:
 
  typedef acceptor_type acceptor_t;
  typedef std::deque<acceptor_t*> container_type;
  typedef typename std::deque<acceptor_t*>::iterator acceptor_iterator;
  typedef sw::detail::basic_tcp_socket<> socket_t;
  typedef typename socket_t::ip_address_t ip_address_t;
 
protected:
 
  /**
   * Methods to overload to use the server:
   */
  virtual void on_started()   { ; }
  virtual void on_stopped()   { ; }
  virtual bool on_request(acceptor_t&)  { return true; }
 
protected:
 
  tcp_server() : max_clients_(16), max_listen_pending_(64), TcpNoDelay(true), TcpReuseAddress(true)
  { ctracem("tcp_server::tcp_server()"); }
 
  virtual ~tcp_server()
  { ctracem("tcp_server::~tcp_server()"); tcp_server::stop(); }
 
public:
 
  /**
   * Start the server
   * @return bool
   */
  inline bool start()
  { return socket_t::listen(socket_t::ip(), TcpReuseAddress, TcpNoDelay, max_listen_pending_); }
 
  /**
   * Returns the maximum number of concurrent clients
   * @return unsigned
   */
  inline unsigned max_clients() const throw()
  { return max_clients_; }
 
  /**
   * Sets the maximum number of concurrent clients
   */
  inline void max_clients(unsigned n) throw()
  { max_clients_ = n; }
 
  /**
   * Returns the maximum number of pending connections (backlog) of a listening socket.
   * @return int
   */
  inline int max_clients_pending() const
  { return max_listen_pending_; }
 
  /**
   * Sets the maximum number of pending connections (backlog) of a listening socket.
   * @param int n
   */
  inline void max_clients_pending(int n)
  { max_listen_pending_ = n > 2 ? n : 2; }
 
  /**
   * Stop the server
   */
  void stop()
  {
    if(socket_t::closed()) return;
    ctracem("tcp_server::stop()");
    max_clients_ = 0;
    socket_t::close();
    if(thread::current_thread() != thread_id()) {
      ctracem("tcp_server::stop(): Not my thread, wait until done");
      while(!socket_t::closed()) thread::usleep(1000);
    }
    // See details in on_close()
  }
 
  /**
   * Returns true if the server is listening
   * @return bool
   */
  inline bool running() const
  { return !socket_t::closed(); }
 
  /**
   * Returns the number of currently connected clients
   * @return unsigned
   */
  inline unsigned num_connected() const throw()
  { return (unsigned) acceptors_.size(); }        // stfwi: double check thread safety requirements.
 
  /**
   * Removes closed acceptors form the list. Can be only called either from the server thread
   * or when the server thread is stopped.
   * @return unsigned
   */
  unsigned clear_closed()
  {
    unsigned n_cleared = 0;
    acceptor_iterator it = acceptors_.begin();
    while (it != acceptors_.end()) {
      if((*it)->closed()) {
        acceptor_t *p_acc = *it;
        it = acceptors_.erase(it);
        delete p_acc;
        ++n_cleared;
      } else {
        ++it;
      }
    }
    return n_cleared;
  }
 
private:
 
  /**
   * Listener initialisation
   */
  void on_connected()
  {
    ctracem("tcp_server::on_connected()");
    this->on_started();
  }
 
  /**
   * Accept callback. Creates and assigns an acceptor invokes on_request().
   */
  void on_accept()
  {
    ctracem("tcp_server::on_accept()");
    if(socket_t::closed()) return; // don't accept new connections when gracefully closing
    if(acceptors_.size() >= max_clients_ && !clear_closed()) {
      thread_t::usleep(1000);
      return;
    }
    acceptor_t *p_acc = 0;
    try { p_acc = new acceptor_type(); } catch(...) { p_acc = 0; }
    if(!p_acc) {
      ctrace("tcp_server::on_accept(): Failed create new acceptor_t object");
      return;
    }
    acceptor_t &acc = *p_acc;
    if(socket_t::accept(acc)) {
      bool accepted = false;
      try {
        accepted = this->on_request(acc);
      } catch (const std::exception & e) {
        socket_t::error_text(e.what());
      } catch (...) {
        socket_t::error_text("exception in server.on_request()");
      }
      if(!accepted) {
        acc.close();
      } else {
        acc.nodelay(true); // Normally the acceptors should inherit this, but to ensure ...
        acceptors_.push_back(p_acc);
      }
    }
  }
 
  /**
   * Close event (thread)
   */
  void on_close()
  {
    ctracem("tcp_server::on_close()");
    for (acceptor_iterator it = acceptors_.begin(); it != acceptors_.end(); ++it) {
      try {
        ctracem("tcp_server::on_close(): close socket %d", (*it)->socket_id());
        (*it)->close();
      } catch (const std::exception & e) {
        socket_t::error_text(e.what());
      } catch (...) {
        socket_t::error_text("exception in server.on_close()");
      }
    }
    ctracem("tcp_server::on_close(): clear_closed()");
    while(!acceptors_.empty()) {
      if(!clear_closed()) thread::usleep(1000);
    }
    ctracem("tcp_server::on_close(): acceptors_.clear()");
    acceptors_.clear();
    this->on_stopped();
  }
 
private:
  // Masking
  void on_receive() { }
  void on_sent()  { }
  bool connect(const ip_address_t&)  { return false; }
  bool connect() { return false; }
  bool listen(const ip_address_t&) { return false; }
  bool listen() { return false; }
  bool create(int type = SOCK_STREAM) { (void) type; return false; }
  bool bind() { return false; }
  bool accept() { return false; }
 
private:
  container_type acceptors_;
  unsigned max_clients_;
  int max_listen_pending_;
  bool TcpNoDelay;
  bool TcpReuseAddress;
};
}}
// </editor-fold>
 
// <editor-fold desc="specialisations" defaultstate="collapsed">
namespace sw { namespace sock {
  typedef sw::detail::basic_ip_address<> ip_address;
  typedef sw::detail::basic_socket<> socket;
  typedef sw::detail::basic_tcp_socket<> tcp_socket;
  typedef sw::detail::basic_tcp_client<> tcp_client;
  typedef sw::detail::basic_tcp_acceptor<> tcp_acceptor;
}}
// </editor-fold>
 
// <editor-fold desc="undefs" defaultstate="collapsed">
#undef swsck
#undef swsck_inv
#undef swsck_err
#undef swsck_eno
#undef swsck_eintr
#undef swsck_nexist
#undef swsck_timeout
#undef swsck_eagain
#undef swsck_default_timeout
#undef ctracem
#undef ctrace
// </editor-fold>
#endif

Beispiele

Examples

UDP sockets

#define SOCK_DEBUG    /* Enables socket method tracing */
#include <socket.hh>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdlib>
 
using namespace std;
 
using sw::sock::udp_socket; // The UDP socket class used
using sw::sock::ip_address; // IP address wrapper used
 
#ifdef OS_WIN
sw::sock::winsock_initializer wsinit; // Windows only
#endif
 
int main(int argc, char** argv)
{
  //--- CLI input
  string str_ip = "127.0.0.1";
  int sendport = 0;
 
  // Port to bind for receiving
  if(argc >=2 && argv[1]) {
    string s = argv[1];
    if(std::count_if(s.begin(), s.end(), ::isdigit) != (long)s.length()) {
      cerr << "Specify a numeric receive port as first argument." << endl;
      return 1;
    }
    str_ip += string(":") + s;
  } else {
    str_ip += ":1234";
  }
 
  // Port to send an echo / forward the message to
  if(argc >=3 && argv[2]) {
    // 0 on wrong input, will cause an error later
    sendport = (unsigned) ::atol(argv[2]);
  }
 
  // Create new UDP socket
  udp_socket sock;
 
  // Create an IP address with an initialiser string
  // This class also contains the port, type, family, and
  // methods to parse/compose ip address strings,
  // lookup host names, lookup service names/ports, etc.
  ip_address ip = str_ip;
 
  // Bind the socket to that IP
  if(!sock.bind(ip)) {
    // Methods error_text() and error() are used for error details
    cerr << "!!! Failed to bind UDP socket to address '" << ip.str() << "', port " << ip.port()
         << " (error: " << sock.error_text() << ")" << endl;
    return 1;
  } else {
    cerr << "Socket bound to address '" << ip.str() << "', port " << ip.port() << endl;
    if(sendport) cerr << "Echo send-to-port is " << sendport << endl;
    cerr << "CTRL-C to exit." << endl;
  }
 
  // The UDP socket class has no "integrated" thread, so this done here
  // in the main thread loop: We simply loop here until the socket is closed.
  while(!sock.closed()) {
    bool is_rx;
 
    // wait() makes the thread dormant until data were sent, received or
    // the specified timeout is exceeded. Wait is declared as:
    // bool wait(bool *is_recv = NULL, bool *is_sent = NULL, int timeout_ms=100),
    // so you can wait for a package been sent, too.
    // In the very end, this method is a wrapper for ::poll() or ::select().
    // wait() returns true if data were sent or received
    if(sock.wait(&is_rx) && is_rx) {
      char data[1025];
      // Receiving is similar to the standard ::recvfrom() function.
      long nreceived = sock.recvfrom(data, sizeof(data)-1, ip);
      data[sizeof(data)-1] = '\0';
      if(nreceived < 0) {
        sock.close();
      } else if(nreceived > 0) {
        data[nreceived] = '\0';
        cout << "received: " << data << endl;
        if(sendport) {
          ip.port(sendport);
          // Sending is similar to the standard ::sendto() function.
          long nsent = sock.sendto(data, nreceived, ip); // echo
          if(nsent >= 0) {
            cout << "send: " << data << endl;
          } else if(sock.error()) {
            cout << "!!! send error:" << sock.error() << endl;
          }
        }
        string s = data;
        if(s.find("close") != string::npos || s.find("stop") != string::npos) sock.close();
      }
    }
  }
  if(sock.error()) {
    cout << "!!! Error: " << sock.error_text() << endl;
    return 1;
  }
  return 0;
}

TCP client

#define SOCK_DEBUG    /* Enables socket method tracing */
#include <iostream>
#include <socket.hh>
 
#ifdef OS_WIN
sw::sock::winsock_initializer wsinit; // Windows only
#endif
 
using namespace std;
 
 
//
// Your TCP client is derived from sw::sock::tcp_client
//
class myclient : public sw::sock::tcp_client
{
  typedef sw::sock::tcp_client client_t;
 
public:
 
  myclient() : client_t()
  { cerr << "myclient::myclient()" << endl; }
 
  virtual ~myclient()
  { cerr << "myclient::~myclient()" << endl; }
 
protected:
 
  // Called when the socket connection is established and
  // the client thread running. This function is already called
  // in the client's own thread.
  void on_connected()
  { cerr << "myclient::on_connected()" << endl; }
 
  // Called when data were received
  void on_receive()
  {
    cerr << "myclient::on_receive() ";
    string s;
    while(client_t::recv(s)) cout << s;
  }
 
  // Called when data were sent.
  void on_sent()
  { cerr << "myclient::on_sent()" << endl; }
 
  // Called when the socket had problems.
  void on_error(long no, const char* text)
  { cerr << "myclient::on_error(" << no << ", << " << text <<  ")" << endl; }
 
  // Called before the client thread quits.
  void on_close()
  { cerr << "myclient::on_close()" << endl; }
 
  // Called frequently, either when data were sent/received or when wait()
  // returned without receiving/sending (wait timeout).
  void on_update()
  { ; }
};
 
//
// Main
//
int main(int argc, char** argv)
{
  // CLI ARGS
  if(argc < 2 || !argv[1]) {
    // Usage
    cerr << "Usage: " << (argv[0] ? argv[0] : "tcp_client") << "[--http <url>] [<address/dns>:<port>]" << endl;
    return 1;
  } else if(string(argv[1]) == "--http") {
    // Make a HTTP request
    if(argc < 3 || !argv[2]) { cerr << "Missing URL" << endl; return 1; }
 
    // The socket class uses sw::sock::ip_address to connect. ip_address simplifies
    // generating inet compliant addresses, has string conversion functions and the
    // "resource address parser" function shown below:
    sw::sock::ip_address adr;
 
    // This static class function can be used to get the chunks and address of a
    // location specification, e.g.
    //
    //  - http://example.com/path // service, host lookup, resource path
    //  - ftp://user@127.0.0.1/   // service, user, V4 address
    //  - ::1                     // V6 address
    //  - [abce::0bbb]:8080       // V6 address, port
    //  - etc...
    //
    string service, user, host, path;
    sw::sock::ip_address::parse_resource_address(argv[2], adr, service, user, host, path, true);
    if(!adr.port()) adr.port(80);
    cout << "SERVICE= " << service << endl;
    cout << "HOST = " << host << endl;
    cout << "PORT = " << adr.port() << endl;
    cout << "PATH = " << path << endl;
    cout << "ADR  = " << adr.str() << endl;
    cout << "IPV6 = " << adr.is_v6() << endl;
 
    // Build a simple GET request:
    string request = string("")
      + "GET / HTTP/1.1\n"
      + "Host: " + host + "\n"
      + "User-Agent: Mozilla/5.0\n"
      + "Connection: close\n"
      + "\n";
    cout << request << endl;
 
    // Now the client: Make a new one ...
    myclient client;
 
    // ... connect ...
    if(!client.connect(adr)) {
      cerr << "Failed to connect: " << client.error_text() << endl;
    }
 
    // ... send the request ...
    client.send(request);
 
    // ... and wait for the socket to be closed.
    while(!client.closed()) {
      sw::thread::usleep(10000);
    }
 
  } else {
    // Connect to a server, no HTTP request, more like telnet
    myclient client;
    if(!client.connect(argv[1])) {
      cerr << "Failed to connect: " << client.error_text() << endl;
    }
    cerr << "CTRL-C to exit." << endl;
    // We send from this main thread, the received data are shown in our
    // client class on_receive().
    while(client.connected()) {
      string s;
      cin >> s;
      s += "\n";
      // This is the string send method, there is also one for raw binary with
      // void* and data size.
      client.send(s);
    }
  }
  return 0;
}

TCP server

#define SOCK_DEBUG   /* Enables socket method tracing */
#include <socket.hh>
#include <iostream>
#include <signal.h>
 
using namespace std;
using namespace sw;
 
#ifdef OS_WIN
sw::sock::winsock_initializer wsinit; // Windows only
#endif
 
// When receiving a signal, e.g. kill or CTRL-C,
// we set the flag "stop" to true.
volatile bool stop = false;
void signal_stop(int sig) {
  stop = true;
  switch(sig) {
    case SIGINT:  fprintf(stderr, "\n[SIGINT]\n"); break;
    #ifndef OS_WIN
    case SIGQUIT: fprintf(stderr, "\n[SIGQUIT]\n"); break;
    #endif
  }
  fflush(stderr);
}
 
//
// First we implement the class that actually handles a request,
// the server class itself only listens and creates new objects of
// this class, one for each request.
//
// We derive sw::sock::tcp_acceptor for that:
//
class echo_acceptor : public sw::sock::tcp_acceptor
{
  typedef sw::sock::tcp_acceptor socket_t; // Convenience typedef only
 
public:
 
  echo_acceptor() : socket_t() // <--- initialize tcp_acceptor, too
  { cerr << "echo_acceptor::echo_acceptor()" << endl; }
 
  virtual ~echo_acceptor()
  { cerr << "echo_acceptor::~echo_acceptor()" << endl; }
 
protected:
 
  // Called when the server has created the object and the socket connection is
  // successfully established. This callback method is already called in the
  // acceptor's own thread.
  void on_connected()
  { cerr << "echo_acceptor::on_connected()" << endl; }
 
  // This callback is invoked whenever data from the connected client were
  // received. Use it to deal with these new data:
  void on_receive()
  { cerr << "echo_acceptor::on_receive() ";
    string ss, s;
    // ::recv is inherited from tcp_acceptor -> tcp_socket -> basic_socket, this one
    // is the string receive method. Another one is for binary buffers (void* and size).
    while(socket_t::recv(s)) {
      ss += s;
      cerr << s;
    }
    // And we directly send the data back --> echo
    socket_t::send(ss);
 
    if(ss.find("exit") != string::npos) {
      // On "exit" we close the socket, this will automatically stop the thread, too,
      // and the server object will cleanup this object eventually.
      socket_t::close();
    } else if(ss.find("stop") != string::npos || ss.find("close") != string::npos) {
      // On "stop" we tell the server to shutdown, see below. The flag is globally declared
      // at top of this file.
      ::stop = true;
    }
  }
 
  // Called when data were sent
  void on_sent()
  { cerr << "echo_acceptor::on_sent()" << endl; }
 
  // Called when an error occurred
  void on_error(long no, const char* text)
  { cerr << "echo_acceptor::on_error(" << no << ", << " << text <<  ")" << endl; }
 
  // Called before the thread exits
  void on_close()
  { cerr << "echo_acceptor::on_close()" << endl; }
 
  // Called frequently, either when data were sent/received or when
  // wait() had a timeout (nothing received). It is not called when the
  // socket closed or closing.
  void on_update()
  { ; }
};
 
 
//
// The server is always a template, and specify your acceptor as
// typename acceptor_type. Normally you don't need to implement your own
// server class and instead use the sw::sock::tcp_server<acceptor_type>
// directly, but to tune the details you can easily derive it:
//
template <typename acceptor_type=echo_acceptor >
class echo_server : public sw::sock::tcp_server<acceptor_type>
{
  typedef acceptor_type acceptor_t; // Convenience typedef
  typedef sw::sock::tcp_server<acceptor_type> server_t; // Convenience typedef
 
public:
 
  echo_server(): server_t()
  { cout << "echo_server::echo_server()" << endl; }
 
  virtual ~echo_server()
  { cout << "echo_server::~echo_server()" << endl; }
 
private:
 
  // Called when the socket is bound and listening, and the thread started.
  // This method is already called in the server's own thread.
  void on_started()
  {
    cout << "echo_server::on_started(): "
         << "LISTENING ON " << this->ip().str() << ", PORT " << this->ip().port()
         << endl;
  }
 
  // Called just before the server thread exits.
  void on_stopped()
  {
    cout << "echo_server::on_stopped()";
    if(server_t::error()) cout << ", error= " << server_t::error_text();
    cout << endl;
  }
 
  // A client has connected, and we can return true to accept the connection,
  // or false to refuse it. The acceptor object that will take over this
  // connection is already instantiated and ready to start. Information, such
  // as the IP address of the client is already saved in the acceptor:
  bool on_request(acceptor_t & a)
  { cout << "echo_server::on_request(" <<  a.ip().str() <<  ")" << endl; return true; }
 
  // Called when an error of the listening socket is detected.
  void on_error(long no, const char* text)
  { cout << "echo_server::on_error(" << no << ": " << text << ")" << endl; }
 
  // Called frequently as long as the server is listening.
  void on_update()
  { /* thread loop function */ }
};
 
//
// And main ...
//
int main(int argc, char** argv)
{
  (void) argc; (void) argv;
  // Signals to fire a server shutdown
  ::signal(SIGINT, signal_stop);
  #ifndef OS_WIN
  ::signal(SIGQUIT, signal_stop);
  #endif
 
  // Instantiate the server
  echo_server<> svr;
 
  // Set IP and port to bind/listen to
  svr.ip("[::1]:1315");
 
  // Start the thread
  if(!svr.start()) {
    cout << "Failed to start server: " << svr.error_text() << endl << endl;
  }
 
  // The main loop simply keeps idling until the server has stopped
  // or the stop flag is set.
  while(svr.running()) {
    // Note: You can also say svr.stop(), but in this example we let the
    // destructor do all this, which is called when exiting the main() function.
    if(stop) break;
    sw::thread::usleep(1000);
  }
 
  // Show possible errors
  if(svr.error()) cout << "Error: " << svr.error_text() << endl << endl;
 
  // To see that the server destructor and the acceptors destructors are called
  // after returning.
  fprintf(stderr, "MAIN RETURN\n"); fflush(stderr);
  return 0;
}

IP address handling

#define SOCK_DEBUG
#include <socket.hh>
#include <iostream>
#include <sstream>
 
using namespace std;
using sw::sock::ip_address;
 
#ifdef OS_WIN
sw::sock::winsock_initializer winit;
#endif
 
// <editor-fold desc="check_assign" defaultstate="collapsed">
int check_assign(string s, bool expect_error)
{
  std::stringstream ss;
  ss << "INPUT STRING  = " << s << endl;
  ip_address adr(s);
  ss << "valid()       = " << adr.valid() << endl;
  ss << "str()         = " << adr.str() << endl;
  ss << "family()      = " << adr.family() << endl;
  ss << "is_v4()       = " << adr.is_v4() << endl;
  ss << "is_v6()       = " << adr.is_v6() << endl;
  ss << "port()        = " << adr.port() << endl;
  ss << "subnet_mask() = " << adr.subnet_mask() << endl;
  if(adr.valid() == expect_error) {
    ss << "!!! UNEXPECTED RESULT" << endl;
  }
  ss << endl;
  if(adr.valid() == expect_error) {
    cerr << ss.str();
    return 1;
  }
  return 0;
}
 
int check_assign_all()
{
  int nerr = 0;
  nerr += check_assign("", true);
  nerr += check_assign("::", false);
  nerr += check_assign("[::]", false);
  nerr += check_assign("[::/64]", false);
  nerr += check_assign("[::/64]:9999", false);
  nerr += check_assign("::1", false);
  nerr += check_assign("[::1]", false);
  nerr += check_assign("[::1/64]", false);
  nerr += check_assign("[::1/64]:9999", false);
  nerr += check_assign("a::bcd0", false);
  nerr += check_assign("[a::bcd0]", false);
  nerr += check_assign("[a::bcd0/64]", false);
  nerr += check_assign("a::bcd0/64", false);
  nerr += check_assign("[a::bcd0]:9999", false);
  nerr += check_assign("[a::bcd0]:9", false);
  nerr += check_assign("192.168.0.1", false);
  nerr += check_assign("192.168.0.1/8", false);
  nerr += check_assign("192.168.0.1/8:9999", false);
  nerr += check_assign("192.168.0.1:9999", false);
  nerr += check_assign("[192.168.0.1]", false);
  nerr += check_assign("[192.168.0.1/24]", false);
  nerr += check_assign("[192.168.0.124]:100", false);
  nerr += check_assign("rewkll", true);
  nerr += check_assign("[]", true);
  nerr += check_assign("192.168..1", true);
  nerr += check_assign("320.168.0.1", true);
  nerr += check_assign("192.168.0.1:abc", true);
  nerr += check_assign("192.168.0.1/abs:abc", true);
  nerr += check_assign("192.168.0.1/abs:123", true);
  nerr += check_assign("192.168.0.a/8:999", true);
  nerr += check_assign(":", true);
  nerr += check_assign("[:]", true);
  nerr += check_assign(":::", true);
  nerr += check_assign("a::Xcd0", true);
  nerr += check_assign("a:b:bcd0", true);
  nerr += check_assign("[a::bcd0", true);
  nerr += check_assign("a::bcd0]", true);
  nerr += check_assign("[a::bcd0", true);
  nerr += check_assign("[a::bcd0/128:10]:10", true);
  nerr += check_assign("[a::bcd0/abc]", true);
  cerr << "check_assign_all() number of fails: " << nerr << endl;
  return nerr;
}
// </editor-fold>
 
// <editor-fold desc="check_sockaddr_in6" defaultstate="collapsed">
int check_sockaddr_in6(string inp_addr_str)
{
  struct sockaddr_in6 addr6;
  memset(&addr6, 0, sizeof(struct sockaddr_in6));
  addr6.sin6_family = AF_INET6;
  if(::inet_pton(AF_INET6, inp_addr_str.c_str(), &(addr6.sin6_addr)) != 1) {
    cerr << "!!! TEST INPUT ADDRESS INVALID" << endl;
    return 1;
  }
  string backcheck_str;
  do {
    char s[INET6_ADDRSTRLEN+2]; s[sizeof(s)-1] = '\0';
    if(!::inet_ntop(AF_INET6, &(addr6.sin6_addr), s, INET6_ADDRSTRLEN)) {
      cerr << "!!! inet_ntop FAILED" << endl;
      return 1;
    }
    backcheck_str = s;
  } while(0);
 
  ip_address adr(addr6);
  if(!adr.valid()) {
    cerr << "!!! Constructor assignment failed" << endl;
    return 1;
  } else if(backcheck_str != adr.str()) {
    cerr << "!!! Constructor assignment: backcheck_str != adr.str(): '" << backcheck_str << "' != " << adr.str() << endl;
    return 1;
  }
 
  adr.clear();
  adr = addr6;
  if(!adr.valid()) {
    cerr << "!!! Operator assignment failed" << endl;
    return 1;
  } else if(backcheck_str != adr.str()) {
    cerr << "!!! Operator assignment: backcheck_str != adr.str(): '" << backcheck_str << "' != " << adr.str() << endl;
    return 1;
  }
  return 0;
 
}
int check_sockaddr_in6_all()
{
  int nerr = 0;
  nerr += check_sockaddr_in6("::1");
  cerr << "check_sockaddr_in6_all() number of fails: " << nerr << endl;
  return nerr;
}
// </editor-fold>
 
// <editor-fold desc="check_sockaddr_in" defaultstate="collapsed">
int check_sockaddr_in4(string inp_addr_str)
{
  struct sockaddr_in addr4;
  memset(&addr4, 0, sizeof(struct sockaddr_in));
  addr4.sin_family = AF_INET;
  if(::inet_pton(AF_INET, inp_addr_str.c_str(), &(addr4.sin_addr)) != 1) {
    cerr << "!!! TEST INPUT ADDRESS INVALID" << endl;
    return 1;
  }
  string backcheck_str;
  do {
    char s[INET6_ADDRSTRLEN+2]; s[sizeof(s)-1] = '\0';
    if(!::inet_ntop(AF_INET, &(addr4.sin_addr), s, INET6_ADDRSTRLEN)) {
      cerr << "!!! inet_ntop FAILED" << endl;
      return 1;
    }
    backcheck_str = s;
  } while(0);
 
  ip_address adr(addr4);
  if(!adr.valid()) {
    cerr << "!!! Constructor assignment failed" << endl;
    return 1;
  } else if(backcheck_str != adr.str()) {
    cerr << "!!! Constructor assignment: backcheck_str != adr.str(): '" << backcheck_str << "' != " << adr.str() << endl;
    return 1;
  }
 
  adr.clear();
  adr = addr4;
  if(!adr.valid()) {
    cerr << "!!! Operator assignment failed" << endl;
    return 1;
  } else if(backcheck_str != adr.str()) {
    cerr << "!!! Operator assignment: backcheck_str != adr.str(): '" << backcheck_str << "' != " << adr.str() << endl;
    return 1;
  }
  return 0;
 
}
int check_sockaddr_in4_all()
{
  int nerr = 0;
  nerr += check_sockaddr_in4("127.0.0.1");
  nerr += check_sockaddr_in4("192.168.0.1");
  nerr += check_sockaddr_in4("10.8.1.1");
  cerr << "check_sockaddr_in4_all() number of fails: " << nerr << endl;
  return nerr;
}
// </editor-fold>
 
// <editor-fold desc="check_parse_resource_address" defaultstate="collapsed">
int check_parse_resource_address(string i_service, string i_user, string i_host, string i_port,
        string i_path, enum sw::sock::ip_address::family_sel family, bool expect_valid)
{
  if(i_host.empty()) { cerr << "check_parse_resource_address(): host must be set"; return 1; }
  string i_resaddr;
  if(!i_service.empty()) i_resaddr += i_service + "://";
  if(!i_user.empty()) i_resaddr += i_user + "@";
  i_resaddr += i_host;
  if(!i_port.empty()) i_resaddr += string(":") + i_port;
  if(!i_path.empty()) i_resaddr += i_path;
 
  ip_address o_ip;
  string o_service, o_user, o_host, o_path;
  bool valid = (!ip_address::parse_resource_address(
    i_resaddr, o_ip, o_service, o_user, o_host, o_path, true, family, ip_address::type_tcp
  ));
  if(valid != expect_valid || o_ip.valid() != expect_valid) {
    cerr << "check_parse_resource_address(->" << i_resaddr << "): " << endl
         << "o_service    = " << o_service << endl
         << "o_user       = "  << o_user << endl
         << "o_host       = " << o_host << endl
         << "o_path       = " << o_path << endl
         << "o_ip.str()   = " << o_ip.str() << endl
         << "o_ip.port()  = " << o_ip.port() << endl
         << "o_ip.is_v4() = " << o_ip.is_v4() << endl
         << "o_ip.is_v6() = " << o_ip.is_v6() << endl
         << "o_ip.family()= " << o_ip.family() << endl
         << "o_ip.size()  = " << o_ip.size() << endl
         << endl;
    return 1;
  }
  return 0;
}
 
int check_parse_resource_address_all()
{
  int nerr = 0;
  const enum ip_address::family_sel V4 = ip_address::family_ipv4;
  const enum ip_address::family_sel V6 = ip_address::family_ipv6;
  const enum ip_address::family_sel VA = ip_address::family_any;
  nerr += check_parse_resource_address("ftp", "stfwi", "[::1]", "", "/home/me", V6, true);
  nerr += check_parse_resource_address("", "", "127.0.0.1", "21", "", VA, true);
  nerr += check_parse_resource_address("http", "", "localhost", "80", "/home/me", V4, true);
  cerr << "check_parse_resource_address_all() number of fails: " << nerr << endl;
  return nerr;
}
// </editor-fold>
 
// <editor-fold desc="check_service_lookup_all" defaultstate="collapsed">
int check_service_lookup_all()
{
  int nerr = 0;
  #define chk(X,Y) { \
    string srv = ip_address::service_by_port(X); \
    if(srv != Y) { \
      ++nerr; cerr<<"ip_address::service_by_port('"<<(X)<<"') == "<<srv<<" != "<<(Y)<<endl; \
    }\
    ip_address::port_type port = ip_address::port_by_service(Y);\
    if(port != (X)) { \
      ++nerr; cerr<<"ip_address::port_by_service('"<<(Y)<<"') == "<<port<<" != "<<(X)<<endl; \
    } \
  }
  chk(21, "ftp");
  chk(22, "ssh");
  chk(23, "telnet");
  chk(25, "smtp");
  chk(80, "http");
  chk(443, "https");
  chk(990, "ftps");
  chk(993, "imaps");
  #undef chk
  cerr << "check_service_lookup_all() number of fails: " << nerr << endl;
  return nerr;
}
// </editor-fold>
 
// <editor-fold desc="check_my_hostname" defaultstate="collapsed">
int check_my_hostname()
{
  int nerr = 0;
  string host = ip_address::my_hostname();
  if(host.empty()) {
    ++nerr; cerr << "ip_address::my_hostname() is empty" << endl;
  }
  cerr << "check_my_hostname() number of fails: " << nerr << endl;
  return nerr;
}
// </editor-fold>
 
// <editor-fold desc="hostname_by_address" defaultstate="collapsed">
int check_hostname_by_address()
{
  #define chk(X) { \
    ip_address::hostlookup_list_t hl = ip_address::host_lookup(X, "", ip_address::family_any, \
      ip_address::type_any); \
    if(!hl.empty()) { \
      string adr = hl.front().saddr; \
      string hst = hl.front().shost; \
      ip_address ip(adr); \
      string host = ip_address::hostname_by_address(ip); \
      if(host != hst) {  \
        ++nerr; cerr << "ip_address::hostname_by_address("<<adr<<") == "<<host<<" != "<<hst<<endl; \
        cerr << "     where ip_address = " << ip.str() << endl; \
      } \
    } \
  }
  int nerr = 0;
 
 
  //chk("::1");
  //chk("www.atwillys.de");
 
  #undef chk
  cerr << "hostname_by_address() number of fails: " << nerr << endl;
  return nerr;
}
// </editor-fold>
 
// <editor-fold desc="main" defaultstate="collapsed">
int main(int argc, char** argv)
{
  (void) argc; (void) argv;
  int nerr = 0;
  nerr += check_assign_all();
  nerr += check_sockaddr_in6_all();
  nerr += check_sockaddr_in4_all();
  nerr += check_parse_resource_address_all();
  nerr += check_service_lookup_all();
  nerr += check_my_hostname();
  nerr += check_hostname_by_address();
  cout << endl << "TOTAL NUMBER OF UNEXPECTED RESULTS: " << nerr << endl;
  return nerr;
}
// </editor-fold>