/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   2007/X/X - 2015/11/13
/// 
/// @brief  TCPソケット関連
/// 
/// @par Note
/// TCPソケットクラス\n\n
/// ＜ネットワークの基本仕様＞\n
/// ・一度に送信指示するデータは「ブロック」として扱う。\n
/// ・送信ブロックは１つ以上のバッファで構成される。\n
/// ・ただし、受信ブロックは必ず１つのバッファとして扱われる。\n
/// ****************************************************************************

#include "CPL_WIN_WS_SocketTCP.h"

namespace CPL {
namespace WIN {
  
// _____________________________________________________________________________
/// @brief ソケットコピー付コンストラクタ
///
/// @param[in] s
///   : WinSock の SOOCKETを渡す。
/// @param[in] in_sp_socket_parent
///   : 属する(親)ソケットのスマートポインタを渡す。
///     属さない場合はnullptrを指定する。基本nullptrでＯＫ。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SocketTCP::SocketTCP( SOCKET s, _in<SpSocket> in_sp_socket_parent )
  : SocketBase( s, in_sp_socket_parent )
{
}

// _____________________________________________________________________________
/// @brief ソケット作成付コンストラクタ
///
/// @param[in] in_AF
///   : アドレスファミリ。基本的にAF_INET(IPv4)かAF_INET6(IPv6)。
///     socket()の第一引数に渡すので、詳細は検索するべし。
/// @param[in] in_type
///   : ソケットタイプ。
///     socket()の第二引数に渡すので、詳細は検索するべし。
/// @param[in] in_protocol
///   : プロトコル。基本0でＯＫ？。
///     socket()の第三引数に渡すので、詳細は検索するべし。
/// @param[in] in_sp_socket_parent
///   : 属する(親)ソケットのスマートポインタを渡す。
///     属さない場合はnullptrを指定する。基本nullptrでＯＫ。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SocketTCP::SocketTCP
( _in<int     > in_AF
, _in<int     > in_type
, _in<int     > in_protocol
, _in<SpSocket> in_sp_socket_parent
) : SocketBase( in_AF, in_type, in_protocol, in_sp_socket_parent )
{
}


// _____________________________________________________________________________
/// @brief ソケット作成付コンストラクタ。
///
/// @param[in] in_hostname
///   : ホスト名。アドレスかホスト名を文字列で指定する。
///     getaddrinfo()の第一引数に渡すので、詳細は検索するべし。
/// @param[in] in_service
///   : サービス名かポート番号を文字列で指定。
///     getaddrinfo()の第二引数に渡すので、詳細は検索するべし。
/// @param[in] in_sp_socket_parent
///   : 属する(親)ソケットのスマートポインタを渡す。
///     属さない場合はnullptrを指定する。基本nullptrでＯＫ。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SocketTCP::SocketTCP
( pin<char    > in_hostname
, pin<char    > in_service
, _in<SpSocket> in_sp_socket_parent
) : SocketBase( in_hostname, in_service, in_sp_socket_parent )
{
}

  
// _____________________________________________________________________________
/// @brief たぶん作成済みソケットを渡す仕様。
///   詳細は忘れたが、面倒くさいので未調査。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SocketTCP::SocketTCP
( _in<SOCKET  > s
, pin<sockaddr> in_ptr_sockaddr
, _in<int     > in_sockaddr_len
, _in<SpSocket> in_sp_socket_parent
) : SocketBase( s, in_sp_socket_parent )
{
  m_buf_sockaddr.resize( in_sockaddr_len );
  CPL_MEMCPY( &m_buf_sockaddr[0], in_ptr_sockaddr, in_sockaddr_len );
}

  
// _____________________________________________________________________________
/// @brief デストラクタ
///
/// 保持するスレッドの終了を待つ。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SocketTCP::~SocketTCP()
{
  //Close();

  this->ExitOn(); // 離脱イベントON
  // スレッド終了待機
  CPL_SAFE_PTR(m_sp_thread_recv)->Join();
  CPL_SAFE_PTR(m_sp_thread_send)->Join();
}



// _____________________________________________________________________________
/// @brief 受信キャッシュのデータの有無をチェック。
///
/// @retval true  : データ有り。
/// @retval false : データ無し。
///
/// @note
///   他スレッドでデータ有無の確実性を担保する場合、外部でロックが必要。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool SocketTCP::zzf_IsRecvCache() const
{
  if( !m_sp_thread_recv )
  {
    return false;
  }

  auto locking =
    LockMutex
      ( m_sp_thread_recv->m_mutex_data
      , m_sp_thread_recv->m_mutex_request
      );

  if( m_sp_thread_recv->m_recv_buffer_queue.IsEmpty() == false  &&
      m_sp_thread_recv->m_recv_buffer_queue.GetFrontBuffer()->size() > 0 )
  {
    return true;
  }
  else
  {
    return false;
  }
}

// _____________________________________________________________________________
/// @brief 送信キャッシュのデータの有無をチェック。
///
/// @retval true  : データ有り。
/// @retval false : データ無し。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool SocketTCP::zzf_IsSendCache() const
{
  if( !m_sp_thread_send ) return false;

  return (m_sp_thread_send->m_send_data_queue.IsEmpty() == 0);
}




// _____________________________________________________________________________
/// @brief クローズ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int SocketTCP::Close()
{
  auto locking =
    LockMutex
      ( m_sp_thread_send->m_mutex_data
      , m_sp_thread_recv->m_mutex_data
      );

  return SocketBase::Close();
}


// _____________________________________________________________________________
/// @copydoc Core::Receiver::Recv(rout<ByteBuffer>,_in<TimePoint>)
/// 
/// @note
///   キャッシュに受信途中のデータがあれば、受信済みデータまで読み込む。\n
///   リクエストブロックの終端まで読み込むと、続くデータがあったとしても一度処理を返す。
/// @note
///   １リクエストに複数のセグメントがあっても、バッファは統合される。\n
///   セグメント単位で分割する場合、GetRecvReport関数により情報を取得し、自前で処理すること。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Core::eResultTransRecv
  SocketTCP::Recv
    ( rout<ByteBuffer> out_buffer
    , _in <TimePoint > in_time_point_end
    )
{
  //Core::String module_name;
  //GetModuleName( module_name );
  //str_type str[ 1024 ];
  //CPL_SPRINTF( str,
  //  SwS("%s: [P:%d][T:%d]: SocketTCP::Recv(): AutoLockRecvCacheAccess"),
  //  (str_cptr)module_name,
  //  (int)GetCurrentProcessId(),
  //  (int)Core::GetThreadID() );

  auto locking =
    LockMutex
      ( m_sp_thread_recv->m_mutex_data
      , m_sp_thread_recv->m_mutex_request
      );

  if( zzf_IsRecvCache() ) // データがある場合。
  {
    try
    {
      // 受信完了フラグを取得。
      ThreadRecv::eFlagComplete
        f_complete = m_sp_thread_recv->IsCompleteRequest();

      // 最も古い受信ブロックの、受信済み全データをバッファにコピーする。
      m_sp_thread_recv->m_recv_buffer_queue.Subtraction( out_buffer );

      // 受信済みブロックがある場合、そのブロックを削除。
      if( f_complete == ThreadRecv::eFlagComplete::ReceivedCache )
      {
        m_sp_thread_recv->m_recv_request_queue.pop_front();
        m_sp_thread_recv->m_recv_buffer_queue.Pop();
      }
      
      CPL_WIN_OUTPUT_EX_A( "%s\n", (char_cptr)&out_buffer[0] );

      switch( f_complete )
      {
      case ThreadRecv::eFlagComplete::NoneData:
        return Core::eResultTransRecv::NoneData;
      case ThreadRecv::eFlagComplete::Receiving:
        return Core::eResultTransRecv::Receiving;
      case ThreadRecv::eFlagComplete::ReceivedCache:
        return Core::eResultTransRecv::Received;
      }
      return Core::eResultTransRecv::Error; // ここが処理されることは無い。はず。
    }
    catch(...)
    {
      return Core::eResultTransRecv::Error;
    }
  }

  return Core::eResultTransRecv::NoneData;
}


// _____________________________________________________________________________
/// @brief 受信データのレポートを取得。
///
/// @param[out] out_report_table
///   : ReportInfoBufferの配列クラス(ReportInfoBufferTable = vector<ReportInfoBuffer>)への参照。
///
/// @retval true  : レポートを取得した。
/// @retval false : データが無い。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int SocketTCP::GetRecvReport( ReportInfoBufferTable& out_report_table ) const
{
  Core::String module_name;
  GetModuleName( module_name );
  str_type str[ 1024 ];
  CPL_SPRINTF
    ( str
    , SwS("%s: [P:%d][T:%d][%s]: AutoLockRecvCacheAccess")
    , (str_cptr)module_name
    , (int)GetCurrentProcessId()
    , (int)Core::GetThreadID()
    , SwS(CPL_FUNCNAME)
    );
  //AutoLockRecvCacheAccess lock( str );
  
  auto locking_req = LockMutex( m_sp_thread_recv->m_mutex_request );

  if( !zzf_IsRecvCache() )
  {
    return false;
  }
  else // データがある場合。
  {
    RequestInfoBuffer req_info = m_sp_thread_recv->m_recv_request_queue.front();
    U32 seg_num = req_info->m_segment_num;
    ReportInfoBuffer repo_info( ReportInfo::ComputeSize( seg_num ) );
    repo_info->socket_ID = static_cast<U32>(m_socket);
    repo_info->size      = m_sp_thread_recv->m_read_size;
    //repo_info->complete  = (m_sp_thread_recv->IsCompleteRequest() == ThreadRecv::eFlagComplete::ReceivedCache);
    repo_info->complete_num = m_sp_thread_recv->GetCompleteSegmentCacheNum();
    repo_info->segment_num  = seg_num;
    CPL_LOOP2( seg_num, idx )
    {
      repo_info->segment_size[ idx ] = req_info->m_segment[ idx ].m_size;
    }
    out_report_table.push_back( repo_info );

    return true;
  }
}


// _____________________________________________________________________________
/// @copydoc Core::Sender::Send(pin<void>,_in<size_t>,_in<TimePoint>,pout<U32>)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Core::eResultTransSend
  SocketTCP::Send
    ( pin <void     > in_ptr_buf
    , _in <size_t   > in_size_send
    , _in <TimePoint> in_time_point_end
    , pout<U32      > out_send_ID
    )
{
  SpByteBuffer sp_buffer_table[] = {
    std::make_shared<ByteBuffer>( in_size_send )
  };

  // コピー
  memcpy( &(*sp_buffer_table[0])[0], in_ptr_buf, in_size_send );

  // 送信スレッド作成
  U32 send_ID = CreateThreadSend( &sp_buffer_table[0], 1 );

  if( out_send_ID )
  {
    *out_send_ID = send_ID;
  }

  if( m_sp_thread_send->WaitSendRequest( send_ID, in_time_point_end ) )
  {
    return Core::eResultTransSend::Timeout_Myself;
  }

  return Core::eResultTransSend::Success;
}

// _____________________________________________________________________________
/// @copydoc Core::Sender::Send(cref<ByteBuffer>,_in<TimePoint>,pout<U32>)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Core::eResultTransSend
  SocketTCP::Send
    ( cref<ByteBuffer> in_buffer
    , _in <TimePoint > in_time_point_end
    , pout<U32       > out_send_ID
    )
{
  return Send( &in_buffer[0], in_buffer.size(), in_time_point_end, out_send_ID );
}

// _____________________________________________________________________________
/// @copydoc Core::Sender::Send(rvref<ByteBuffer>,_in<TimePoint>,pout<U32>)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Core::eResultTransSend
  SocketTCP::Send
    ( rvref<ByteBuffer> in_buffer
    , _in  <TimePoint > in_time_point_end
    , pout <U32       > out_send_ID
    )
{
  return Send( &in_buffer[0], in_buffer.size(), in_time_point_end, out_send_ID );
}

// _____________________________________________________________________________
/// @brief 送信用基本関数
///
/// スマートポインタを指定して送信するSend()の共通関数。
///
/// @param[in] in_buffer_table
///   : 送信するデータが格納されたバッファポインタの配列。\n
///     一度に指定できる要素数は最大でCPL_WIN_WS_SOCKET_SEGMENT_MAX.
/// @param[in] in_buf_table_num
///   : in_buffer_tableの要素数。
/// @param[in] in_time_point_end
///   : タイムアウトとする時点を指定。
/// @param[in] in_flag_copy
///   : 指定したバッファを内部でコピー保持するかどうか。
/// @param[out] out_send_ID
///   : 送信リクエストIDを取得する変数(U32)へのポインタ。null可。
///
/// @retval FLAg_SEND_SUCCESS : 成功
/// @retval FLAg_SEND_ERROR   : 失敗
/// @retval FLAg_SEND_TIMEOUT : タイムアウト
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Core::eResultTransSend
  SocketTCP::zzf_Send
    ( cptr<SpByteBuffer> in_buffer_table
    , _in<int          > in_buf_table_num
    , _in<TimePoint    > in_time_point_end
    , _in<bool         > in_flag_copy
    , pout<U32         > out_send_ID
    )
{
  try
  {
    
#ifdef CPL_DBGSW_WINSOCK_send
    { // デバッグ用メッセージ
      Core::String module_name;
      GetModuleName( module_name );
      CPL_WIN_OUTPUT_EX
        ( "[seg_num:%d]"
        , in_buf_table_num
        );
      CPL_LOOP2( in_buf_table_num, idx )
      {
        if( idx == 0 )
        {
          CPL_OUTPUT(
            SwS("[size:(%d,)%d"),
            RequestInfo::ComputeSize(in_buf_table_num),
            (int)in_buffer_table[idx]->size() );
        }
        else
        {
          CPL_OUTPUT( SwS(",%d"), (int)in_buffer_table[idx]->size() );
        }
        if( idx == in_buf_table_num-1 )
        {
          CPL_OUTPUT( SwS("]") );
        }
      }
      CPL_OUTPUT( SwS("\n") );
    }
#endif

    U32 send_ID = -1;

    // コピー判定。
    if( in_flag_copy )
    {
      Core::unique_ptr<SpByteBuffer[]>
        sp_buffer_table( CPL_NEW(SpByteBuffer)[ in_buf_table_num ] );
      // コピー用バッファ作成とデータコピー。
      CPL_LOOP2( in_buf_table_num, idx )
      {
        // バッファ作成
        size_t src_buf_size = in_buffer_table[ idx ]->size();
        sp_buffer_table[ idx ] = CPL_MAKE_SHARED( ByteBuffer, src_buf_size );
        // コピー
        void_ptr  ptr_buf_dest = &(*sp_buffer_table[ idx ])[0];
        void_cptr ptr_buf_src  = &(*in_buffer_table[ idx ])[0];
        CPL_MEMCPY( ptr_buf_dest, ptr_buf_src, src_buf_size );
      }
      // 送信スレッド作成
      send_ID = CreateThreadSend( &sp_buffer_table[0], in_buf_table_num );
    }
    else
    {
      // 送信スレッド作成
      send_ID = CreateThreadSend( in_buffer_table, in_buf_table_num );
    }

    if( out_send_ID )
    {
      *out_send_ID = send_ID;
    }

    if( m_sp_thread_send->WaitSendRequest( send_ID, in_time_point_end ) )
    {
      return Core::eResultTransSend::Timeout_Myself;
    }
    return Core::eResultTransSend::Success;
  }
  catch(...)
  {
    return Core::eResultTransSend::Success;
  }
}

//// _____________________________________________________________________________
///// @copydoc Core::Sender::Send(_in<SpByteBuffer>,_in<TimePoint>,_in<bool>,pout<U32>)
//// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Core::ResultTransSend
//  SocketTCP::Send
//    ( _in <SpByteBuffer> in_sp_buffer
//    , _in <TimePoint   > in_time_point_end
//    , _in <bool        > in_flag_copy
//    , pout<U32         > out_send_ID
//    )
//{
//  SpByteBuffer buf_tbl[] = { in_sp_buffer };
//  return zzf_Send( buf_tbl, 1, in_time_point_end, in_flag_copy, out_send_ID );
//}


//// _____________________________________________________________________________
///// @copydoc Core::Sender::Send(cptr<SpByteBuffer>,_in<int>,_in<TimePoint>,_in<bool>,pout<U32>)
//// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Core::ResultTransSend
//  SocketTCP::Send
//    ( cptr<SpByteBuffer> in_buffer_table
//    , _in <int         > in_buf_table_num
//    , _in <TimePoint   > in_time_point_end
//    , _in <bool        > in_flag_copy
//    , pout<U32         > out_send_ID
//    )
//{
//  return
//    zzf_Send
//      ( in_buffer_table
//      , in_buf_table_num
//      , in_time_point_end
//      , in_flag_copy
//      , out_send_ID
//      );
//}


// _____________________________________________________________________________
/// @brief 送信データのレポートを取得。
///
/// @param[out] out_report_table
///   : ReportInfoBufferの配列クラス(ReportInfoBufferTable = vector<ReportInfoBuffer>)への参照。
///
/// @retval true  : レポートを取得した。
/// @retval false : データが無い。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int SocketTCP::GetSendReport( ReportInfoBufferTable& out_report_table ) const
{
  Core::String module_name;
  GetModuleName( module_name );
  str_type str[ 1024 ];
  CPL_SPRINTF( str,
    SwS("%s: [P:%d][T:%d]: SocketTCP::GetSendReport(): AutoLockRecvCacheAccess"),
    (str_cptr)module_name,
    (int)GetCurrentProcessId(),
    (int)Core::GetThreadID() );
  //AutoLockSendCacheAccess lock( str );

  auto locking_req = LockMutex( m_sp_thread_recv->m_mutex_request );

  if( !zzf_IsSendCache() )
  {
    return false;
  }
  else // データがある場合
  {
    RequestInfoBuffer req_info = m_sp_thread_send->m_send_request_queue.front();
    U32 seg_num = req_info->m_segment_num;
    ReportInfoBuffer repo_info( ReportInfo::ComputeSize( seg_num ) );
    repo_info->socket_ID    = static_cast<U32>(m_socket);
    repo_info->block_size   = req_info->m_size;
    repo_info->size         = m_sp_thread_send->m_send_size;
    //repo_info->complete   = (m_sp_thread_send->m_send_buffer_queue.size() > 0);
    repo_info->complete_num = m_sp_thread_send->GetCompleteSegmentCacheNum();
    repo_info->segment_num  = seg_num;
    CPL_LOOP2( seg_num, idx )
    {
      repo_info->segment_size[ idx ] = req_info->m_segment[ idx ].m_size;
    }

    out_report_table.push_back( repo_info );

    return true;
  }
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// イベント待機（拡張イベント付）
//  引数 :
//    io_event_param : イベント待機用パラメータ。EventParam参照。
//  戻り値 :
//    成功すると０、失敗するとSOCKET_ERRORを返す。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//int SocketTCP::WaitEvents( EventParam& io_event_param ) const
//{
//  if( io_event_param.in_events )
//  {
//    WSAEVENT evt[16] = { 0 };
//    evt[0] = m_evt_send.GetHandle();
//    CPL_LOOP2( io_event_param.in_event_num, idx )
//    {
//      evt[ idx+1 ] = io_event_param.in_events[ idx ];
//    }
//    io_event_param.in_events    = evt;
//    io_event_param.in_event_num += 1;
//    int ret = SocketBase::WaitEvents( io_event_param );
//    io_event_param.in_events = nullptr;
//    if( ret == 0  &&  io_event_param.out_event_index == 2 )
//    {
//      io_event_param.out_event_flags |= FD_WRITE;
//    }
//    return ret;
//  }
//  else
//  {
//    WSAEVENT evt = m_evt_send.GetHandle();
//    io_event_param.in_events    = &evt;
//    io_event_param.in_event_num = 1;
//    int ret = SocketBase::WaitEvents( io_event_param );
//    io_event_param.in_events = nullptr;
//
//    if( ret == 0  &&  io_event_param.out_event_index == 2 )
//    {
//      io_event_param.out_event_flags |= FD_WRITE;
//    }
//    return ret;
//  }
//}


// _____________________________________________________________________________
/// @brief 送信スレッド作成。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void SocketTCP::CreateThreadRecv()
{
  m_sp_thread_recv = CPL_MAKE_SHARED( ThreadRecv, this );
  m_sp_thread_recv->Start();
}


// _____________________________________________________________________________
/// @brief 送信スレッドの作成と、送信データの追加。
/// 
/// @param[in] in_buffer_table
///   : データバッファを指すスマートポインタの配列（の先頭ポインタ）
/// @param[in] in_table_num
///   : in_buffer_tableの配列数。
/// 
/// @return 転送リクエストのIDを返す。エラーの場合-1が返る。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
U32
  SocketTCP::CreateThreadSend
    ( SpByteBuffer const in_buffer_table[]
    , int                in_table_num
    )
{
  if( m_sp_thread_send )
  {
    // データ追加（スレッドは自動で作成される）
    return
      m_sp_thread_send->AddData(
        in_buffer_table,
        in_table_num );
  }
  else
  {
    // 送信スレッドクラス作成（スレッドは自動で作成される）
    U32 get_ID;
    m_sp_thread_send =
      CPL_MAKE_SHARED
        ( ThreadSend
        , this, in_buffer_table, in_table_num, &get_ID
        );
    return get_ID;
  }
}




// インスタンス化（コンパイルエラーチェック用）
//static SocketTCP s;

} // namespace WIN
} // namespace CPL
