かってきままに!

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

非同期ソケット通信(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;

}


別スレッドからフォーム上のコントロールを操作する

2010-12-14 17:54:47 | C++/CLI


        

         //別スレッドからメインスレッドのフォーム上のコントロールのメソッドを呼ぶためにデリゲートを定義

         //引数が必要な場合
        delegate void SetFocusDelegate1(int);

        //引数が必要ない場合  
        delegate void SetFocusDelegate2();


        //別スレッドからメインスレッドのフォーム上のコントロールのメソッドを呼ぶためのデリゲートに登録するメソッドを定義
        
        //引数有の場合
        void SetFocus1(int i)  {

            //別スレッドから呼ばれた場合は if文内に入る
            if (this->InvokeRequired)  {
              
                MessageBox::Show("別スレッドから呼び出し" + i, "InvokeRequired");
               
                //別スレッドからコントロールのメソッドをそのまま呼ぶとエラー
                //this->textBox2->Focus();
               
                //別スレッドからの呼び出しによる、メインスレッドのフォーム上のコントロールのメソッドを呼ぶためのデリゲートに、実行するメソッドを登録
                SetFocusDelegate1^ focusDel = gcnew SetFocusDelegate1(this, &Form1::SetFocus1);
               
                //メインスレッドから SetFocus1() を実行してもらうために SetFocus1() が登録されたデリゲートを実行
                //二番目の引数にデリゲートに渡す引数を渡す
                this->Invoke(focusDel, i+1);

                //ここで return しないと下に流れて別スレッドから this->textBox2->Focus() が呼ばれてエラーとなる
                return;

            }


            //以下メインスレッド上から呼ばれた時の処理
            MessageBox::Show("メインスレッドから呼び出し" + i, "InvokeRequired");

            //メインスレッドからの呼び出しなのでエラーはでない
            this->textBox2->Focus();

            return;

        }


        //引数無の場合
        void SetFocus2()  {

            //別スレッドから呼ばれた場合は if文内に入る
            if (this->InvokeRequired)  {
             
                MessageBox::Show("別スレッドから呼び出し", "InvokeRequired");
               
                //別スレッドからコントロールのメソッドをそのまま呼ぶとエラー
                //this->textBox2->Focus();

                //メインスレッドから SetFocus1() を実行してもらうために SetFocus1() が登録されたデリゲートを実行

                //*デリゲートの定義も一度に行うこともできる
                this->Invoke(gcnew SetFocusDelegate2(this, &Form1::SetFocus2));

                return;

            }


            //以下メインスレッド上から呼ばれた時の処理
            MessageBox::Show("メインスレッドから呼び出し", "InvokeRequired");

            //メインスレッドからの呼び出しなのでエラーはでない
            this->textBox2->Focus();
           
            return;

        }

 

        //別スレッドで実行されるメソッドを定義
        //このメソッドは引数を取れない

        //SetFocusが引数有の場合
        void worker1(System::Object^ i)  {

            SetFocus1(safe_cast(i));

            return;

        }


        //引数無の場合
        void worker2()  {

            SetFocus2();

            return;

        }

 

    //「別スレッドでfocus引数有」ボタンが押されたとき
    private: System::Void button1_Click(System::Object^  sender, System::EventArgs^  e) {

                 //別スレッドで実行するメソッドをデリゲートに登録(スレッドとして実行するメソッドに引数を渡す場合)
                 System::Threading::ParameterizedThreadStart^ s = gcnew System::Threading::ParameterizedThreadStart(this, &Form1::worker1);

                 //別スレッドで実行するデリゲートをスレッドとして作成
                 System::Threading::Thread^ t = gcnew System::Threading::Thread(s);

                 //スレッドスタート  引数は適当
                 t->Start(15);

             }


    //「メインスレッドでfocus引数有」ボタンが押されたとき
    private: System::Void button2_Click(System::Object^  sender, System::EventArgs^  e) {

                 this->SetFocus1(10);  //引数は適当

             }

   
    //「別スレッドでfocus引数無」ボタンが押されたとき
    private: System::Void button4_Click(System::Object^  sender, System::EventArgs^  e) {

                 //別スレッドで実行するメソッドをデリゲートに登録(スレッドとして実行するメソッドに引数を渡さない場合)
                 System::Threading::ThreadStart^ s = gcnew System::Threading::ThreadStart(this, &Form1::worker2);

                 //別スレッドで実行するデリゲートをスレッドとして作成
                 System::Threading::Thread^ t = gcnew System::Threading::Thread(s);

                 t->Start();

             }

   
    //「メインスレッドでfocus引数無」ボタンが押されたとき
    private: System::Void button3_Click(System::Object^  sender, System::EventArgs^  e) {
                
                 this->SetFocus2();

             }


