text: ﻿//======================================================================
  //-----------------------------------------------------------------------
  /**
   * @file        iutest_socket.hpp
   * @brief       soket
   *
   * @author      t.shirayanagi
   * @par         copyright
   * Copyright (C) 2013-2016, Takazumi Shirayanagi\n
   * This software is released under the new BSD License,
   * see LICENSE
  */
  //-----------------------------------------------------------------------
  //======================================================================
  #ifndef INCG_IRIS_IUTEST_SOCKET_HPP_77654A63_0A08_43CA_950E_61232690163B_
  #define INCG_IRIS_IUTEST_SOCKET_HPP_77654A63_0A08_43CA_950E_61232690163B_
  
  //======================================================================
  // include
  #include "iutest_internal_defs.hpp"
  #include "iutest_stream.hpp"
  
  #ifdef IUTEST_HAS_SOCKET
  
  #ifdef IUTEST_OS_WINDOWS
  #  include <winsock2.h>
  #  include <ws2tcpip.h>
  #else
  #  include <arpa/inet.h>
  #  include <netdb.h>
  #endif
  
  #if defined(_MSC_VER)
  #  if defined(IUTEST_OS_WINDOWS_MOBILE)
  #    pragma comment(lib, "ws2.lib")
  #  else
  #    pragma comment(lib, "ws2_32.lib")
  #  endif
  #endif
  
  namespace iutest {
  namespace detail
  {
  
  //======================================================================
  // class
  /**
   * @brief   ソケットベースクラス
  */
  class BasicSocket
  {
  public:
  #ifdef IUTEST_OS_WINDOWS
      typedef SOCKET descriptor_t;
      typedef int length_t;
  
  #if !defined(IUTEST_NO_INCLASS_MEMBER_INITIALIZATION)
      static const descriptor_t INVALID_DESCRIPTOR = INVALID_SOCKET;
  #else
      enum { INVALID_DESCRIPTOR = INVALID_SOCKET };
  #endif
  
  #else
      typedef int descriptor_t;
      typedef size_t length_t;
  
  #if !defined(IUTEST_NO_INCLASS_MEMBER_INITIALIZATION)
      static const descriptor_t INVALID_DESCRIPTOR = -1;
  #else
      enum { INVALID_DESCRIPTOR = -1 };
  #endif
  
  #endif
  public:
      BasicSocket() : m_socket(INVALID_DESCRIPTOR)
      {
  #ifdef IUTEST_OS_WINDOWS
          WSADATA wsaData;
          (void)WSAStartup(MAKEWORD(2, 2), &wsaData);
  #endif
      }
      ~BasicSocket(void)
      {
          Close();
  #ifdef IUTEST_OS_WINDOWS
          WSACleanup();
  #endif
      }
  public:
      bool Open(const char* host, const char* port)
      {
          if( m_socket != INVALID_DESCRIPTOR )
          {
              return true;
          }
          addrinfo* servinfo = NULL;
          addrinfo hints;
          memset(&hints, 0, sizeof(hints));
          hints.ai_family = AF_UNSPEC;
          hints.ai_socktype = SOCK_STREAM;
          const int err_no = getaddrinfo(host, port, &hints, &servinfo);
          if( err_no != 0 )
          {
              return false;
          }
  
          for( addrinfo* curr=servinfo; curr != NULL; curr = curr->ai_next )
          {
              const descriptor_t fd = socket(curr->ai_family, curr->ai_socktype, curr->ai_protocol);
              if( fd != INVALID_DESCRIPTOR )
              {
                  if( connect(fd, curr->ai_addr, static_cast<length_t>(curr->ai_addrlen)) != -1 )
                  {
                      m_socket = fd;
                      break;
                  }
                  Close(fd);
              }
          }
          freeaddrinfo(servinfo);
          return (m_socket != INVALID_DESCRIPTOR);
      }
      void Close()
      {
          Close(m_socket);
          m_socket = INVALID_DESCRIPTOR;
      };
      void CheckLastError()
      {
  #ifdef IUTEST_OS_WINDOWS
          const int le = WSAGetLastError();
          IUTEST_LOG_(WARNING) << "WSAGetLastError:" << le;
  #endif
      }
  public:
      static int Close(descriptor_t d)
      {
  #ifdef IUTEST_OS_WINDOWS
          return closesocket(d);
  #else
          return close(d);
  #endif
      }
  
  public:
      bool IsValid() const
      {
          return m_socket != INVALID_DESCRIPTOR;
      }
  protected:
      descriptor_t m_socket;
  
      IUTEST_PP_DISALLOW_COPY_AND_ASSIGN(BasicSocket);
  };
  
  /**
   * @brief   ソケット書き込みクラス
  */
  class SocketWriter : virtual public BasicSocket
      , public IOutStream
  {
  public:
      SocketWriter() {}
  public:
      bool Send(const ::std::string& message)
      {
          return Write(message.c_str(), message.length(), 1u);
      }
      bool SendLn(const ::std::string& message)
      {
  #ifdef IUTEST_OS_WINDOWS
          return Send(message + "\r\n");
  #else
          return Send(message + "\r\n");
          //return Send(message + "\n");
  #endif
      }
  public:
      virtual bool Write(const void* buf, size_t size, size_t cnt) IUTEST_CXX_OVERRIDE
      {
          if( !IsValid() )
          {
              return false;
          }
          const int size_ = static_cast<int>(size);
          for( size_t i=0; i < cnt; ++i )
          {
  #ifdef IUTEST_OS_WINDOWS
              if( send(m_socket, static_cast<const char*>(buf), size_, 0) == SOCKET_ERROR )
              {
                  CheckLastError();
                  return false;
              }
  #else
              if( write(m_socket, buf, size_) == -1 )
              {
                  return false;
              }
  #endif
          }
          return true;
      }
  private:
      IUTEST_PP_DISALLOW_COPY_AND_ASSIGN(SocketWriter);
  };
  
  /*
   * @brief   ソケット読み込みクラス
  */
  class SocketReader : virtual public BasicSocket
  {
  public:
      SocketReader() {}
  public:
      bool Read(void* buf, size_t size)
      {
          if( !IsValid() )
          {
              return false;
          }
          const int size_ = static_cast<int>(size);
  #ifdef IUTEST_OS_WINDOWS
          if( recv(m_socket, static_cast<char*>(buf), size_, 0) == SOCKET_ERROR )
          {
              CheckLastError();
              return false;
          }
  #else
          if( read(m_socket, buf, size_) == -1 )
          {
              return false;
          }
  #endif
          return true;
      }
  private:
      IUTEST_PP_DISALLOW_COPY_AND_ASSIGN(SocketReader);
  };
  
  /*
   * @brief   ソケット読み込みクラス
  */
  class Socket : public SocketWriter, public SocketReader
  {
  public:
      Socket() {}
  };
  
  }   // end of namespace detail
  }   // end of namespace iutest
  
  #endif
  
  #endif // INCG_IRIS_IUTEST_SOCKET_HPP_77654A63_0A08_43CA_950E_61232690163B_
  