かってきままに!

わたくし、とんちゃんが日々の話題をかってきままに記していきます。

非同期ソケット通信(TCP) サーバー

2010-12-17 17:05:13 | C++/CLI

using namespace System;
using namespace System::IO;
using namespace System::Text;
using namespace System::Threading;
using namespace System::Diagnostics;
using namespace System::Net;
using namespace System::Net::Sockets;
using namespace System::Collections::Generic;


//AsyncAcceptCallback用Stateオブジェクト
ref class AsyncAcceptObject  {

public: Socket^  Listener;                               //リスナー用ソケット
          ManualResetEvent^  ConnectedEvent;   //接続完了イベント(接続 : true)

        //コンストラクタ
        AsyncAcceptObject()  {
            this->ConnectedEvent = gcnew ManualResetEvent(false);
        }

};

//AsyncRecieveCallback用Stateオブジェクト
ref class AsyncRecieveObject  {

public: Socket^  Soc;                           //通信用ソケット
          array^  ReceiveBuffer;     //受信用バッファ
          MemoryStream^  ReceivedData;  //受信データ連結用バッキングストア

        //接続中の通信用ソケットコレクション
        static initonly SynchronizedCollection^ SocketList;

        //staticなため複数のスレッドから同時にアクセス可能性があるのでスレッドセーフのものを使う
        //ただし For Eachなどの複合処理ではスレッドセーフは保証されないのでロックする必要がある
        //System.ServiceModelアセンブリへの参照設定が必要

        //コンストラクタ
        AsyncRecieveObject(Socket^ s)  {
            this->Soc = s;
            this->ReceiveBuffer = gcnew array(1024);
            this->ReceivedData = gcnew MemoryStream();
        }

        //スタティックコンストラクタ
private: static AsyncRecieveObject()  {
            SocketList = gcnew SynchronizedCollection();
         }

};

//BeginSend用非同期コールバック
void AsyncSendCallback(IAsyncResult^ ar)
{

    Socket^ socket = safe_cast(ar->AsyncState);

    //データの送信
    try  {

        //送信完了
        int bytesSent = socket->EndSend(ar); 
        Console::WriteLine("クライアントに {0} bytes 送信しました", bytesSent);

        //例外処理 (簡易的に全て System::Exception で受けています)
    }  catch(System::Exception^ e)  {  
        Debug::WriteLine("\n" + e + "\n");
    }

}

//BeginRecieve用非同期コールバック
void AsyncReceiveCallback(System::IAsyncResult^ ar)
{

    AsyncRecieveObject^ aro = safe_cast(ar->AsyncState);
    int length = 0;

    //データの受信
    try  {

        //受信完了
        length = aro->Soc->EndReceive(ar);
       
    }  catch (System::Exception^ e)  {
        //例外が発生したソケットをリストから削除
        aro->SocketList->Remove(aro->Soc);

        //ソケットを閉じる
        if(aro->Soc->Connected)
            aro->Soc->Shutdown(SocketShutdown::Both);

        aro->Soc->Close();

        Debug::WriteLine("\n" + e + "\n");
        Console::WriteLine(e->Message);
        return;

    }

    //クライアントから切断されたか調べる
    if (length <= 0)  {

        //切断したソケットをリストから削除
        aro->SocketList->Remove(aro->Soc);

        if(aro->Soc->Connected)
            aro->Soc->Shutdown(SocketShutdown::Both);
        aro->Soc->Close();

        Console::WriteLine("クライアントが切断しました");
        return;

    }

    //受信データの蓄積
    aro->ReceivedData->Write(aro->ReceiveBuffer, 0, length);

    //受信データの処理

    //ネットワークバッファのキューにデータがもうない場合(最後まで受信した時)
    if (aro->Soc->Available == 0)  {           

        //データの表示
        String^ str = Encoding::UTF8->GetString(aro->ReceivedData->ToArray());

        System::Console::WriteLine("クライアントから受信\n送信元アドレス {0}, ポート{1}\n受信内容 : {2}",
            safe_cast(aro->Soc->RemoteEndPoint)->Address,
            safe_cast(aro->Soc->RemoteEndPoint)->Port, str);

        //接続中の全てのソケットに応答メッセージを返す
        array^ SendBuffer = Encoding::UTF8->GetBytes(String::Format("サーバー受信完了\n送信元アドレス {0}, ポート{1}\n受信内容 : ",
            safe_cast(aro->Soc->RemoteEndPoint)->Address,
            safe_cast(aro->Soc->RemoteEndPoint)->Port));
  
         //コレクションのロック
        Monitor::Enter(AsyncRecieveObject::SocketList->SyncRoot);        

        for each (Socket^ socket in aro->SocketList) {
            socket->BeginSend(SendBuffer, 0, SendBuffer->Length,
                SocketFlags::None, gcnew AsyncCallback(&AsyncSendCallback), socket);
            socket->BeginSend(aro->ReceivedData->ToArray(), 0, aro->ReceivedData->ToArray()->Length,
                SocketFlags::None, gcnew AsyncCallback(&AsyncSendCallback), socket);
        }

        //アンロック
        Monitor::Exit(AsyncRecieveObject::SocketList->SyncRoot);

        //バッキングストアの初期化
        aro->ReceivedData->Close();
        aro->ReceivedData = gcnew MemoryStream();

    }

    //再び非同期受信の開始
    aro->Soc->BeginReceive(aro->ReceiveBuffer, 0, aro->ReceiveBuffer->Length,
        SocketFlags::None, gcnew AsyncCallback(&AsyncReceiveCallback), aro);

}