Array.Sort(), List.Sort() + Icomparable実装

2010-09-30 13:46:17 | C++/CLI

#using <mscorlib.dll>

enum class SortCategory {
 Number, Name
};

ref class test : System::IComparable
//ref class test : System::IComparable <test^>    //型を指定するとき
{
public:
 static SortCategory SortCat;            //ソートを行う
 static bool SortReverse;                //ソートの方向
 int value;
 System::String^ str;

 test(int n, System::String^ s) {        //コンストラクタ
  value = n;
  str = s;
 }

 virtual int CompareTo(Object^ obj) {    //Icomparable の実装

 //virtual int CompareTo(test^ obj) {    //型を指定したとき
 //型を指定した場合は safe_cast 無しで obj を test^ として使えます
 //クラスの宣言で Icomparable <test^> とする必要があります


  if(!SortReverse){        //昇順のとき

   if(SortCat == SortCategory::Name){            //名前でソート
    return this->str->CompareTo(safe_cast<test^>(obj)->str);

   }else if(SortCat == SortCategory::Number){    //番号でソート
    return this->value.CompareTo(safe_cast<test^>(obj)->value);

   }

  }else{                    //降順のとき

   if(SortCat == SortCategory::Name){            //名前でソート
    return safe_cast<test^>(obj)->str->CompareTo(this->str);

   }else if(SortCat == SortCategory::Number){    //番号でソート
    return safe_cast<test^>(obj)->value.CompareTo(this->value);

   }
  }
  throw gcnew System::Exception("エラー");
 }
};

int main(array<System::String^>^ argc)
{
 //参照クラス test の array をつくり適当にデータを与える
 array<test^>^ te = gcnew array<test^>(5);
 te[0] = gcnew test(4, "apple");
 te[1] = gcnew test(2, "pare");
 te[2] = gcnew test(5, "grape");
 te[3] = gcnew test(3, "banana");
 te[4] = gcnew test(1, "pineapple");

 //現在の内容を表示
 System::Console::WriteLine("array");
 for each(test^ t in te)
  System::Console::WriteLine(t->value + ", " + t->str);
 System::Console::WriteLine();

 //番号、昇順
 test::SortReverse = false;
 test::SortCat = SortCategory::Number;

 System::Array::Sort(te);

 for each(test^ t in te)
  System::Console::WriteLine(t->value + ", " + t->str);
 System::Console::WriteLine();
 
 //番号、降順
 test::SortReverse = true;
 test::SortCat = SortCategory::Number;

 System::Array::Sort(te);

 for each(test^ t in te)
  System::Console::WriteLine(t->value + ", " + t->str);
 System::Console::WriteLine();

 //名前、昇順
 test::SortReverse = false;
 test::SortCat = SortCategory::Name;

 System::Array::Sort(te);

 for each(test^ t in te)
  System::Console::WriteLine(t->value + ", " + t->str);
 System::Console::WriteLine();

 //名前、降順
 test::SortReverse = true;
 test::SortCat = SortCategory::Name;

 System::Array::Sort(te);

 for each(test^ t in te)
  System::Console::WriteLine(t->value + ", " + t->str);
 System::Console::WriteLine();

 
 //参照クラス test の list をつくり適当にデータを与える
 System::Collections::Generic::List<test^>^ li = gcnew System::Collections::Generic::List<test^>();
 li->Add(gcnew test(21, "blue"));
 li->Add(gcnew test(18, "red"));
 li->Add(gcnew test(28, "violet"));
 li->Add(gcnew test(11, "green"));
 li->Add(gcnew test(30, "silver"));

 //現在の内容を表示
 System::Console::WriteLine("list");
 for each(test^ t in li)
  System::Console::WriteLine(t->value + ", " + t->str);
 System::Console::WriteLine();

 //番号、昇順
 test::SortReverse = false;
 test::SortCat = SortCategory::Number;

 li->Sort();

 for each(test^ t in li)
  System::Console::WriteLine(t->value + ", " + t->str);
 System::Console::WriteLine();
 
 //番号、降順
 test::SortReverse = true;
 test::SortCat = SortCategory::Number;

 li->Sort();

 for each(test^ t in li)
  System::Console::WriteLine(t->value + ", " + t->str);
 System::Console::WriteLine();

 //名前、昇順
 test::SortReverse = false;
 test::SortCat = SortCategory::Name;

 li->Sort();

 for each(test^ t in li)
  System::Console::WriteLine(t->value + ", " + t->str);

 System::Console::WriteLine();

 //名前、降順
 test::SortReverse = true;
 test::SortCat = SortCategory::Name;

 li->Sort();

 for each(test^ t in li)
  System::Console::WriteLine(t->value + ", " + t->str);
 System::Console::WriteLine();

 return 0;
}

