winsockを使ってみる。(2) -acceptスレッド-

前回(winsockを使ってみる。(1) - while( c++ );)、コネクションを作って簡単な送受信を行いましたが、acceptはいわゆるブロッキング関数であり、クライアントが接続してくるまで、またはサーバソケットが閉じられるまで、呼び出し元スレッドが停止します。このままでは複数のクライアントと通信する場合やサーバ側で何らかの処理を行う場合に不都合なので、サブスレッドでacceptを行います。

  • メインスレッドからacceptスレッドを作成

  • acceptスレッドでクライアントのconnect待ち

  • とりあえずコネクション確立時に文字列を送り返す

  • サーバソケットを閉じるとacceptスレッド終了


hogeテンプレートライブラリ

  • smart_ptr
    • shared_ptr
    • weak_ptr
    • intrusive_ptr
    • scoped_ptr
    • nullptr
    • enable_shared_from_this
  • thread
    • thread
    • event

Server

/**
 *  acceptスレッド実行関数
 */
struct AcceptThreadFunc
{
    void operator()()
    {
        for( ; ; )
        {
            //クライアントの接続待ち
            std::cout << "クライアントの接続待ち" << std::endl;
            SocketPtr client = socket->accept();
            if( client == nullptr )break;

            //ソケットからストリームを取得
            SocketStreamWeakPtr stream = client->get_stream();
            //適当な文字列を送る
            stream->write_line( "hoge" );
        }
    }

    AcceptThreadFunc( const ServerSocketPtr& socket )
        : socket( socket )
    {}

    ServerSocketPtr socket;
};
#include <net/socket.h>
#include <hoge/thread/thread.h>

int main()
{
    try
    {
        //winsock初期化
        net::startup();
        //指定ポートでサーバ用TCPソケットを作成
        ServerSocketPtr socket = IServerSocket::create( 12345 );

        //acceptスレッド開始
        hoge::thread_ptr accept_thread = hoge::thread::create(
            AcceptThreadFunc( socket )
            );
        accept_thread->start();

        for( ; ; )
        {
            if( GetAsyncKeyState( VK_ESCAPE ) & 0x8000 )break;
        }

        //ソケットを閉じて、acceptスレッドを終了させる
        socket->close();
        //acceptスレッドの終了待ち
        accept_thread->join();
    }
    catch( net::socket_error& e )
    {
        std::cout << e.what() << std::endl;
    }


    //winsock破棄
    net::cleanup();

    return 0;
}
  • ISocketにcloseメソッドを追加した。
  • 例外発生時にacceptスレッドが正常終了しないですが、とりあえず気にしない。
  • Escキーで終了。

Client

#include <net/socket.h>

int main()
{
    try
    {
        using namespace net::tcp;
        //winsock初期化
        net::startup();
        //クライアント用TCPソケットを作成
        ClientSocketPtr socket = IClientSocket::create();
        //指定ポート、IPアドレスのサーバに接続
        socket->connect( 12345, "localhost" );
        //ソケットからストリームを取得
        SocketStreamWeakPtr stream = socket->get_stream();
        //1行分の文字列を受信
        std::string str;
        stream->read_line( str );
        std::cout << str;
    }
    catch( net::socket_error& e )
    {
        std::cout << e.what() << std::endl;
    }

    net::cleanup();
    return 0;
}
サンプル