//BeginAccept用非同期コールバック
void AsyncAcceptCallback(System::IAsyncResult^ ar)
{

    AsyncAcceptObject^ aao = safe_cast(ar->AsyncState);
    Socket^ listener = aao->Listener;

    //接続処理
    try  {

        //接続完了&ソケットの取得
        Socket^ socket = listener->EndAccept(ar);

        Console::WriteLine("クライアントが接続しました。");
        Console::WriteLine("クライアント情報 : アドレス{0}, ポート{1}",
            safe_cast(socket->RemoteEndPoint)->Address,
            safe_cast(socket->RemoteEndPoint)->Port);

        //接続したソケットをリスト追加
        AsyncRecieveObject::SocketList->Add(socket);

        //非同期受信の開始処理

        AsyncRecieveObject^ aro = gcnew AsyncRecieveObject(socket);

        //非同期受信の開始
        socket->BeginReceive(aro->ReceiveBuffer, 0, aro->ReceiveBuffer->Length,
            SocketFlags::None, gcnew AsyncCallback(&AsyncReceiveCallback), aro);

    }  catch(System::Exception^ e)  {
        Debug::WriteLine("\n" + e + "\n");
        return;

    }

    //再び非同期にで接続受け入れを開始
    listener->BeginAccept(gcnew AsyncCallback(&AsyncAcceptCallback), aao);

    //接続イベントをセット
    aao->ConnectedEvent->Set();

}

//メイン
int main(array ^args)
{
   
    //ホスト名前を取得する場合
    //System::String^ host = System::Net::Dns::GetHostName();

    //使用するIPアドレスとポートの設定

    //ローカルホストを指定(適宜変更してください)
    String^ host = "localhost";

    //ホストのIPアドレスリストを取得
    array^ adressList = Dns::GetHostAddresses(host);

    //IPアドレスの列挙
    for each (System::Net::IPAddress^ ip in adressList) 
        System::Console::WriteLine(ip);

    IPAddress^ ipAdd;
 
    //ホストの持つIPアドレスリストからIPV4のアドレスを取得
    for each (IPAddress^ ip in adressList)
        if(ip->AddressFamily == AddressFamily::InterNetwork)         
            ipAdd = ip;

    //ポートの設定(適宜変更してください)
    int port = 2001; 

    //Listenの開始処理

    AsyncAcceptObject^ aao = gcnew AsyncAcceptObject();

    //リスナーの作成
    aao->Listener =
        gcnew Socket(AddressFamily::InterNetwork, SocketType::Stream, ProtocolType::Tcp);

    //使用するIPアドレス&ポートを設定
    aao->Listener->Bind(gcnew IPEndPoint(ipAdd, port));           
    aao->Listener->Listen(10);

    //Listen開始
    aao->Listener->BeginAccept(gcnew AsyncCallback(&AsyncAcceptCallback), aao);


    //1つの接続が確立するまで待機
    aao->ConnectedEvent->WaitOne();


    //終了処理

    Console::ReadLine();
    Console::WriteLine("終了します");

    //コレクションのロック
    Monitor::Enter(AsyncRecieveObject::SocketList->SyncRoot);

    try  {

        //接続中のソケットを全て切断
        for each (Socket^ socket in AsyncRecieveObject::SocketList)  {
            if(socket->Connected)
                socket->Shutdown(SocketShutdown::Both);

            socket->Close();
        }

    }  catch(System::Exception^ e)  {
        Debug::WriteLine("\n" + e + "\n");

    }

    //アンロック
    Monitor::Exit(AsyncRecieveObject::SocketList->SyncRoot);         

    Console::ReadLine();
    return 0;

}


最新の画像もっと見る

コメントを投稿