/*
表示結果
array
4, apple
2, pare
5, grape
3, banana
1, pineapple

1, pineapple
2, pare
3, banana
4, apple
5, grape

5, grape
4, apple
3, banana
2, pare
1, pineapple

4, apple
3, banana
5, grape
2, pare
1, pineapple

1, pineapple
2, pare
5, grape
3, banana
4, apple

list
21, blue
18, red
28, violet
11, green
30, silver

11, green
18, red
21, blue
28, violet
30, silver

30, silver
28, violet
21, blue
18, red
11, green

21, blue
11, green
18, red
30, silver
28, violet

28, violet
30, silver
18, red
11, green
21, blue
*/


array

2010-09-24 17:26:30 | C++/CLI

#include <iostream>
#using <mscorlib.dll>


ref class A {

public:

 int field;
};


int main(array<System::String ^> ^args)
{

 array<int>^ objectI = gcnew array<int>(5);

 //array は配列の情報を持つオブジェクトのハンドル
 //c の配列の様に配列の先頭を示す物ではない
 //int は自動的に 0 で初期化される


 array<A^>^ objectA = gcnew array<A^>(5);

 //配列が参照クラスの時は、ハンドルを配列にもつ
 //この時点では配列の中身は nullptr なので中身も初期化する


 for each(A^% obj in objectA)
  obj = gcnew A();

 //配列の数分 gcnew で確保する
 //for each 内で A^% としないと objectA は初期化できない
 
 //A^ obj だと
 //1.  ローカル変数 obj に objectA 内の 配列の要素 A^ がコピーされる
 //2.  obj に gcnew で新しいハンドルが入る
 //3.  obj は objectA 内の配列の要素 A^ とは別の変数なので objectA の要素は初期化されない

 //A^% obj だと
 //1.  トラッキング参照 obj が objectA 内の 配列の要素 A^ のエイリアスとなる
 //2.  obj に gcnew で新しいハンドルが入る
 //3.  obj は objectA 内の配列の要素 A^ その物なので objectA の要素は初期化される


 for each(int% n in objectI)
  n++;

 //とにかく配列そのものに手を加えたい場合は参照をつかう

 
 getchar();
  
 return 0;

}


ref class

2010-09-21 16:38:39 | C++/CLI

#include <iostream>
#using <mscorlib.dll>

ref class A {         //参照クラス ref をつけます。

public:

 int a;

};

int main(array<System::String^>^ args)
{
 A^ a = gcnew A();    //参照クラスはマネージヒープ上に作られる。返されるのはハンドルという。
                      //インスタンスはGCによってマネージヒープ上を移動する。
                      //ポインタのように演算はできない。
                      //gcnewで作成してdeleteのような後始末は勝手にやってくれるので必要ない。
                      //ただし、いつ削除されるかはわからない。

 a->a = 10;           //アクセスにはアロー演算子を使う。


 A b;                 //このようにも使える。
                      //内部的には A^ b = gcnew A となっている。
                      //この場合スコープを抜けると削除される。

 b.a = 20;            //この場合はドット演算子を使う。

 
 System::Console::WriteLine(a->a);
 System::Console::WriteLine(b.a);

 getchar();

 return 0;
}

// 表示結果
// 10
// 20