LoginSignup
1
2

More than 5 years have passed since last update.

コンピュータ将棋ソフトとの対局サーバーを立てよう<その11>

Last updated at Posted at 2017-03-13

前回の記事 : http://qiita.com/muzudho1/items/6fbe1681a7e14dcac3f2

バグの原因を調べよう

コンシューマの受信ループを 別スレッドにしてから おかしくなった気がする。

じゃあ、コンシューマの受信ループは 別スレッドにせず、
プロデューサーの送信を 別スレッドにしたらいいのか?

試すだけ試そう。

まず、ev_loop がエラーを出すソースコード。

// OS      : Windows10 : // このプログラムは動かない
//
// OS      : Ubuntu 16.04
// Library : libev
//         : Install   : Command  : sudo apt-get update
//                                : sudo apt-get install libev-dev
// Service : RabbitMQ
//         : Reference : Web site : Top page http://www.rabbitmq.com/
//         : Install   : Web site : Installing on Debian / Ubuntu http://www.rabbitmq.com/install-debian.html
//         : Manual    : Command  : man rabbitmqctl
//         : Start     : Command  : rabbitmq-server
//         : Stop      : Command  : rabbitmqctl stop
//         : Check     : Command  : rabbitmqctl status
//         :           : Command  : rabbitmqctl list_queues
// Library : AMQP-CPP
//         : Reference : Web site : AMQP-CPP README.md https://github.com/CopernicaMarketingSoftware/AMQP-CPP
//         : Reference : Web site : QueueDeclare http://docs.spring.io/spring-amqp-net/docs/1.0.x/api/html/Spring.Messaging.Amqp.Rabbit~Spring.Messaging.Amqp.Rabbit.Connection.CachedModel~QueueDeclare(String,Boolean,Boolean,Boolean,Boolean,Boolean,IDictionary).html
//         : Reference : Web site : EventingBasicConsumer https://www.rabbitmq.com/releases/rabbitmq-dotnet-client/v1.4.0/rabbitmq-dotnet-client-1.4.0-net-2.0-htmldoc/type-RabbitMQ.Client.Events.EventingBasicConsumer.html
//         : Reference : Web site : BasicConsume https://www.rabbitmq.com/releases/rabbitmq-dotnet-client/v1.4.0/rabbitmq-dotnet-client-1.4.0-net-2.0-htmldoc/type-RabbitMQ.Client.IModel.html#method-M:RabbitMQ.Client.IModel.BasicConsume(System.UInt16,System.String,System.Boolean,System.Collections.IDictionary,RabbitMQ.Client.IBasicConsumer)
//         : Reference : Web site : C#でconstな配列を実現する (もっとクールにプログラミング) http://pgnote.net/?p=885
//         : Reference : Web site : AMQP-CPP (docsforge.com) http://docsforge.com/11/AMQP-CPP/docs/latest/namespace-AMQP/class-TcpConnection/
//
// Program : this
//         : Compile   : Command  : g++ -std=c++11 tamesi33a2_cpp.cpp -o tamesi33a2_cpp.exe -lev -lamqpcpp -pthread
//         : Execute   : Command  : // バックグラウンドで実行
//                                : ./tamesi33a2_cpp.exe --msgqueue 1117 durable 1116 durable 2> ./tamesi33a2_cpp.err.log &
//         : Stop      : Typing   : [Ctrl]+[C]
//
// メッセージの末尾に daze を付けます。
// デキューは「1112」、エンキューは「1113」キューに向けて行います。
// 標準入出力は、キーボード、画面には接続しないようにします。

#include <string> // std::string
#include <iostream> // std::cout
#include <sstream> // std::ostringstream
#include <chrono>
#include <thread> // sleep_for
#include <future> // thread

// プロセス間通信用
#include <ev.h>
#include <amqpcpp.h>
#include <amqpcpp/libev.h>

static AMQP::Address ADDRESS{ "amqp://localhost:5672" };
// 0 : enqueue用設定
// 1 : dequeue用設定
static const int ENQUEUE_INDEX = 0;
static const int DEQUEUE_INDEX = 1;
static const int NUM_INDEX = 2;
static std::string name_queues[] = { "1117", "1116" };
static std::string lifeSpan_queues[] = { "durable", "durable" };

/// <summary>
/// 回転式バッファー。
/// これはメイン・スレッドに置く。
/// デキューのスレッドでエンキューすることはできない。
/// デキュー処理は、回転式バッファーを仲介にしてエンキュー処理にメッセージを渡す。
/// </summary>
namespace rotationBuffer
{
    const int bufferSize = 100;
    static std::string buffer[bufferSize] = {};
    static int bufferCursors[2] = { 0, 0 };
    const int PUT_INDEX = 0;
    const int GET_INDEX = 1;

    std::mutex _mutex;
    static void putMessage(std::string message)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        buffer[bufferCursors[PUT_INDEX]] = message;
        bufferCursors[PUT_INDEX]++;
        if (!(bufferCursors[PUT_INDEX] < bufferSize))
        {
            bufferCursors[PUT_INDEX] = 0;
        }
    }
    static std::string getMessage()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        if ("" != buffer[bufferCursors[GET_INDEX]])
        {
            std::string message = buffer[bufferCursors[GET_INDEX]];
            buffer[bufferCursors[GET_INDEX]] = "";
            bufferCursors[GET_INDEX]++;
            if (!(bufferCursors[GET_INDEX] < bufferSize))
            {
                bufferCursors[GET_INDEX] = 0;
            }
            return message;
        }
        return "";
    }
}

// メッセージキューにエンキュー
static void enqueue(struct ev_loop* loop, AMQP::TcpConnection& connection, AMQP::TcpChannel& channel, std::string message)
{
    std::string exchange_name = "myexchange";
    std::string routing_key = "";

    int lifeSpan = 0;
    std::string a = lifeSpan_queues[ENQUEUE_INDEX];
    if      ("durable"    == a) { lifeSpan = AMQP::durable;    }
    else if ("autodelete" == a) { lifeSpan = AMQP::autodelete; }
    else if ("passive"    == a) { lifeSpan = AMQP::passive;    }
    else if ("exclusive"  == a) { lifeSpan = AMQP::exclusive;  }
    else
    {
        std::cerr << "未対応のキュー寿命が指定されました。["+ a +"]" << std::endl;
        exit(1);
    }

    channel.declareQueue(name_queues[ENQUEUE_INDEX], lifeSpan)
        .onError([&a](const char* errMsg) {
            std::cerr << "error declaring queue: " << errMsg << " lifeSpan=[" << a << "]" << "\n";
        });

    channel.bindQueue(exchange_name, name_queues[ENQUEUE_INDEX], routing_key)
        .onSuccess([&connection, &channel, &exchange_name, &routing_key, &message]() {

        if (!channel.publish(exchange_name, routing_key, message.c_str(), message.size())) {
            std::cerr << "failed to publish?\n";
        }

        // break in ev loop.
        connection.close();
    });

    // このループは、パブリッシュ後に抜ける
    ev_run(loop);
}

// 受信できたときに割り込んでくる処理
// startConsume() しておくこと。
static std::string dequeue() {
    std::string message;

    while ("" == message)
    {
        message = rotationBuffer::getMessage();
    }

    return message;
}

// メッセージ・キューの監視を開始
static void workConsume()
{
    // Connect to the AMQP service.
    auto *loop = EV_DEFAULT;
    AMQP::LibEvHandler handler(loop);
    AMQP::TcpConnection connection(&handler, ADDRESS);
    AMQP::TcpChannel channel(&connection);

    // I will go to the front of the box named "1111".
    int lifeSpan = 0;
    std::string a = lifeSpan_queues[DEQUEUE_INDEX];
    if      ("durable"    == a) { lifeSpan = AMQP::durable;    }
    else if ("autodelete" == a) { lifeSpan = AMQP::autodelete; }
    else if ("passive"    == a) { lifeSpan = AMQP::passive;    }
    else if ("exclusive"  == a) { lifeSpan = AMQP::exclusive;  }
    else {
        std::cerr << "未対応のキュー寿命が指定されました。[" + a + "]" << std::endl;
        exit(1);
    }
    channel.declareQueue(name_queues[DEQUEUE_INDEX], lifeSpan);

    // I look inside the box.
    auto errorCb = [&a](const char *errMsg) {
        std::cerr << "My ID watching failed [" << errMsg << "] lifeSpan=[" << a << "]" << std::endl;
    };
    auto messageCb = [&channel](const AMQP::Message &message, uint64_t deliveryTag, bool redelivered) {

        std::string myString(message.body(), message.bodySize());
        rotationBuffer::putMessage(myString);

        channel.ack(deliveryTag);
    };
    channel.consume(name_queues[DEQUEUE_INDEX])
        .onReceived(messageCb)
        .onError(errorCb);

    // I will keep on forever.
    ev_run(loop, 0);

    // I will not come here.
    return;
}

int main(int argc, char* argv[])
{
    std::string cmdArg;
    // プログラム名を省き、コマンドライン引数だけをつなげる。
    for (int i = 1; i < argc; ++i)
    {
        cmdArg += std::string(argv[i]) + " ";
    }
    std::istringstream ssCmd(cmdArg);

    // 半角スペース区切りの最初の1トークンを拾う
    std::string token;
    ssCmd >> token;

    if (token == "--msgqueue") {
        // ex.) --msgqueue 1115 durable 1114 durable
        ssCmd >> name_queues[ENQUEUE_INDEX];
        ssCmd >> lifeSpan_queues[ENQUEUE_INDEX];
        ssCmd >> name_queues[DEQUEUE_INDEX];
        ssCmd >> lifeSpan_queues[DEQUEUE_INDEX];
    }

    // デキューの常時監視をスタート
    auto th1 = std::thread([] { workConsume(); });

    // エンキュー用の接続
    //auto* loop = EV_DEFAULT;
    struct ev_loop* loop = EV_DEFAULT;
    AMQP::LibEvHandler handler{ loop };
    AMQP::TcpConnection connection{ &handler, ADDRESS };
    AMQP::TcpChannel channel{ &connection };

    // 無限ループ
    for (;;)
    {
        std::string message = rotationBuffer::getMessage();
        if ("" != message)
        {
            // 末尾に daze を付ける。
            message += "daze";

            // エンキューする
            enqueue(loop, connection, channel, message);
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(20));
    }

    // このプログラムは、自分では接続を切らない。

    // th1 スレッドの終了を待つ
    th1.join();
    return 0;
}

これを改造することにする。

現状

  • デキューを別スレッドにする
  • エンキューの無限ループを回す

にしているが、次のように変える。

  • エンキューを別スレッドにする
  • デキューの ev_loop を置いておくに任せる

何が違うのか、という気はする。

名前は tamesi35a2_cpp.cpp にする

ちょっと待て。

無限ループの外で接続して、無限ループの中で切断してないか?
これでは 接続と切断の1対1対応が 崩れている。

いったん、tamesi33a2_cpp.cpp のこの部分を直そう。

tamesi35a2_cpp.cpp

// OS      : Windows10 : // このプログラムは動かない
//
// OS      : Ubuntu 16.04
// Library : libev
//         : Install   : Command  : sudo apt-get update
//                                : sudo apt-get install libev-dev
// Service : RabbitMQ
//         : Reference : Web site : Top page http://www.rabbitmq.com/
//         : Install   : Web site : Installing on Debian / Ubuntu http://www.rabbitmq.com/install-debian.html
//         : Manual    : Command  : man rabbitmqctl
//         : Start     : Command  : rabbitmq-server
//         : Stop      : Command  : rabbitmqctl stop
//         : Check     : Command  : rabbitmqctl status
//         :           : Command  : rabbitmqctl list_queues
// Library : AMQP-CPP
//         : Reference : Web site : AMQP-CPP README.md https://github.com/CopernicaMarketingSoftware/AMQP-CPP
//         : Reference : Web site : QueueDeclare http://docs.spring.io/spring-amqp-net/docs/1.0.x/api/html/Spring.Messaging.Amqp.Rabbit~Spring.Messaging.Amqp.Rabbit.Connection.CachedModel~QueueDeclare(String,Boolean,Boolean,Boolean,Boolean,Boolean,IDictionary).html
//         : Reference : Web site : EventingBasicConsumer https://www.rabbitmq.com/releases/rabbitmq-dotnet-client/v1.4.0/rabbitmq-dotnet-client-1.4.0-net-2.0-htmldoc/type-RabbitMQ.Client.Events.EventingBasicConsumer.html
//         : Reference : Web site : BasicConsume https://www.rabbitmq.com/releases/rabbitmq-dotnet-client/v1.4.0/rabbitmq-dotnet-client-1.4.0-net-2.0-htmldoc/type-RabbitMQ.Client.IModel.html#method-M:RabbitMQ.Client.IModel.BasicConsume(System.UInt16,System.String,System.Boolean,System.Collections.IDictionary,RabbitMQ.Client.IBasicConsumer)
//         : Reference : Web site : C#でconstな配列を実現する (もっとクールにプログラミング) http://pgnote.net/?p=885
//         : Reference : Web site : AMQP-CPP (docsforge.com) http://docsforge.com/11/AMQP-CPP/docs/latest/namespace-AMQP/class-TcpConnection/
//
// Program : this
//         : Compile   : Command  : g++ -std=c++11 tamesi35a2_cpp.cpp -o tamesi35a2_cpp.exe -lev -lamqpcpp -pthread
//         : Execute   : Command  : // バックグラウンドで実行
//                                : ./tamesi35a2_cpp.exe --msgqueue 1117 durable 1116 durable 2> ./tamesi35a2_cpp.err.log &
//         : Stop      : Typing   : [Ctrl]+[C]
//
// メッセージの末尾に daze を付けます。
// デキューは「1112」、エンキューは「1113」キューに向けて行います。
// 標準入出力は、キーボード、画面には接続しないようにします。

#include <string> // std::string
#include <iostream> // std::cout
#include <sstream> // std::ostringstream
#include <chrono>
#include <thread> // sleep_for
#include <future> // thread

// プロセス間通信用
#include <ev.h>
#include <amqpcpp.h>
#include <amqpcpp/libev.h>

static AMQP::Address ADDRESS{ "amqp://localhost:5672" };
// 0 : enqueue用設定
// 1 : dequeue用設定
static const int ENQUEUE_INDEX = 0;
static const int DEQUEUE_INDEX = 1;
static const int NUM_INDEX = 2;
static std::string name_queues[] = { "1117", "1116" };
static std::string lifeSpan_queues[] = { "durable", "durable" };

/// <summary>
/// 回転式バッファー。
/// これはメイン・スレッドに置く。
/// デキューのスレッドでエンキューすることはできない。
/// デキュー処理は、回転式バッファーを仲介にしてエンキュー処理にメッセージを渡す。
/// </summary>
namespace rotationBuffer
{
    const int bufferSize = 100;
    static std::string buffer[bufferSize] = {};
    static int bufferCursors[2] = { 0, 0 };
    const int PUT_INDEX = 0;
    const int GET_INDEX = 1;

    std::mutex _mutex;
    static void putMessage(std::string message)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        buffer[bufferCursors[PUT_INDEX]] = message;
        bufferCursors[PUT_INDEX]++;
        if (!(bufferCursors[PUT_INDEX] < bufferSize))
        {
            bufferCursors[PUT_INDEX] = 0;
        }
    }
    static std::string getMessage()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        if ("" != buffer[bufferCursors[GET_INDEX]])
        {
            std::string message = buffer[bufferCursors[GET_INDEX]];
            buffer[bufferCursors[GET_INDEX]] = "";
            bufferCursors[GET_INDEX]++;
            if (!(bufferCursors[GET_INDEX] < bufferSize))
            {
                bufferCursors[GET_INDEX] = 0;
            }
            return message;
        }
        return "";
    }
}

// メッセージキューにエンキュー
static void enqueue(std::string message)
{
    // エンキュー用の接続
    //auto* loop = EV_DEFAULT;
    struct ev_loop* loop = EV_DEFAULT;
    AMQP::LibEvHandler handler{ loop };
    AMQP::TcpConnection connection{ &handler, ADDRESS };
    AMQP::TcpChannel channel{ &connection };

    std::string exchange_name = "myexchange";
    std::string routing_key = "";

    int lifeSpan = 0;
    std::string a = lifeSpan_queues[ENQUEUE_INDEX];
    if ("durable" == a) { lifeSpan = AMQP::durable; }
    else if ("autodelete" == a) { lifeSpan = AMQP::autodelete; }
    else if ("passive" == a) { lifeSpan = AMQP::passive; }
    else if ("exclusive" == a) { lifeSpan = AMQP::exclusive; }
    else
    {
        std::cerr << "未対応のキュー寿命が指定されました。[" + a + "]" << std::endl;
        exit(1);
    }

    channel.declareQueue(name_queues[ENQUEUE_INDEX], lifeSpan)
        .onError([&a](const char* errMsg) {
        std::cerr << "error declaring queue: " << errMsg << " lifeSpan=[" << a << "]" << "\n";
    });

    channel.bindQueue(exchange_name, name_queues[ENQUEUE_INDEX], routing_key)
        .onSuccess([&connection, &channel, &exchange_name, &routing_key, &message]() {

        if (!channel.publish(exchange_name, routing_key, message.c_str(), message.size())) {
            std::cerr << "failed to publish?\n";
        }

        // break in ev loop.
        connection.close();
    });

    // このループは、パブリッシュ後に抜ける
    ev_run(loop);
}

// 受信できたときに割り込んでくる処理
// startConsume() しておくこと。
static std::string dequeue() {
    std::string message;

    while ("" == message)
    {
        message = rotationBuffer::getMessage();
    }

    return message;
}

//// メッセージ・キューの送信を担当
//static void workProduce()
//{
//
//}

// メッセージ・キューの監視を開始
static void workConsume()
{
    // Connect to the AMQP service.
    auto *loop = EV_DEFAULT;
    AMQP::LibEvHandler handler(loop);
    AMQP::TcpConnection connection(&handler, ADDRESS);
    AMQP::TcpChannel channel(&connection);

    // I will go to the front of the box named "1111".
    int lifeSpan = 0;
    std::string a = lifeSpan_queues[DEQUEUE_INDEX];
    if ("durable" == a) { lifeSpan = AMQP::durable; }
    else if ("autodelete" == a) { lifeSpan = AMQP::autodelete; }
    else if ("passive" == a) { lifeSpan = AMQP::passive; }
    else if ("exclusive" == a) { lifeSpan = AMQP::exclusive; }
    else {
        std::cerr << "未対応のキュー寿命が指定されました。[" + a + "]" << std::endl;
        exit(1);
    }
    channel.declareQueue(name_queues[DEQUEUE_INDEX], lifeSpan);

    // I look inside the box.
    auto errorCb = [&a](const char *errMsg) {
        std::cerr << "My ID watching failed [" << errMsg << "] lifeSpan=[" << a << "]" << std::endl;
    };
    auto messageCb = [&channel](const AMQP::Message &message, uint64_t deliveryTag, bool redelivered) {

        std::string myString(message.body(), message.bodySize());
        rotationBuffer::putMessage(myString);

        channel.ack(deliveryTag);
    };
    channel.consume(name_queues[DEQUEUE_INDEX])
        .onReceived(messageCb)
        .onError(errorCb);

    // I will keep on forever.
    ev_run(loop, 0);

    // I will not come here.
    return;
}

int main(int argc, char* argv[])
{
    std::string cmdArg;
    // プログラム名を省き、コマンドライン引数だけをつなげる。
    for (int i = 1; i < argc; ++i)
    {
        cmdArg += std::string(argv[i]) + " ";
    }
    std::istringstream ssCmd(cmdArg);

    // 半角スペース区切りの最初の1トークンを拾う
    std::string token;
    ssCmd >> token;

    if (token == "--msgqueue") {
        // ex.) --msgqueue 1115 durable 1114 durable
        ssCmd >> name_queues[ENQUEUE_INDEX];
        ssCmd >> lifeSpan_queues[ENQUEUE_INDEX];
        ssCmd >> name_queues[DEQUEUE_INDEX];
        ssCmd >> lifeSpan_queues[DEQUEUE_INDEX];
    }

    // デキューの常時監視をスタート
    auto th1 = std::thread([] { workConsume(); });

    // 無限ループ
    for (;;)
    {
        std::string message = rotationBuffer::getMessage();
        if ("" != message)
        {
            // 末尾に daze を付ける。
            message += "daze";

            // エンキューする
            enqueue( message);
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(20));
    }

    // このプログラムは、自分では接続を切らない。

    // th1 スレッドの終了を待つ
    th1.join();
    return 0;
}
# g++ -std=c++11 tamesi35a2_cpp.cpp -o tamesi35a2_cpp.exe -lev -lamqpcpp -pthread
# ./tamesi35a2_cpp.exe --msgqueue 1117 durable 1116 durable 2> ./tamesi35a2_cpp.err.log &
[1] 20175
# ./tamesi33a2_cpp.exe --msgqueue 1117 durable 1116 durable &
[2] 20225
My ID watching failed [Channel is in error state] lifeSpan=[durable]

うーむ。

# ps aux | grep .exe
root      1743  3.5  0.0 126580    52 ?        Sl   Mar11 153:59 /usr/bin/cli ./loop28_cs.exe
root      1820  0.0  0.0   4216    12 ?        S    Mar11   0:24 ./loop28_cpp.exe
root      3554  0.2  0.4 179196  4916 ?        Sl   Mar13   2:11 ./tamesi33a2_cpp.exe
root      7457  0.2  0.2 244736  2904 ?        Sl   Mar13   1:49 ./tamesi33a2_cpp.exe --msgqueue 1117 durable 1116 durable
root     11288  0.2  0.4 341724  4428 ?        Sl   Mar11   9:53 /usr/bin/cli ./tamesi33a2_cs.exe
root     20175  0.2  0.3 170872  3156 pts/6    Sl   07:28   0:00 ./tamesi35a2_cpp.exe --msgqueue 1117 durable 1116 durable
root     20225  0.2  0.2 244736  2876 pts/6    Sl   07:31   0:00 ./tamesi33a2_cpp.exe --msgqueue 1117 durable 1116 durable
root     20240  0.2  0.2 179200  2984 pts/6    Sl   07:32   0:00 ./tamesi33a2_cpp.exe --msgqueue 1117 durable 1116 durable
root     20259  0.0  0.0  12936   988 pts/6    S+   07:33   0:00 grep --color=auto .exe

なんか いっぱい動いてるな。止めるか。

# kill 1743
# kill 1820
# kill 3554
# kill 7457
# kill 11288
# kill 20175
# kill 20225
[1]   Terminated              ./tamesi35a2_cpp.exe --msgqueue 1117 durable 1116 durable 2> ./tamesi35a2_cpp.err.log
# kill 20240
[2]-  Terminated              ./tamesi33a2_cpp.exe --msgqueue 1117 durable 1116 durable
# ps aux | grep .exe
root     20296  0.0  0.0  12936   984 pts/6    S+   07:35   0:00 grep --color=auto .exe
[3]+  Terminated              ./tamesi33a2_cpp.exe --msgqueue 1117 durable 1116 durable

起動するプログラム番号も間違えてるし。

# ./tamesi35a2_cpp.exe --msgqueue 1117 durable 1116 durable &
[1] 20308
My ID watching failed [Channel is in error state] lifeSpan=[durable]
# ./tamesi35a2_cpp.exe --msgqueue 1117 durable 1116 durable &
[2] 20324
My ID watching failed [Channel is in error state] lifeSpan=[durable]

うーむ。

# ./tamesi35a2_cpp.exe --msgqueue 1117 durable 1116 durable 2> ./tamesi35a2_cpp.err.log &
[3] 20489
Welcome! AMQP::durable=[1] AMQP::autodelete=[2] AMQP::passive=[8] AMQP::exclusive=[512]

ためしに値を出力したら うまくいったぞ?

# ./tamesi34_cs.exe
削除したいキューがあれば名前を、無ければ空文字列を入れろだぜ☆(^~^)
キュー名を入力    : キューを削除します
空文字列で[Enter] : 次のステップへ進む
Name or empty ? >
エンキュー先のメッセージ・キューの名前を入れろだぜ☆(^~^)
Queue name? > 1116
エンキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆(^~^)
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが1人も接続していなかったら消す
(4) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(8) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > 1
デキュー先のメッセージ・キューの名前を入れろだぜ☆(^~^)
Queue name? > 1117
デキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆(^~^)
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが1人も接続していなかったら消す
(4) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(8) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > 1
GetChannel index = [1]
    QUEUE_NAMES[index] = [1117]
    passive_lifeSpans[index] = [False]
    durable_lifeSpans[index] = [True]
    exclusive_lifeSpans[index] = [False]
    autodelete_lifeSpans[index] = [False]
    nowait は仮に false 固定
    arguments は仮に null 固定
終了するときは[Ctrl]+[C]キーを押せだぜ☆(^~^)
エンキューするときはメッセージを打ち込んで[Enter]キーを押せだぜ☆(^◇^)
Enqueue? > usi
GetChannel index = [0]
    QUEUE_NAMES[index] = [1116]
    passive_lifeSpans[index] = [False]
    durable_lifeSpans[index] = [True]
    exclusive_lifeSpans[index] = [False]
    autodelete_lifeSpans[index] = [False]
    nowait は仮に false 固定
    arguments は仮に null 固定

Unhandled Exception:
RabbitMQ.Client.Exceptions.OperationInterruptedException: The AMQP operation was interrupted: AMQP close-reason, initiated by Peer, code=406, text="PRECONDITION_FAILED - inequivalent arg 'durable' for queue '1116' in vhost '/': received 'true' but current is 'false'", classId=50, methodId=10, cause=
  at RabbitMQ.Client.Impl.SimpleBlockingRpcContinuation.GetReply () <0x41a50410 + 0x00107> in <filename unknown>:0
  at RabbitMQ.Client.Impl.ModelBase.ModelRpc (RabbitMQ.Client.Impl.MethodBase method, RabbitMQ.Client.Impl.ContentHeaderBase header, System.Byte[] body) <0x41a4e290 + 0x000fd> in <filename unknown>:0
  at RabbitMQ.Client.Framing.Impl.v0_8.Model.QueueDeclare (System.String queue, Boolean passive, Boolean durable, Boolean exclusive, Boolean autoDelete, Boolean nowait, IDictionary arguments) <0x41a526b0 + 0x00113> in <filename unknown>:0
  at UsagiMQ.Program.GetChannel (Int32 index) <0x41a13d20 + 0x00436> in <filename unknown>:0
  at UsagiMQ.Program.Enqueue (System.String message) <0x41a537c0 + 0x00013> in <filename unknown>:0
  at UsagiMQ.Program.Main (System.String[] args) <0x41a0ed50 + 0x0099f> in <filename unknown>:0
[ERROR] FATAL UNHANDLED EXCEPTION: RabbitMQ.Client.Exceptions.OperationInterruptedException: The AMQP operation was interrupted: AMQP close-reason, initiated by Peer, code=406, text="PRECONDITION_FAILED - inequivalent arg 'durable' for queue '1116' in vhost '/': received 'true' but current is 'false'", classId=50, methodId=10, cause=
  at RabbitMQ.Client.Impl.SimpleBlockingRpcContinuation.GetReply () <0x41a50410 + 0x00107> in <filename unknown>:0
  at RabbitMQ.Client.Impl.ModelBase.ModelRpc (RabbitMQ.Client.Impl.MethodBase method, RabbitMQ.Client.Impl.ContentHeaderBase header, System.Byte[] body) <0x41a4e290 + 0x000fd> in <filename unknown>:0
  at RabbitMQ.Client.Framing.Impl.v0_8.Model.QueueDeclare (System.String queue, Boolean passive, Boolean durable, Boolean exclusive, Boolean autoDelete, Boolean nowait, IDictionary arguments) <0x41a526b0 + 0x00113> in <filename unknown>:0
  at UsagiMQ.Program.GetChannel (Int32 index) <0x41a13d20 + 0x00436> in <filename unknown>:0
  at UsagiMQ.Program.Enqueue (System.String message) <0x41a537c0 + 0x00013> in <filename unknown>:0
  at UsagiMQ.Program.Main (System.String[] args) <0x41a0ed50 + 0x0099f> in <filename unknown>:0

このエラーも分からずじまい。

接続部分を直そう。

あれっ、直してこれか。

じゃあ、C++ と C# で送受信テストしてみよう。

# ps aux | grep exe
root     20308  0.2  0.3 170872  3156 pts/6    Sl   07:35   0:02 ./tamesi35a2_cpp.exe --msgqueue 1117 durable 1116 durable
root     20324  0.2  0.5 170872  5188 pts/6    Sl   07:36   0:01 ./tamesi35a2_cpp.exe --msgqueue 1117 durable 1116 durable
root     20489  0.2  0.3 170872  3136 pts/6    Sl   07:42   0:00 ./tamesi35a2_cpp.exe --msgqueue 1117 durable 1116 durable
root     20587  0.0  0.0  12936   988 pts/6    S+   07:48   0:00 grep --color=auto exe

なんで こんなにプロセス立ってるんだろな。

C# ではシングルトンでやってた

tamesi33a2_cs.cs

// OS  : Windows 10
// IDE : Visual Studio 2015
//       Install : NuGet   : Install-Package RabbitMQ.Client -Version 4.1.1
//
// OS  : Ubuntu 16.04
//       Compile : Command : mcs /r:RabbitMQ.Client.dll -define:UBUNTU tamesi33a2_cs.cs
//               : Command : chmod 755 tamesi33a2_cs.cs
//       Execute : Command : // バックグラウンドで実行する
//                         : nohup ./tamesi33a2_cs.exe > ./tamesi33a2_cs.out.log 2> ./tamesi33a2_cs.err.log < /dev/null &
//       Check   :         : // tamesi33a1_cs.exe を使う
//--------------------------------------------------------------------------------
// tamesi33a2_cs.cs

// Ubuntu の RabbitMQ はソースのバージョンが古いのか、API が異なった。
// #define UBUNTU

using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Collections.ObjectModel;
using System.Text;
using System.Threading;

namespace UsagiMQ
{
    /// <summary>
    /// メッセージの末尾に daze を付けます。
    /// デキューは「1112」、エンキューは「1113」キューに向けて行います。
    /// 標準入出力は、キーボード、画面には接続しないようにします。
    /// 
    /// 参照 : QueueDeclare http://docs.spring.io/spring-amqp-net/docs/1.0.x/api/html/Spring.Messaging.Amqp.Rabbit~Spring.Messaging.Amqp.Rabbit.Connection.CachedModel~QueueDeclare(String,Boolean,Boolean,Boolean,Boolean,Boolean,IDictionary).html
    /// 参照 : EventingBasicConsumer https://www.rabbitmq.com/releases/rabbitmq-dotnet-client/v1.4.0/rabbitmq-dotnet-client-1.4.0-net-2.0-htmldoc/type-RabbitMQ.Client.Events.EventingBasicConsumer.html
    /// 参照 : BasicConsume https://www.rabbitmq.com/releases/rabbitmq-dotnet-client/v1.4.0/rabbitmq-dotnet-client-1.4.0-net-2.0-htmldoc/type-RabbitMQ.Client.IModel.html#method-M:RabbitMQ.Client.IModel.BasicConsume(System.UInt16,System.String,System.Boolean,System.Collections.IDictionary,RabbitMQ.Client.IBasicConsumer)
    /// 参照 : C#でconstな配列を実現する (もっとクールにプログラミング) http://pgnote.net/?p=885
    /// </summary>
    class Program
    {
        public const string HOST_NAME = "localhost";
        public static readonly ReadOnlyCollection<string> QUEUE_NAMES =
            Array.AsReadOnly(new string[] { "1113", "1112" });
        public const int ENQUEUE_INDEX = 0;
        public const int DEQUEUE_INDEX = 1;

        // 回転式バッファー
        public static RotationBuffer rollingBuffer = new RotationBuffer();

        public static ConnectionFactory GetFactory()
        {
            if (null == m_factory_)
            {
                m_factory_ = new ConnectionFactory() { HostName = HOST_NAME };
            }
            return m_factory_;
        }
        static ConnectionFactory m_factory_;

        public static IConnection GetConnection()
        {
            if (null == m_connection_)
            {
                m_connection_ = GetFactory().CreateConnection();
            }
            return m_connection_;
        }
        static IConnection m_connection_;

        public static bool IsConnected()
        {
            if (null != m_connection_)
            {
                return m_connection_.IsOpen;
            }
            return false;
        }

        public static IModel GetChannel(int index)
        {
            if (null == m_channels_[index])
            {
                m_channels_[index] = GetConnection().CreateModel();
#if UBUNTU
                // Ubuntuでは何故か Spring.Messaging.Amqp.Rabbit の引数 7 つのやつになっている。
                m_channels_[index].QueueDeclare(QUEUE_NAMES[index], false, false, false, false, false, null);
#else
                m_channels_[index].QueueDeclare(QUEUE_NAMES[index], false, false, false, null);
#endif
            }
            return m_channels_[index];
        }
        static IModel[] m_channels_ = new IModel[2];

        public static EventingBasicConsumer GetConsumer(int index)
        {
            if (null == m_consumers_[index])
            {
#if UBUNTU
                // Ubuntuでは何故か v1.4.0 の引数が 0 個のやつになっている。調べたが引数が1個~6個のものは無かった。
                m_consumers_[index] = new EventingBasicConsumer();
#else
                m_consumers_[index] = new EventingBasicConsumer(GetChannel(index));
#endif

            }
            return m_consumers_[index];
        }
        static EventingBasicConsumer[] m_consumers_ = new EventingBasicConsumer[2];

        /// <summary>
        /// 受信できたときに割り込んでくる処理
        /// </summary>
#if UBUNTU
        public static BasicDeliverEventHandler GetReceiveHandler()
#else
        public static EventHandler<BasicDeliverEventArgs> GetReceiveHandler()
#endif
        {
            if (null == m_eventHandler_)
            {
#if UBUNTU
                m_eventHandler_ = new BasicDeliverEventHandler((model, ea) =>
#else
                m_eventHandler_ = new EventHandler<BasicDeliverEventArgs>((model, ea) =>
#endif
                {
                    byte[] body = ea.Body;
                    string message = Encoding.UTF8.GetString(body);

                    // 末尾に daze を付ける。
                    message += "daze";

                    // 回転式バッファーに入れる
                    rollingBuffer.PutMessage(message);
                });
            }

            return m_eventHandler_;
        }

#if UBUNTU
        static BasicDeliverEventHandler m_eventHandler_;
#else
        static EventHandler<BasicDeliverEventArgs> m_eventHandler_;
#endif

        /// <summary>
        /// 対応するオープンは無いけれど、開けたら閉める、を完璧に対応する必要がある。
        /// </summary>
        static void CloseChannel(int index)
        {
            if (null != m_channels_[index])
            {
                m_channels_[index].Close();
                m_channels_[index] = null;
            }
        }

        static void Main(string[] args)
        {
            // 常時監視をスタート
            StartDequeue();

            // 無限ループ
            while (IsConnected())
            {
                string message = rollingBuffer.GetMessage();
                if (null != message)
                {
                    // エンキューする
                    Enqueue(message);
                }
                Thread.Sleep(20);
            }

            // このプログラムは、自分では接続を切らない。
        }

        static void Enqueue(string message)
        {
            IModel channel = GetChannel(ENQUEUE_INDEX);

            byte[] body = Encoding.UTF8.GetBytes(message);
            channel.BasicPublish("", QUEUE_NAMES[ENQUEUE_INDEX], null, body);

            // 対応するオープンは無いが、ちゃんと閉じないと、レシーブしてくれない。
            CloseChannel(ENQUEUE_INDEX);
        }

        static void StartDequeue()
        {
            IModel channel = GetChannel(DEQUEUE_INDEX);
            EventingBasicConsumer consumer = GetConsumer(DEQUEUE_INDEX);
            // 受信できたときに割り込んでくる処理
            consumer.Received += GetReceiveHandler();

#if UBUNTU
            // Ubuntuでは何故か引数が 5 個のやつになっている。
            channel.BasicConsume( QUEUE_NAMES[DEQUEUE_INDEX], true, "", null, consumer);
#else
            channel.BasicConsume( QUEUE_NAMES[DEQUEUE_INDEX], true, consumer);
#endif

            // 割込み処理の削除
            // consumer.Received -= GetReceiveHandler();

            // 対応するオープンは無いが、ちゃんと閉じないと、レシーブしてくれない。
            // CloseChannel(DEQUEUE_INDEX);
        }
    }

    /// <summary>
    /// 回転式バッファー。
    /// これはメイン・スレッドに置く。
    /// デキューのスレッドでエンキューすることはできない。
    /// デキュー処理は、回転式バッファーを仲介にしてエンキュー処理にメッセージを渡す。
    /// </summary>
    public class RotationBuffer
    {
        public const int bufferSize = 100;
        public string[] buffer = new string[bufferSize];
        public int[] bufferCursors = new int[] { 0, 0 };
        public const int PUT_INDEX = 0;
        public const int GET_INDEX = 1;
        public void PutMessage(string message)
        {
            buffer[bufferCursors[PUT_INDEX]] = message;
            bufferCursors[PUT_INDEX]++;
            if (!(bufferCursors[PUT_INDEX] < bufferSize))
            {
                bufferCursors[PUT_INDEX] = 0;
            }
        }
        public string GetMessage()
        {
            if (null != buffer[bufferCursors[GET_INDEX]])
            {
                string message = buffer[bufferCursors[GET_INDEX]];
                buffer[bufferCursors[GET_INDEX]] = null;
                bufferCursors[GET_INDEX]++;
                if (!(bufferCursors[GET_INDEX] < bufferSize))
                {
                    bufferCursors[GET_INDEX] = 0;
                }
                return message;
            }
            return null;
        }
    }
}

番号を合わせて tamesi35a2_cs.cs に変えてみる

キュー名も選べるように改造しよう。 tamesi34_cs.cs ではそういう機能を入れていた。

tamesi34_cs.cs

// OS  : Windows 10
// IDE : Visual Studio 2015
//       Install : NuGet   : Install-Package RabbitMQ.Client -Version 4.1.1
//
// OS  : Ubuntu 16.04
//       Compile : Command : mcs /r:RabbitMQ.Client.dll -define:UBUNTU tamesi34_cs.cs
//               : Command : // chmod 755 tamesi34_cs.cs
//       Execute : Command : // フォアグラウンドで実行する
//                         : ./tamesi34_cs.exe 2> ./tamesi34_cs.err.log
//       Check   : Command : // キューの中身の数を調べる
//                         : rabbitmqctl list_queues
//
// Library : RabbitMQ
//           Refference : Website : RabbitMQ http://www.rabbitmq.com/
//                      : Website : RabbitMQ管理コマンド(rabbitmqctl)使い方 (Qiita) http://qiita.com/tamikura@github/items/5293cda4c0026b2d7022
//                      : Website : amqpを使ってRabbitMQのキューを操作する (Qiita) http://qiita.com/tamikura@github/items/a268afa51c5537ca4fe6
//--------------------------------------------------------------------------------
// tamesi34_cs.cs

// Ubuntu の RabbitMQ はソースのバージョンが古いのか、API が異なった。
// #define UBUNTU

using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Text;

namespace UsagiMQ
{
    [Flags]
    enum LifeSpanType
    {
        /// <summary>
        /// RabbitMQが止まってもキューを残す
        /// </summary>
        Durable = 1,
        /// <summary>
        /// コンシューマーが1人も接続していなかったら消す
        /// </summary>
        Autodelete = 0x1<<1,
        /// <summary>
        /// キューが存在するかどうかチェックするだけ。中身見ない時これ
        /// </summary>
        Passive = 0x1 << 2,
        /// <summary>
        /// この接続でだけ使える。この接続が切れたら消す
        /// </summary>
        Exclusive = 0x1 << 3
    }

    /// <summary>
    /// メッセージを エンキューします。
    /// キューの名前は指定してください。
    /// デキューは割込みを受け付けます。
    /// 
    /// 参照 : QueueDeclare (v1.0) http://docs.spring.io/spring-amqp-net/docs/1.0.x/api/html/Spring.Messaging.Amqp.Rabbit~Spring.Messaging.Amqp.Rabbit.Connection.CachedModel~QueueDeclare(String,Boolean,Boolean,Boolean,Boolean,Boolean,IDictionary).html
    /// 参照 : EventingBasicConsumer https://www.rabbitmq.com/releases/rabbitmq-dotnet-client/v1.4.0/rabbitmq-dotnet-client-1.4.0-net-2.0-htmldoc/type-RabbitMQ.Client.Events.EventingBasicConsumer.html
    /// 参照 : QueueDelete (v1.4) https://www.rabbitmq.com/releases/rabbitmq-dotnet-client/v1.4.0/rabbitmq-dotnet-client-1.4.0-net-2.0-htmldoc/type-RabbitMQ.Client.IModel.html#method-M:RabbitMQ.Client.IModel.QueueDelete(System.UInt16,System.String,System.Boolean,System.Boolean,System.Boolean)
    /// 参照 : BasicConsume https://www.rabbitmq.com/releases/rabbitmq-dotnet-client/v1.4.0/rabbitmq-dotnet-client-1.4.0-net-2.0-htmldoc/type-RabbitMQ.Client.IModel.html#method-M:RabbitMQ.Client.IModel.BasicConsume(System.UInt16,System.String,System.Boolean,System.Collections.IDictionary,RabbitMQ.Client.IBasicConsumer)
    /// 参照 : C#でconstな配列を実現する (もっとクールにプログラミング) http://pgnote.net/?p=885
    /// </summary>
    class Program
    {
        const int ENQUEUE_INDEX = 0;
        const int DEQUEUE_INDEX = 1;
        const int DELETEQUEUE_INDEX = 2;
        const int NUM_INDEX = 3;
        const string HOST_NAME = "localhost";
        static string[] QUEUE_NAMES = new string[NUM_INDEX];
        /// <summary>
        /// キューの寿命(複合可能)
        /// durable    : RabbitMQが止まってもキューを残す
        /// autodelete : コンシューマーが1人も接続していなかったら消す
        /// passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
        /// exclusive  : この接続でだけ使える。この接続が切れたら消す
        /// </summary>
        static LifeSpanType[] lifeSpans_queue = new LifeSpanType[NUM_INDEX];
        static bool[]
            durable_lifeSpans = new bool[NUM_INDEX],
            autodelete_lifeSpans = new bool[NUM_INDEX],
            passive_lifeSpans = new bool[NUM_INDEX],
            exclusive_lifeSpans = new bool[NUM_INDEX];
        /// <summary>
        /// 
        /// </summary>
        /// <param name="index_queue"></param>
        /// <param name="name_queue"></param>
        /// <param name="lifeSpan">
        /// (0) durable
        /// (1) autodelete
        /// (2) passive
        /// (3) exclusive
        /// </param>
        static void SetLifeSpan(int index_queue, string name_queue, LifeSpanType lifeSpan)
        {
            QUEUE_NAMES[index_queue] = name_queue;
            lifeSpans_queue[index_queue] = lifeSpan;

            // 一旦クリアー
            durable_lifeSpans[index_queue] = false;
            autodelete_lifeSpans[index_queue] = false;
            passive_lifeSpans[index_queue] = false;
            exclusive_lifeSpans[index_queue] = false;

            // durable
            if (((int)lifeSpan & (int)LifeSpanType.Durable) == (int)LifeSpanType.Durable) { durable_lifeSpans[index_queue] = true; }
            // autodelete
            if (((int)lifeSpan & (int)LifeSpanType.Autodelete) == (int)LifeSpanType.Autodelete) { autodelete_lifeSpans[index_queue] = true; }
            // passive
            if (((int)lifeSpan & (int)LifeSpanType.Passive) == (int)LifeSpanType.Passive) { passive_lifeSpans[index_queue] = true; }
            // exclusive
            if (((int)lifeSpan & (int)LifeSpanType.Exclusive) == (int)LifeSpanType.Exclusive) { exclusive_lifeSpans[index_queue] = true; }
        }


        public static ConnectionFactory GetFactory()
        {
            if (null == m_factory_)
            {
                m_factory_ = new ConnectionFactory() { HostName = HOST_NAME };
            }
            return m_factory_;
        }
        static ConnectionFactory m_factory_;

        public static IConnection GetConnection()
        {
            if (null == m_connection_)
            {
                m_connection_ = GetFactory().CreateConnection();
            }
            return m_connection_;
        }
        static IConnection m_connection_;

        public static IModel GetChannel(int index)
        {
            if (null == m_channels_[index])
            {
                m_channels_[index] = GetConnection().CreateModel();

                // 引数が7つの QueueDeclare でエラーを吐くことがあるので、情報を出力しておく。
                Console.Error.WriteLine("GetChannel index = [" + index + "]");
                Console.Error.WriteLine("    QUEUE_NAMES[index] = [" + QUEUE_NAMES[index] + "]");
                Console.Error.WriteLine("    passive_lifeSpans[index] = [" + passive_lifeSpans[index] + "]");
                Console.Error.WriteLine("    durable_lifeSpans[index] = [" + durable_lifeSpans[index] + "]");
                Console.Error.WriteLine("    exclusive_lifeSpans[index] = [" + exclusive_lifeSpans[index] + "]");
                Console.Error.WriteLine("    autodelete_lifeSpans[index] = [" + autodelete_lifeSpans[index] + "]");
                Console.Error.WriteLine("    nowait は仮に false 固定");
                Console.Error.WriteLine("    arguments は仮に null 固定");

#if UBUNTU
                // Ubuntuでは何故か Spring.Messaging.Amqp.Rabbit の引数 7 つのやつになっている。
                m_channels_[index].QueueDeclare(QUEUE_NAMES[index], passive_lifeSpans[index], durable_lifeSpans[index], exclusive_lifeSpans[index], autodelete_lifeSpans[index], false, null);
#else
                m_channels_[index].QueueDeclare(QUEUE_NAMES[index], durable_lifeSpans[index], exclusive_lifeSpans[index], autodelete_lifeSpans[index], null);
#endif
            }
            return m_channels_[index];
        }
        static IModel[] m_channels_ = new IModel[NUM_INDEX];

        public static EventingBasicConsumer GetConsumer(int index)
        {
            if (null == m_consumers_[index])
            {
#if UBUNTU
                // Ubuntuでは何故か v1.4.0 の引数が 0 個のやつになっている。調べたが引数が1個~6個のものは無かった。
                m_consumers_[index] = new EventingBasicConsumer();
#else
                m_consumers_[index] = new EventingBasicConsumer(GetChannel(index));
#endif

            }
            return m_consumers_[index];
        }
        static EventingBasicConsumer[] m_consumers_ = new EventingBasicConsumer[NUM_INDEX];

        /// <summary>
        /// 受信できたときに割り込んでくる処理
        /// </summary>
#if UBUNTU
        public static BasicDeliverEventHandler GetDequeueHandler()
#else
        public static EventHandler<BasicDeliverEventArgs> GetDequeueHandler()
#endif
        {
            if (null == m_dequeueHandler_)
            {
#if UBUNTU
                m_dequeueHandler_ = new BasicDeliverEventHandler((model, ea) =>
#else
                m_dequeueHandler_ = new EventHandler<BasicDeliverEventArgs>((model, ea) =>
#endif
                {
                    byte[] body = ea.Body;
                    string message = Encoding.UTF8.GetString(body);
                    Console.WriteLine("<---- [interrupt!] Dequeue(^q^) {0}", message);
                });
            }

            return m_dequeueHandler_;
        }

#if UBUNTU
        static BasicDeliverEventHandler m_dequeueHandler_;
#else
        static EventHandler<BasicDeliverEventArgs> m_dequeueHandler_;
#endif

        /// <summary>
        /// 対応するオープンは無いけれど、開けたら閉める、を完璧に対応する必要がある。
        /// </summary>
        static void CloseConnection()
        {
            if (null != m_connection_)
            {
                m_connection_.Close();
                m_connection_ = null;
            }
        }
        /// <summary>
        /// 対応するオープンは無いけれど、開けたら閉める、を完璧に対応する必要がある。
        /// </summary>
        static void CloseChannel(int index)
        {
            if (null != m_channels_[index])
            {
                m_channels_[index].Close();
                m_channels_[index] = null;
            }
        }

        static void Main(string[] args)
        {
            //----------------------------------------
            // Delete
            //----------------------------------------
            for (;;)
            {
                Console.Write(@"削除したいキューがあれば名前を、無ければ空文字列を入れろだぜ☆(^~^)
キュー名を入力    : キューを削除します
空文字列で[Enter] : 次のステップへ進む
Name or empty ? > ");
                string queueName_delete = Console.ReadLine();
                if (""== queueName_delete.Trim())
                {
                    break;
                }


                Console.Write(@"削除するメッセージ・キューの寿命を、足し算して答えろだぜ☆(^~^)
("+ ((int)LifeSpanType.Durable) + @") durable    : RabbitMQが止まってもキューを残す
(" + ((int)LifeSpanType.Autodelete) + @") autodelete : コンシューマーが1人も接続していなかったら消す
(" + ((int)LifeSpanType.Passive) + @") passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(" + ((int)LifeSpanType.Exclusive) + @") exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > ");
                LifeSpanType lifeSpan_delete = (LifeSpanType)int.Parse(Console.ReadLine());
                SetLifeSpan(DELETEQUEUE_INDEX, queueName_delete, lifeSpan_delete);
                uint result = DeleteQueue();
                Console.WriteLine(@"["+ queueName_delete + "]キューを削除したはずだぜ☆(^~^) result=["+ result + "]");
            }

            //----------------------------------------
            // Enqueue settings
            //----------------------------------------
            for (;;)
            {
                Console.Write(@"エンキュー先のメッセージ・キューの名前を入れろだぜ☆(^~^)
Queue name? > ");
                string queueName_enqueue = Console.ReadLine();

                Console.Write(@"エンキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆(^~^)
(" + ((int)LifeSpanType.Durable) + @") durable    : RabbitMQが止まってもキューを残す
(" + ((int)LifeSpanType.Autodelete) + @") autodelete : コンシューマーが1人も接続していなかったら消す
(" + ((int)LifeSpanType.Passive) + @") passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(" + ((int)LifeSpanType.Exclusive) + @") exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > ");
                int lifeSpan_enqueue;
                if(int.TryParse(Console.ReadLine(),out lifeSpan_enqueue))
                {
                    SetLifeSpan(ENQUEUE_INDEX, queueName_enqueue, (LifeSpanType)lifeSpan_enqueue);
                    break;
                }
            }

            //----------------------------------------
            // Enqueue settings
            //----------------------------------------
            for (;;)
            {
                Console.Write(@"デキュー先のメッセージ・キューの名前を入れろだぜ☆(^~^)
Queue name? > ");
                string queueName_dequeue = Console.ReadLine();

                Console.Write(@"デキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆(^~^)
(" + ((int)LifeSpanType.Durable) + @") durable    : RabbitMQが止まってもキューを残す
(" + ((int)LifeSpanType.Autodelete) + @") autodelete : コンシューマーが1人も接続していなかったら消す
(" + ((int)LifeSpanType.Passive) + @") passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(" + ((int)LifeSpanType.Exclusive) + @") exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > ");
                int lifeSpan_dequeue;
                if(int.TryParse(Console.ReadLine(),out lifeSpan_dequeue))
                {
                    StartConsume(queueName_dequeue, (LifeSpanType)lifeSpan_dequeue);
                    break;
                }
            }

            Console.Write(@"終了するときは[Ctrl]+[C]キーを押せだぜ☆(^~^)
エンキューするときはメッセージを打ち込んで[Enter]キーを押せだぜ☆(^◇^)
Enqueue? > ");
            for (;;)
            {
                // "Hello World!" などを入力
                string line = Console.ReadLine();
                Enqueue(line);
                Console.Write(@"Enqueue? > ");
            }

            // ここには来ない
            // CloseConnection();
        }

        static uint DeleteQueue()
        {
            IModel channel = GetChannel(DELETEQUEUE_INDEX);

#if UBUNTU
            // Ubuntuでは何故か 昔の .Net 用の引数 5 つのやつの変則版になっている。
            // uint QueueDelete(ushort ticket, string queue, bool ifUnused, bool ifEmpty, bool nowait)
            uint result = channel.QueueDelete( QUEUE_NAMES[DELETEQUEUE_INDEX],true,true,true);
#else
            uint result = channel.QueueDelete(QUEUE_NAMES[DELETEQUEUE_INDEX],true,true);
#endif

            // 対応するオープンは無いが、ちゃんと閉じないと、レシーブしてくれない。
            CloseChannel(DELETEQUEUE_INDEX);
            return result;
        }

        static void Enqueue(string message)
        {
            IModel channel = GetChannel(ENQUEUE_INDEX);

            byte[] body = Encoding.UTF8.GetBytes(message);
            channel.BasicPublish("", QUEUE_NAMES[ENQUEUE_INDEX], null, body);

            Console.WriteLine(" Enqueue(^q^) {0}", message);

            // 対応するオープンは無いが、ちゃんと閉じないと、レシーブしてくれない。
            CloseChannel(ENQUEUE_INDEX);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="name_queue">メッセージ・キューの名前</param>
        /// <param name="lifeSpan_queue">既存のメッセージ・キューの場合、メッセージ・キューの設定は合わせる必要がある。
        /// durable    : RabbitMQが止まってもキューを残す
        /// autodelete : コンシューマーが1人も接続していなかったら消す
        /// passive    : キューが存在するかどうかチェックするだけで、中身を見ない時はこれ
        /// exclusive  : この接続でだけ使える。この接続が切れたら消す
        /// </param>
        static void StartConsume(string name_queue, LifeSpanType lifeSpan_queue)
        {
            SetLifeSpan(DEQUEUE_INDEX, name_queue, lifeSpan_queue);

            IModel channel = GetChannel(DEQUEUE_INDEX);
            EventingBasicConsumer consumer = GetConsumer(DEQUEUE_INDEX);

            // 受信できたときに割り込んでくる処理
            consumer.Received += GetDequeueHandler();

#if UBUNTU
            // Ubuntuでは何故か引数が 5 個のやつになっている。
            channel.BasicConsume( QUEUE_NAMES[DEQUEUE_INDEX], true, "", null, consumer);
#else
            channel.BasicConsume( QUEUE_NAMES[DEQUEUE_INDEX], true, consumer);
#endif
            // 終了はさせない
            // consumer.Received -= GetReceiveHandler();
            // CloseChannel(DEQUEUE_INDEX);
        }
    }
}

QueryDeclare の引数は false の方が良かったんだろうか?

#if UBUNTU
            // Ubuntuでは何故か Spring.Messaging.Amqp.Rabbit の引数 7 つのやつになっている。
            // m_channels_[index].QueueDeclare(QUEUE_NAMES[index], false, false, false, false, false, null);
            m_channels_[index].QueueDeclare(QUEUE_NAMES[index], passive_lifeSpans[index], durable_lifeSpans[index], exclusive_lifeSpans[index], autodelete_lifeSpans[index], false, null);
#else
            //m_channels_[index].QueueDeclare(QUEUE_NAMES[index], false, false, false, null);
            m_channels_[index].QueueDeclare(QUEUE_NAMES[index], durable_lifeSpans[index], exclusive_lifeSpans[index], autodelete_lifeSpans[index], null);
#endif

こんなコードでいいんだろうか?

//--------------------------------------------------------------------------------
// tamesi35a2_cs.cs
//
// メッセージの末尾に daze を付けます。
// 標準入出力は、キーボード、画面には接続しないようにします。
// キューの名前は指定してください。
// デキューは割込みを受け付けます。
//--------------------------------------------------------------------------------
// OS  : Windows 10
// IDE : Visual Studio 2015
//       Install : NuGet   : Install-Package RabbitMQ.Client -Version 4.1.1
//
// OS  : Ubuntu 16.04
//       Compile : Command : mcs /r:RabbitMQ.Client.dll -define:UBUNTU tamesi35a2_cs.cs
//               : Command : // chmod 755 tamesi35a2_cs.cs
//       Execute : Command : // バックグラウンドで実行する
//                         : nohup ./tamesi35a2_cs.exe > ./tamesi35a2_cs.out.log 2> ./tamesi35a2_cs.err.log < /dev/null &
//       Check   :         : // tamesi33a1_cs.exe を使う
//--------------------------------------------------------------------------------

// Ubuntu の RabbitMQ はソースのバージョンが古いのか、API が異なった。
// #define UBUNTU

using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Text;
using System.Threading;

/// <summary>
/// このプログラム内だけで使われる数字。AMQP-CPPでの実装とは異なる。
/// AMQP-CPPでの実装 : AMQP::durable=[1] AMQP::autodelete=[2] AMQP::passive=[8] AMQP::exclusive=[512]
/// </summary>
[Flags]
enum LifeSpanType
{
    /// <summary>
    /// RabbitMQが止まってもキューを残す
    /// </summary>
    Durable = 1,
    /// <summary>
    /// コンシューマーが1人も接続していなかったら消す
    /// </summary>
    Autodelete = 0x1 << 1,
    /// <summary>
    /// キューが存在するかどうかチェックするだけ。中身見ない時これ
    /// </summary>
    Passive = 0x1 << 2,
    /// <summary>
    /// この接続でだけ使える。この接続が切れたら消す
    /// </summary>
    Exclusive = 0x1 << 3
}

/// <summary>
/// 参照 : QueueDeclare http://docs.spring.io/spring-amqp-net/docs/1.0.x/api/html/Spring.Messaging.Amqp.Rabbit~Spring.Messaging.Amqp.Rabbit.Connection.CachedModel~QueueDeclare(String,Boolean,Boolean,Boolean,Boolean,Boolean,IDictionary).html
/// 参照 : EventingBasicConsumer https://www.rabbitmq.com/releases/rabbitmq-dotnet-client/v1.4.0/rabbitmq-dotnet-client-1.4.0-net-2.0-htmldoc/type-RabbitMQ.Client.Events.EventingBasicConsumer.html
/// 参照 : BasicConsume https://www.rabbitmq.com/releases/rabbitmq-dotnet-client/v1.4.0/rabbitmq-dotnet-client-1.4.0-net-2.0-htmldoc/type-RabbitMQ.Client.IModel.html#method-M:RabbitMQ.Client.IModel.BasicConsume(System.UInt16,System.String,System.Boolean,System.Collections.IDictionary,RabbitMQ.Client.IBasicConsumer)
/// 参照 : C#でconstな配列を実現する (もっとクールにプログラミング) http://pgnote.net/?p=885
/// </summary>
class Program
{
    const int ENQUEUE_INDEX = 0;
    const int DEQUEUE_INDEX = 1;
    const int DELETEQUEUE_INDEX = 2;
    const int NUM_INDEX = 3;
    const string HOST_NAME = "localhost";
    static string[] QUEUE_NAMES = new string[NUM_INDEX];
    /// <summary>
    /// キューの寿命(複合可能)
    /// durable    : RabbitMQが止まってもキューを残す
    /// autodelete : コンシューマーが1人も接続していなかったら消す
    /// passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
    /// exclusive  : この接続でだけ使える。この接続が切れたら消す
    /// </summary>
    static LifeSpanType[] lifeSpans_queue = new LifeSpanType[NUM_INDEX];
    static bool[]
        durable_lifeSpans = new bool[NUM_INDEX],
        autodelete_lifeSpans = new bool[NUM_INDEX],
        passive_lifeSpans = new bool[NUM_INDEX],
        exclusive_lifeSpans = new bool[NUM_INDEX];
    static void SetLifeSpan(int index_queue, string name_queue, LifeSpanType lifeSpan)
    {
        QUEUE_NAMES[index_queue] = name_queue;
        lifeSpans_queue[index_queue] = lifeSpan;

        // 一旦クリアー
        durable_lifeSpans[index_queue] = false;
        autodelete_lifeSpans[index_queue] = false;
        passive_lifeSpans[index_queue] = false;
        exclusive_lifeSpans[index_queue] = false;

        // durable
        if (((int)lifeSpan & (int)LifeSpanType.Durable) == (int)LifeSpanType.Durable) { durable_lifeSpans[index_queue] = true; }
        // autodelete
        if (((int)lifeSpan & (int)LifeSpanType.Autodelete) == (int)LifeSpanType.Autodelete) { autodelete_lifeSpans[index_queue] = true; }
        // passive
        if (((int)lifeSpan & (int)LifeSpanType.Passive) == (int)LifeSpanType.Passive) { passive_lifeSpans[index_queue] = true; }
        // exclusive
        if (((int)lifeSpan & (int)LifeSpanType.Exclusive) == (int)LifeSpanType.Exclusive) { exclusive_lifeSpans[index_queue] = true; }
    }

    // 回転式バッファー
    public static RotationBuffer rollingBuffer = new RotationBuffer();

    public static ConnectionFactory GetFactory()
    {
        if (null == m_factory_)
        {
            m_factory_ = new ConnectionFactory() { HostName = HOST_NAME };
        }
        return m_factory_;
    }
    static ConnectionFactory m_factory_;

    public static IConnection GetConnection()
    {
        if (null == m_connection_)
        {
            m_connection_ = GetFactory().CreateConnection();
        }
        return m_connection_;
    }
    static IConnection m_connection_;

    public static bool IsConnected()
    {
        if (null != m_connection_)
        {
            return m_connection_.IsOpen;
        }
        return false;
    }

    public static IModel GetChannel(int index)
    {
        if (null == m_channels_[index])
        {
            m_channels_[index] = GetConnection().CreateModel();

            // 引数が7つの QueueDeclare でエラーを吐くことがあるので、情報を出力しておく。
            Console.Error.WriteLine("GetChannel index = [" + index + "]");
            Console.Error.WriteLine("    QUEUE_NAMES[index] = [" + QUEUE_NAMES[index] + "]");
            Console.Error.WriteLine("    passive_lifeSpans[index] = [" + passive_lifeSpans[index] + "]");
            Console.Error.WriteLine("    durable_lifeSpans[index] = [" + durable_lifeSpans[index] + "]");
            Console.Error.WriteLine("    exclusive_lifeSpans[index] = [" + exclusive_lifeSpans[index] + "]");
            Console.Error.WriteLine("    autodelete_lifeSpans[index] = [" + autodelete_lifeSpans[index] + "]");
            Console.Error.WriteLine("    nowait は仮に false 固定");
            Console.Error.WriteLine("    arguments は仮に null 固定");

#if UBUNTU
            // Ubuntuでは何故か Spring.Messaging.Amqp.Rabbit の引数 7 つのやつになっている。
            // m_channels_[index].QueueDeclare(QUEUE_NAMES[index], false, false, false, false, false, null);
            m_channels_[index].QueueDeclare(QUEUE_NAMES[index], passive_lifeSpans[index], durable_lifeSpans[index], exclusive_lifeSpans[index], autodelete_lifeSpans[index], false, null);
#else
            //m_channels_[index].QueueDeclare(QUEUE_NAMES[index], false, false, false, null);
            m_channels_[index].QueueDeclare(QUEUE_NAMES[index], durable_lifeSpans[index], exclusive_lifeSpans[index], autodelete_lifeSpans[index], null);
#endif
        }
        return m_channels_[index];
    }
    static IModel[] m_channels_ = new IModel[2];

    public static EventingBasicConsumer GetConsumer(int index)
    {
        if (null == m_consumers_[index])
        {
#if UBUNTU
            // Ubuntuでは何故か v1.4.0 の引数が 0 個のやつになっている。調べたが引数が1個~6個のものは無かった。
            m_consumers_[index] = new EventingBasicConsumer();
#else
            m_consumers_[index] = new EventingBasicConsumer(GetChannel(index));
#endif

        }
        return m_consumers_[index];
    }
    static EventingBasicConsumer[] m_consumers_ = new EventingBasicConsumer[2];

    /// <summary>
    /// 受信できたときに割り込んでくる処理
    /// </summary>
#if UBUNTU
    public static BasicDeliverEventHandler GetDequeueHandler()
#else
    public static EventHandler<BasicDeliverEventArgs> GetDequeueHandler()
#endif
    {
        if (null == m_dequeueHandler_)
        {
#if UBUNTU
            m_dequeueHandler_ = new BasicDeliverEventHandler((model, ea) =>
#else
            m_dequeueHandler_ = new EventHandler<BasicDeliverEventArgs>((model, ea) =>
#endif
            {
                byte[] body = ea.Body;
                string message = Encoding.UTF8.GetString(body);

                // 回転式バッファーに入れる
                rollingBuffer.PutMessage(message);
            });
        }

        return m_dequeueHandler_;
    }

#if UBUNTU
    static BasicDeliverEventHandler m_dequeueHandler_;
#else
    static EventHandler<BasicDeliverEventArgs> m_dequeueHandler_;
#endif

    /// <summary>
    /// 対応するオープンは無いけれど、開けたら閉める、を完璧に対応する必要がある。
    /// </summary>
    static void CloseChannel(int index)
    {
        if (null != m_channels_[index])
        {
            m_channels_[index].Close();
            m_channels_[index] = null;
        }
    }

    static uint DeleteQueue()
    {
        IModel channel = GetChannel(DELETEQUEUE_INDEX);

#if UBUNTU
            // Ubuntuでは何故か 昔の .Net 用の引数 5 つのやつの変則版になっている。
            // uint QueueDelete(ushort ticket, string queue, bool ifUnused, bool ifEmpty, bool nowait)
            uint result = channel.QueueDelete( QUEUE_NAMES[DELETEQUEUE_INDEX],true,true,true);
#else
        uint result = channel.QueueDelete(QUEUE_NAMES[DELETEQUEUE_INDEX], true, true);
#endif

        // 対応するオープンは無いが、ちゃんと閉じないと、レシーブしてくれない。
        CloseChannel(DELETEQUEUE_INDEX);
        return result;
    }

    static void Enqueue(string message)
    {
        // 末尾に daze を付ける。
        message += "daze";

        IModel channel = GetChannel(ENQUEUE_INDEX);
        byte[] body = Encoding.UTF8.GetBytes(message);
        channel.BasicPublish("", QUEUE_NAMES[ENQUEUE_INDEX], null, body);

        // 対応するオープンは無いが、ちゃんと閉じないと、レシーブしてくれない。
        CloseChannel(ENQUEUE_INDEX);
    }

    static void StartConsume()
    {
        IModel channel = GetChannel(DEQUEUE_INDEX);
        EventingBasicConsumer consumer = GetConsumer(DEQUEUE_INDEX);
        // 受信できたときに割り込んでくる処理
        consumer.Received += GetDequeueHandler();

#if UBUNTU
        // Ubuntuでは何故か引数が 5 個のやつになっている。
        channel.BasicConsume( QUEUE_NAMES[DEQUEUE_INDEX], true, "", null, consumer);
#else
        channel.BasicConsume(QUEUE_NAMES[DEQUEUE_INDEX], true, consumer);
#endif

        // 終了はさせない
        // consumer.Received -= GetReceiveHandler();
        // CloseChannel(DEQUEUE_INDEX);
    }

    static void Main(string[] args)
    {
        //----------------------------------------
        // Delete
        //----------------------------------------
        for (;;)
        {
            Console.Write(@"削除したいキューがあれば名前を、無ければ空文字列を入れろだぜ☆(^~^)
キュー名を入力    : キューを削除します
空文字列で[Enter] : 次のステップへ進む
Name or empty ? > ");
            string queueName_delete = Console.ReadLine();
            if ("" == queueName_delete.Trim())
            {
                break;
            }


            Console.Write(@"削除するメッセージ・キューの寿命を、足し算して答えろだぜ☆(^~^)
(" + ((int)LifeSpanType.Durable) + @") durable    : RabbitMQが止まってもキューを残す
(" + ((int)LifeSpanType.Autodelete) + @") autodelete : コンシューマーが1人も接続していなかったら消す
(" + ((int)LifeSpanType.Passive) + @") passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(" + ((int)LifeSpanType.Exclusive) + @") exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > ");
            LifeSpanType lifeSpan_delete = (LifeSpanType)int.Parse(Console.ReadLine());
            SetLifeSpan(DELETEQUEUE_INDEX, queueName_delete, lifeSpan_delete);
            uint result = DeleteQueue();
            Console.WriteLine(@"[" + queueName_delete + "]キューを削除したはずだぜ☆(^~^) result=[" + result + "]");
        }

        //----------------------------------------
        // Enqueue settings
        //----------------------------------------
        for (;;)
        {
            Console.Write(@"エンキュー先のメッセージ・キューの名前を入れろだぜ☆(^~^)
Queue name? > ");
            string queueName_enqueue = Console.ReadLine();

            Console.Write(@"エンキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆(^~^)
(" + ((int)LifeSpanType.Durable) + @") durable    : RabbitMQが止まってもキューを残す
(" + ((int)LifeSpanType.Autodelete) + @") autodelete : コンシューマーが1人も接続していなかったら消す
(" + ((int)LifeSpanType.Passive) + @") passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(" + ((int)LifeSpanType.Exclusive) + @") exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > ");
            int lifeSpan_enqueue;
            if (int.TryParse(Console.ReadLine(), out lifeSpan_enqueue))
            {
                SetLifeSpan(ENQUEUE_INDEX, queueName_enqueue, (LifeSpanType)lifeSpan_enqueue);
                break;
            }
        }

        //----------------------------------------
        // Enqueue settings
        //----------------------------------------
        for (;;)
        {
            Console.Write(@"デキュー先のメッセージ・キューの名前を入れろだぜ☆(^~^)
Queue name? > ");
            string queueName_dequeue = Console.ReadLine();

            Console.Write(@"デキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆(^~^)
(" + ((int)LifeSpanType.Durable) + @") durable    : RabbitMQが止まってもキューを残す
(" + ((int)LifeSpanType.Autodelete) + @") autodelete : コンシューマーが1人も接続していなかったら消す
(" + ((int)LifeSpanType.Passive) + @") passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(" + ((int)LifeSpanType.Exclusive) + @") exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > ");
            int lifeSpan_dequeue;
            if (int.TryParse(Console.ReadLine(), out lifeSpan_dequeue))
            {
                SetLifeSpan(DEQUEUE_INDEX, queueName_dequeue, (LifeSpanType)lifeSpan_dequeue);

                StartConsume();
                break;
            }
        }

        // 常時監視をスタート
        StartConsume();

        // 無限ループ
        while (IsConnected())
        {
            string message = rollingBuffer.GetMessage();
            if (null != message)
            {
                // エンキューする
                Enqueue(message);
            }
            Thread.Sleep(20);
        }

        // このプログラムは、自分では接続を切らない。
    }
}

/// <summary>
/// 回転式バッファー。
/// これはメイン・スレッドに置く。
/// デキューのスレッドでエンキューすることはできない。
/// デキュー処理は、回転式バッファーを仲介にしてエンキュー処理にメッセージを渡す。
/// </summary>
public class RotationBuffer
{
    public const int bufferSize = 100;
    public string[] buffer = new string[bufferSize];
    public int[] bufferCursors = new int[] { 0, 0 };
    public const int PUT_INDEX = 0;
    public const int GET_INDEX = 1;
    public void PutMessage(string message)
    {
        buffer[bufferCursors[PUT_INDEX]] = message;
        bufferCursors[PUT_INDEX]++;
        if (!(bufferCursors[PUT_INDEX] < bufferSize))
        {
            bufferCursors[PUT_INDEX] = 0;
        }
    }
    public string GetMessage()
    {
        if (null != buffer[bufferCursors[GET_INDEX]])
        {
            string message = buffer[bufferCursors[GET_INDEX]];
            buffer[bufferCursors[GET_INDEX]] = null;
            bufferCursors[GET_INDEX]++;
            if (!(bufferCursors[GET_INDEX] < bufferSize))
            {
                bufferCursors[GET_INDEX] = 0;
            }
            return message;
        }
        return null;
    }
}

改造し終わってから気づいたんだが、バックグラウンドで動かすプログラムでは 標準入出力で設定なんかできない。

tamesi35a2_cs.cs コマンドライン引数指定版

こんなんでいいんだろうか?

//--------------------------------------------------------------------------------
// tamesi35a2_cs.cs
//
// メッセージの末尾に daze を付けます。
// 標準入出力は、キーボード、画面には接続しないようにします。
// キューの名前はコマンドライン引数で指定してください。
// デキューは割込みを受け付けます。
//--------------------------------------------------------------------------------
// OS  : Windows 10
// IDE : Visual Studio 2015
//       Install : NuGet   : Install-Package RabbitMQ.Client -Version 4.1.1
//
// OS  : Ubuntu 16.04
//       Compile : Command : mcs /r:RabbitMQ.Client.dll -define:UBUNTU tamesi35a2_cs.cs
//       Execute : Command : // バックグラウンドで実行する
//                         : nohup ./tamesi35a2_cs.exe --msgqueue 1117 durable 1116 durable > ./tamesi35a2_cs.out.log 2> ./tamesi35a2_cs.err.log < /dev/null &
//       Check   :         : // tamesi33a1_cs.exe を使う
//--------------------------------------------------------------------------------

// Ubuntu の RabbitMQ はソースのバージョンが古いのか、API が異なった。
// #define UBUNTU

using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Text;
using System.Threading;

/// <summary>
/// このプログラム内だけで使われる数字。AMQP-CPPでの実装とは異なる。
/// AMQP-CPPでの実装 : AMQP::durable=[1] AMQP::autodelete=[2] AMQP::passive=[8] AMQP::exclusive=[512]
/// </summary>
[Flags]
enum LifeSpanType
{
    /// <summary>
    /// RabbitMQが止まってもキューを残す
    /// </summary>
    Durable = 1,
    /// <summary>
    /// コンシューマーが1人も接続していなかったら消す
    /// </summary>
    Autodelete = 0x1 << 1,
    /// <summary>
    /// キューが存在するかどうかチェックするだけ。中身見ない時これ
    /// </summary>
    Passive = 0x1 << 2,
    /// <summary>
    /// この接続でだけ使える。この接続が切れたら消す
    /// </summary>
    Exclusive = 0x1 << 3
}

/// <summary>
/// 参照 : QueueDeclare http://docs.spring.io/spring-amqp-net/docs/1.0.x/api/html/Spring.Messaging.Amqp.Rabbit~Spring.Messaging.Amqp.Rabbit.Connection.CachedModel~QueueDeclare(String,Boolean,Boolean,Boolean,Boolean,Boolean,IDictionary).html
/// 参照 : EventingBasicConsumer https://www.rabbitmq.com/releases/rabbitmq-dotnet-client/v1.4.0/rabbitmq-dotnet-client-1.4.0-net-2.0-htmldoc/type-RabbitMQ.Client.Events.EventingBasicConsumer.html
/// 参照 : BasicConsume https://www.rabbitmq.com/releases/rabbitmq-dotnet-client/v1.4.0/rabbitmq-dotnet-client-1.4.0-net-2.0-htmldoc/type-RabbitMQ.Client.IModel.html#method-M:RabbitMQ.Client.IModel.BasicConsume(System.UInt16,System.String,System.Boolean,System.Collections.IDictionary,RabbitMQ.Client.IBasicConsumer)
/// 参照 : C#でconstな配列を実現する (もっとクールにプログラミング) http://pgnote.net/?p=885
/// </summary>
class Program
{
    const int ENQUEUE_INDEX = 0;
    const int DEQUEUE_INDEX = 1;
    const int NUM_INDEX = 2;
    const string HOST_NAME = "localhost";
    static string[] name_queues = new string[NUM_INDEX];
    /// <summary>
    /// キューの寿命(複合可能)
    /// durable    : RabbitMQが止まってもキューを残す
    /// autodelete : コンシューマーが1人も接続していなかったら消す
    /// passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
    /// exclusive  : この接続でだけ使える。この接続が切れたら消す
    /// </summary>
    static LifeSpanType[] lifeSpan_queues = new LifeSpanType[NUM_INDEX];
    static LifeSpanType LifeSpanString_To_Enum(string lifeSpan)
    {
        switch (lifeSpan)
        {
            case "durable": return LifeSpanType.Durable;
            case "autodelete": return LifeSpanType.Autodelete;
            case "passive": return LifeSpanType.Passive;
            case "exclusive": return LifeSpanType.Exclusive;
            default: throw new Exception("未対応のlifeSpan [" + lifeSpan + "]");
        }
    }
    static bool[]
        durable_lifeSpans = new bool[NUM_INDEX],
        autodelete_lifeSpans = new bool[NUM_INDEX],
        passive_lifeSpans = new bool[NUM_INDEX],
        exclusive_lifeSpans = new bool[NUM_INDEX];
    static void SetLifeSpan(int index_queue, string name_queue, LifeSpanType lifeSpan)
    {
        name_queues[index_queue] = name_queue;
        lifeSpan_queues[index_queue] = lifeSpan;

        // 一旦クリアー
        durable_lifeSpans[index_queue] = false;
        autodelete_lifeSpans[index_queue] = false;
        passive_lifeSpans[index_queue] = false;
        exclusive_lifeSpans[index_queue] = false;

        // durable
        if (((int)lifeSpan & (int)LifeSpanType.Durable) == (int)LifeSpanType.Durable) { durable_lifeSpans[index_queue] = true; }
        // autodelete
        if (((int)lifeSpan & (int)LifeSpanType.Autodelete) == (int)LifeSpanType.Autodelete) { autodelete_lifeSpans[index_queue] = true; }
        // passive
        if (((int)lifeSpan & (int)LifeSpanType.Passive) == (int)LifeSpanType.Passive) { passive_lifeSpans[index_queue] = true; }
        // exclusive
        if (((int)lifeSpan & (int)LifeSpanType.Exclusive) == (int)LifeSpanType.Exclusive) { exclusive_lifeSpans[index_queue] = true; }
    }

    // 回転式バッファー
    static RotationBuffer rollingBuffer = new RotationBuffer();

    static ConnectionFactory GetFactory()
    {
        if (null == m_factory_)
        {
            m_factory_ = new ConnectionFactory() { HostName = HOST_NAME };
        }
        return m_factory_;
    }
    static ConnectionFactory m_factory_;

    static IConnection GetConnection()
    {
        if (null == m_connection_)
        {
            m_connection_ = GetFactory().CreateConnection();
        }
        return m_connection_;
    }
    static IConnection m_connection_;

    static bool IsConnected()
    {
        if (null != m_connection_)
        {
            return m_connection_.IsOpen;
        }
        return false;
    }

    static IModel GetChannel(int index)
    {
        if (null == m_channels_[index])
        {
            m_channels_[index] = GetConnection().CreateModel();

            // 引数が7つの QueueDeclare でエラーを吐くことがあるので、情報を出力しておく。
            Console.Error.WriteLine("GetChannel index = [" + index + "]");
            Console.Error.WriteLine("    QUEUE_NAMES[index] = [" + name_queues[index] + "]");
            Console.Error.WriteLine("    passive_lifeSpans[index] = [" + passive_lifeSpans[index] + "]");
            Console.Error.WriteLine("    durable_lifeSpans[index] = [" + durable_lifeSpans[index] + "]");
            Console.Error.WriteLine("    exclusive_lifeSpans[index] = [" + exclusive_lifeSpans[index] + "]");
            Console.Error.WriteLine("    autodelete_lifeSpans[index] = [" + autodelete_lifeSpans[index] + "]");
            Console.Error.WriteLine("    nowait は仮に false 固定");
            Console.Error.WriteLine("    arguments は仮に null 固定");

#if UBUNTU
            // Ubuntuでは何故か Spring.Messaging.Amqp.Rabbit の引数 7 つのやつになっている。
            // m_channels_[index].QueueDeclare(QUEUE_NAMES[index], false, false, false, false, false, null);
            m_channels_[index].QueueDeclare(QUEUE_NAMES[index], passive_lifeSpans[index], durable_lifeSpans[index], exclusive_lifeSpans[index], autodelete_lifeSpans[index], false, null);
#else
            //m_channels_[index].QueueDeclare(QUEUE_NAMES[index], false, false, false, null);
            m_channels_[index].QueueDeclare(name_queues[index], durable_lifeSpans[index], exclusive_lifeSpans[index], autodelete_lifeSpans[index], null);
#endif
        }
        return m_channels_[index];
    }
    static IModel[] m_channels_ = new IModel[2];

    static EventingBasicConsumer GetConsumer(int index)
    {
        if (null == m_consumers_[index])
        {
#if UBUNTU
            // Ubuntuでは何故か v1.4.0 の引数が 0 個のやつになっている。調べたが引数が1個~6個のものは無かった。
            m_consumers_[index] = new EventingBasicConsumer();
#else
            m_consumers_[index] = new EventingBasicConsumer(GetChannel(index));
#endif

        }
        return m_consumers_[index];
    }
    static EventingBasicConsumer[] m_consumers_ = new EventingBasicConsumer[2];

    /// <summary>
    /// 受信できたときに割り込んでくる処理
    /// </summary>
#if UBUNTU
    public static BasicDeliverEventHandler GetDequeueHandler()
#else
    static EventHandler<BasicDeliverEventArgs> GetDequeueHandler()
#endif
    {
        if (null == m_dequeueHandler_)
        {
#if UBUNTU
            m_dequeueHandler_ = new BasicDeliverEventHandler((model, ea) =>
#else
            m_dequeueHandler_ = new EventHandler<BasicDeliverEventArgs>((model, ea) =>
#endif
            {
                byte[] body = ea.Body;
                string message = Encoding.UTF8.GetString(body);

                // 回転式バッファーに入れる
                rollingBuffer.PutMessage(message);
            });
        }

        return m_dequeueHandler_;
    }

#if UBUNTU
    static BasicDeliverEventHandler m_dequeueHandler_;
#else
    static EventHandler<BasicDeliverEventArgs> m_dequeueHandler_;
#endif

    /// <summary>
    /// 対応するオープンは無いけれど、開けたら閉める、を完璧に対応する必要がある。
    /// </summary>
    static void CloseChannel(int index)
    {
        if (null != m_channels_[index])
        {
            m_channels_[index].Close();
            m_channels_[index] = null;
        }
    }

    static void Enqueue(string message)
    {
        // 末尾に daze を付ける。
        message += "daze";

        IModel channel = GetChannel(ENQUEUE_INDEX);
        byte[] body = Encoding.UTF8.GetBytes(message);
        channel.BasicPublish("", name_queues[ENQUEUE_INDEX], null, body);

        // 対応するオープンは無いが、ちゃんと閉じないと、レシーブしてくれない。
        CloseChannel(ENQUEUE_INDEX);
    }

    static void StartConsume()
    {
        IModel channel = GetChannel(DEQUEUE_INDEX);
        EventingBasicConsumer consumer = GetConsumer(DEQUEUE_INDEX);
        // 受信できたときに割り込んでくる処理
        consumer.Received += GetDequeueHandler();

#if UBUNTU
        // Ubuntuでは何故か引数が 5 個のやつになっている。
        channel.BasicConsume( QUEUE_NAMES[DEQUEUE_INDEX], true, "", null, consumer);
#else
        channel.BasicConsume(name_queues[DEQUEUE_INDEX], true, consumer);
#endif

        // 終了はさせない
        // consumer.Received -= GetReceiveHandler();
        // CloseChannel(DEQUEUE_INDEX);
    }

    static void Main(string[] args)
    {
        if (args.Length<1)
        {
            goto gt_Error1;
        }

        // 引数の解析
        {
            // 与件
            // 「--msgqueue 1117 durable 1116 durable」という引数は「--msgqueue」「1117」「durable」「1116」「durable」に分解される。

            // 受け皿
            // 0:enqueue 1:dequeue

            // 記憶
            int m0 = 0; // カーソル相当

            // 解析器
            foreach (string a in args)
            {
                if (m0==0)
                {
                    switch (a)
                    {
                        case "--msgqueue": m0++; break;
                        default: goto gt_Error1;
                    }
                }
                else //--msgqueue 内容
                {
                    switch ((m0 - 1) % 2) // 引数名の分1引いている
                    {
                        case 0: name_queues[(m0 - 1) / 2] = a; m0++; break;
                        case 1: lifeSpan_queues[(m0 - 1) / 2] = LifeSpanString_To_Enum(a); m0++; break;
                    }
                }
            }

            // 結果
            for (int i = 0; i < 2; i++)
            {
                SetLifeSpan(i, name_queues[i], lifeSpan_queues[i]);
            }
        }

        // 常時監視をスタート
        StartConsume();

        // 無限ループ
        while (IsConnected())
        {
            string message = rollingBuffer.GetMessage();
            if (null != message)
            {
                // エンキューする
                Enqueue(message);
            }
            Thread.Sleep(20);
        }

        // このプログラムは、自分では接続を切らない。
        return;

        gt_Error1:
            throw new Exception(@"コマンドライン引数に「--msgqueue キュー名 寿命 キュー名 寿命」を指定しろだぜ☆(^~^)
エンキュー先、デキュー先の順番な☆(^▽^)
寿命は durable, autodelete, passive, exclusive のいずれかで、
複合指定にはまだ対応していないぜ☆(>_<)");
    }
}

/// <summary>
/// 回転式バッファー。
/// これはメイン・スレッドに置く。
/// デキューのスレッドでエンキューすることはできない。
/// デキュー処理は、回転式バッファーを仲介にしてエンキュー処理にメッセージを渡す。
/// </summary>
public class RotationBuffer
{
    public const int bufferSize = 100;
    public string[] buffer = new string[bufferSize];
    public int[] bufferCursors = new int[] { 0, 0 };
    public const int PUT_INDEX = 0;
    public const int GET_INDEX = 1;
    public void PutMessage(string message)
    {
        buffer[bufferCursors[PUT_INDEX]] = message;
        bufferCursors[PUT_INDEX]++;
        if (!(bufferCursors[PUT_INDEX] < bufferSize))
        {
            bufferCursors[PUT_INDEX] = 0;
        }
    }
    public string GetMessage()
    {
        if (null != buffer[bufferCursors[GET_INDEX]])
        {
            string message = buffer[bufferCursors[GET_INDEX]];
            buffer[bufferCursors[GET_INDEX]] = null;
            bufferCursors[GET_INDEX]++;
            if (!(bufferCursors[GET_INDEX] < bufferSize))
            {
                bufferCursors[GET_INDEX] = 0;
            }
            return message;
        }
        return null;
    }
}

そういえば今まで標準入力を捨ててなかった気がするな。

./tamesi35a2_cs.exe --msgqueue 1117 durable 1116 durable > ./tamesi35a2_cs.out.log 2> ./tamesi35a2_cs.err.log < /dev/null &
# ./tamesi34_cs.exe
削除したいキューがあれば名前を、無ければ空文字列を入れろだぜ☆(^~^)
キュー名を入力    : キューを削除します
空文字列で[Enter] : 次のステップへ進む
Name or empty ? >
エンキュー先のメッセージ・キューの名前を入れろだぜ☆(^~^)
Queue name? > 1116
エンキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆(^~^)
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが1人も接続していなかったら消す
(4) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(8) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > 0
デキュー先のメッセージ・キューの名前を入れろだぜ☆(^~^)
Queue name? > 1117
デキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆(^~^)
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが1人も接続していなかったら消す
(4) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(8) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > 0
GetChannel index = [1]
    QUEUE_NAMES[index] = [1117]
    passive_lifeSpans[index] = [False]
    durable_lifeSpans[index] = [False]
    exclusive_lifeSpans[index] = [False]
    autodelete_lifeSpans[index] = [False]
    nowait は仮に false 固定
    arguments は仮に null 固定

Unhandled Exception:
RabbitMQ.Client.Exceptions.OperationInterruptedException: The AMQP operation was interrupted: AMQP close-reason, initiated by Peer, code=406, text="PRECONDITION_FAILED - inequivalent arg 'durable' for queue '1117' in vhost '/': received 'false' but current is 'true'", classId=50, methodId=10, cause=
  at RabbitMQ.Client.Impl.SimpleBlockingRpcContinuation.GetReply () <0x4013c420 + 0x00107> in <filename unknown>:0
  at RabbitMQ.Client.Impl.ModelBase.ModelRpc (RabbitMQ.Client.Impl.MethodBase method, RabbitMQ.Client.Impl.ContentHeaderBase header, System.Byte[] body) <0x4013a290 + 0x000fd> in <filename unknown>:0
  at RabbitMQ.Client.Framing.Impl.v0_8.Model.QueueDeclare (System.String queue, Boolean passive, Boolean durable, Boolean exclusive, Boolean autoDelete, Boolean nowait, IDictionary arguments) <0x4013e6d0 + 0x00113> in <filename unknown>:0
  at UsagiMQ.Program.GetChannel (Int32 index) <0x400ffd20 + 0x00436> in <filename unknown>:0
  at UsagiMQ.Program.StartConsume (System.String name_queue, LifeSpanType lifeSpan_queue) <0x400ffc40 + 0x0002b> in <filename unknown>:0
  at UsagiMQ.Program.Main (System.String[] args) <0x400fad50 + 0x0096b> in <filename unknown>:0
[ERROR] FATAL UNHANDLED EXCEPTION: RabbitMQ.Client.Exceptions.OperationInterruptedException: The AMQP operation was interrupted: AMQP close-reason, initiated by Peer, code=406, text="PRECONDITION_FAILED - inequivalent arg 'durable' for queue '1117' in vhost '/': received 'false' but current is 'true'", classId=50, methodId=10, cause=
  at RabbitMQ.Client.Impl.SimpleBlockingRpcContinuation.GetReply () <0x4013c420 + 0x00107> in <filename unknown>:0
  at RabbitMQ.Client.Impl.ModelBase.ModelRpc (RabbitMQ.Client.Impl.MethodBase method, RabbitMQ.Client.Impl.ContentHeaderBase header, System.Byte[] body) <0x4013a290 + 0x000fd> in <filename unknown>:0
  at RabbitMQ.Client.Framing.Impl.v0_8.Model.QueueDeclare (System.String queue, Boolean passive, Boolean durable, Boolean exclusive, Boolean autoDelete, Boolean nowait, IDictionary arguments) <0x4013e6d0 + 0x00113> in <filename unknown>:0
  at UsagiMQ.Program.GetChannel (Int32 index) <0x400ffd20 + 0x00436> in <filename unknown>:0
  at UsagiMQ.Program.StartConsume (System.String name_queue, LifeSpanType lifeSpan_queue) <0x400ffc40 + 0x0002b> in <filename unknown>:0
  at UsagiMQ.Program.Main (System.String[] args) <0x400fad50 + 0x0096b> in <filename unknown>:0

チャンネル宣言の引数が4つfalse なのもおかしい。もっと情報を出そう。

Visual Studio 2015 で tamesi34_cs.cs を動かしてみた

削除したいキューがあれば名前を、無ければ空文字列を入れろだぜ☆(^~^)
キュー名を入力    : キューを削除します
空文字列で[Enter] : 次のステップへ進む
Name or empty ? >
エンキュー先のメッセージ・キューの名前を入れろだぜ☆(^~^)
Queue name? > 1117
エンキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆(^~^)
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが1人も接続していなかったら消す
(4) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(8) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > 0
デキュー先のメッセージ・キューの名前を入れろだぜ☆(^~^)
Queue name? > 1116
デキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆(^~^)
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが1人も接続していなかったら消す
(4) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(8) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > 0
tamesi34_cs.cs Dump
    name_queues         [ENQUEUE_INDEX]    =[1117]
    lifeSpan_queues     [ENQUEUE_INDEX]    =[0]
    durable_lifeSpans   [ENQUEUE_INDEX]    =[False]
    autodelete_lifeSpans[ENQUEUE_INDEX]    =[False]
    passive_lifeSpans   [ENQUEUE_INDEX]    =[False]
    exclusive_lifeSpans [ENQUEUE_INDEX]    =[False]
    ----
    name_queues         [DEQUEUE_INDEX]    =[1116]
    lifeSpan_queues     [DEQUEUE_INDEX]    =[0]
    durable_lifeSpans   [DEQUEUE_INDEX]    =[False]
    autodelete_lifeSpans[DEQUEUE_INDEX]    =[False]
    passive_lifeSpans   [DEQUEUE_INDEX]    =[False]
    exclusive_lifeSpans [DEQUEUE_INDEX]    =[False]
    ----
    name_queues         [DELETEQUEUE_INDEX]=[]
    lifeSpan_queues     [DELETEQUEUE_INDEX]=[0]
    durable_lifeSpans   [DELETEQUEUE_INDEX]=[False]
    autodelete_lifeSpans[DELETEQUEUE_INDEX]=[False]
    passive_lifeSpans   [DELETEQUEUE_INDEX]=[False]
    exclusive_lifeSpans [DELETEQUEUE_INDEX]=[False]
    ----

(tamesi34_cs.cs?) GetChannel index = [1]
    name_queues[index] = [1116]
    passive_lifeSpans[index] = [False]
    durable_lifeSpans[index] = [False]
    exclusive_lifeSpans[index] = [False]
    autodelete_lifeSpans[index] = [False]
    nowait は仮に false 固定
    arguments は仮に null 固定

おかしいじゃないか。false が4つ入っている。

あっ! durable、0スタートじゃなくて 1スタートに変えたんだった!

削除したいキューがあれば名前を、無ければ空文字列を入れろだぜ☆(^~^)
キュー名を入力    : キューを削除します
空文字列で[Enter] : 次のステップへ進む
Name or empty ? >
エンキュー先のメッセージ・キューの名前を入れろだぜ☆(^~^)
Queue name? > 1117
エンキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆(^~^)
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが1人も接続していなかったら消す
(4) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(8) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > 1
デキュー先のメッセージ・キューの名前を入れろだぜ☆(^~^)
Queue name? > 1116
デキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆(^~^)
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが1人も接続していなかったら消す
(4) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(8) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > 1
tamesi34_cs.cs Dump
    name_queues         [ENQUEUE_INDEX]    =[1117]
    lifeSpan_queues     [ENQUEUE_INDEX]    =[Durable]
    durable_lifeSpans   [ENQUEUE_INDEX]    =[True]
    autodelete_lifeSpans[ENQUEUE_INDEX]    =[False]
    passive_lifeSpans   [ENQUEUE_INDEX]    =[False]
    exclusive_lifeSpans [ENQUEUE_INDEX]    =[False]
    ----
    name_queues         [DEQUEUE_INDEX]    =[1116]
    lifeSpan_queues     [DEQUEUE_INDEX]    =[Durable]
    durable_lifeSpans   [DEQUEUE_INDEX]    =[True]
    autodelete_lifeSpans[DEQUEUE_INDEX]    =[False]
    passive_lifeSpans   [DEQUEUE_INDEX]    =[False]
    exclusive_lifeSpans [DEQUEUE_INDEX]    =[False]
    ----
    name_queues         [DELETEQUEUE_INDEX]=[]
    lifeSpan_queues     [DELETEQUEUE_INDEX]=[0]
    durable_lifeSpans   [DELETEQUEUE_INDEX]=[False]
    autodelete_lifeSpans[DELETEQUEUE_INDEX]=[False]
    passive_lifeSpans   [DELETEQUEUE_INDEX]=[False]
    exclusive_lifeSpans [DELETEQUEUE_INDEX]=[False]
    ----

(tamesi34_cs.cs?) GetChannel index = [1]
    name_queues[index] = [1116]
    passive_lifeSpans[index] = [False]
    durable_lifeSpans[index] = [True]
    exclusive_lifeSpans[index] = [False]
    autodelete_lifeSpans[index] = [False]
    nowait は仮に false 固定
    arguments は仮に null 固定
終了するときは[Ctrl]+[C]キーを押せだぜ☆(^~^)
エンキューするときはメッセージを打ち込んで[Enter]キーを押せだぜ☆(^◇^)
Enqueue? >

ここまでは おっけー。

# ./tamesi35a2_cs.exe --msgqueue 1117 durable 1116 durable > ./tamesi35a2_cs.out.log 2> ./tamesi35a2_cs.err.log < /dev/null &
[1] 24403
# ./tamesi34_cs.exe
削除したいキューがあれば名前を、無ければ空文字列を入れろだぜ☆(^~^)
キュー名を入力    : キューを削除します
空文字列で[Enter] : 次のステップへ進む
Name or empty ? >
エンキュー先のメッセージ・キューの名前を入れろだぜ☆(^~^)
Queue name? > 1116
エンキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆(^~^)
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが1人も接続していなかったら消す
(4) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(8) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > 1
デキュー先のメッセージ・キューの名前を入れろだぜ☆(^~^)
Queue name? > 1117
デキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆(^~^)
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが1人も接続していなかったら消す
(4) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(8) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > 1
GetChannel index = [1]
    QUEUE_NAMES[index] = [1117]
    passive_lifeSpans[index] = [False]
    durable_lifeSpans[index] = [True]
    exclusive_lifeSpans[index] = [False]
    autodelete_lifeSpans[index] = [False]
    nowait は仮に false 固定
    arguments は仮に null 固定
終了するときは[Ctrl]+[C]キーを押せだぜ☆(^~^)
エンキューするときはメッセージを打ち込んで[Enter]キーを押せだぜ☆(^◇^)
Enqueue? >

1 にしたら、動いてるじゃないか。

Enqueue? > kame
GetChannel index = [0]
    QUEUE_NAMES[index] = [1116]
    passive_lifeSpans[index] = [False]
    durable_lifeSpans[index] = [True]
    exclusive_lifeSpans[index] = [False]
    autodelete_lifeSpans[index] = [False]
    nowait は仮に false 固定
    arguments は仮に null 固定

Unhandled Exception:
RabbitMQ.Client.Exceptions.OperationInterruptedException: The AMQP operation was interrupted: AMQP close-reason, initiated by Peer, code=406, text="PRECONDITION_FAILED - inequivalent arg 'durable' for queue '1116' in vhost '/': received 'true' but current is 'false'", classId=50, methodId=10, cause=
  at RabbitMQ.Client.Impl.SimpleBlockingRpcContinuation.GetReply () <0x42014f00 + 0x00103> in <filename unknown>:0
  at RabbitMQ.Client.Impl.ModelBase.ModelRpc (RabbitMQ.Client.Impl.MethodBase method, RabbitMQ.Client.Impl.ContentHeaderBase header, System.Byte[] body) <0x42012d90 + 0x000f9> in <filename unknown>:0
  at RabbitMQ.Client.Framing.Impl.v0_8.Model.QueueDeclare (System.String queue, Boolean passive, Boolean durable, Boolean exclusive, Boolean autoDelete, Boolean nowait, IDictionary arguments) <0x420171d0 + 0x00113> in <filename unknown>:0
  at UsagiMQ.Program.GetChannel (Int32 index) <0x41fd88d0 + 0x00436> in <filename unknown>:0
  at UsagiMQ.Program.Enqueue (System.String message) <0x420182e0 + 0x00013> in <filename unknown>:0
  at UsagiMQ.Program.Main (System.String[] args) <0x41fd3d50 + 0x0099f> in <filename unknown>:0
[ERROR] FATAL UNHANDLED EXCEPTION: RabbitMQ.Client.Exceptions.OperationInterruptedException: The AMQP operation was interrupted: AMQP close-reason, initiated by Peer, code=406, text="PRECONDITION_FAILED - inequivalent arg 'durable' for queue '1116' in vhost '/': received 'true' but current is 'false'", classId=50, methodId=10, cause=
  at RabbitMQ.Client.Impl.SimpleBlockingRpcContinuation.GetReply () <0x42014f00 + 0x00103> in <filename unknown>:0
  at RabbitMQ.Client.Impl.ModelBase.ModelRpc (RabbitMQ.Client.Impl.MethodBase method, RabbitMQ.Client.Impl.ContentHeaderBase header, System.Byte[] body) <0x42012d90 + 0x000f9> in <filename unknown>:0
  at RabbitMQ.Client.Framing.Impl.v0_8.Model.QueueDeclare (System.String queue, Boolean passive, Boolean durable, Boolean exclusive, Boolean autoDelete, Boolean nowait, IDictionary arguments) <0x420171d0 + 0x00113> in <filename unknown>:0
  at UsagiMQ.Program.GetChannel (Int32 index) <0x41fd88d0 + 0x00436> in <filename unknown>:0
  at UsagiMQ.Program.Enqueue (System.String message) <0x420182e0 + 0x00013> in <filename unknown>:0
  at UsagiMQ.Program.Main (System.String[] args) <0x41fd3d50 + 0x0099f> in <filename unknown>:0
[1]+  Exit 1                  ./tamesi35a2_cs.exe --msgqueue 1117 durable 1116 durable > ./tamesi35a2_cs.out.log 2> ./tamesi35a2_cs.err.log < /dev/null

動いてないぜ。

PRECONDITION_FAILED - inequivalent arg 'durable' for queue '1116' in vhost '/': received 'true' but current is 'false'

こういうエラーなんだろうけど。

じゃあ false にしたらいいのか?

あるいは エンキューを別スレッドにするのが まだ残っている

その前に Visual Studio 2015 でやったら

tamesi34_cs.exe

削除したいキューがあれば名前を、無ければ空文字列を入れろだぜ☆(^~^)
キュー名を入力    : キューを削除します
空文字列で[Enter] : 次のステップへ進む
Name or empty ? >
エンキュー先のメッセージ・キューの名前を入れろだぜ☆(^~^)
Queue name? > 1116
エンキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆(^~^)
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが1人も接続していなかったら消す
(4) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(8) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > 1
デキュー先のメッセージ・キューの名前を入れろだぜ☆(^~^)
Queue name? > 1117
デキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆(^~^)
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが1人も接続していなかったら消す
(4) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(8) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > 1
tamesi34_cs.cs Dump
    name_queues         [ENQUEUE_INDEX]    =[1116]
    lifeSpan_queues     [ENQUEUE_INDEX]    =[Durable]
    durable_lifeSpans   [ENQUEUE_INDEX]    =[True]
    autodelete_lifeSpans[ENQUEUE_INDEX]    =[False]
    passive_lifeSpans   [ENQUEUE_INDEX]    =[False]
    exclusive_lifeSpans [ENQUEUE_INDEX]    =[False]
    ----
    name_queues         [DEQUEUE_INDEX]    =[1117]
    lifeSpan_queues     [DEQUEUE_INDEX]    =[Durable]
    durable_lifeSpans   [DEQUEUE_INDEX]    =[True]
    autodelete_lifeSpans[DEQUEUE_INDEX]    =[False]
    passive_lifeSpans   [DEQUEUE_INDEX]    =[False]
    exclusive_lifeSpans [DEQUEUE_INDEX]    =[False]
    ----
    name_queues         [DELETEQUEUE_INDEX]=[]
    lifeSpan_queues     [DELETEQUEUE_INDEX]=[0]
    durable_lifeSpans   [DELETEQUEUE_INDEX]=[False]
    autodelete_lifeSpans[DELETEQUEUE_INDEX]=[False]
    passive_lifeSpans   [DELETEQUEUE_INDEX]=[False]
    exclusive_lifeSpans [DELETEQUEUE_INDEX]=[False]
    ----

(tamesi34_cs.cs?) GetChannel index = [1]
    name_queues[index] = [1117]
    passive_lifeSpans[index] = [False]
    durable_lifeSpans[index] = [True]
    exclusive_lifeSpans[index] = [False]
    autodelete_lifeSpans[index] = [False]
    nowait は仮に false 固定
    arguments は仮に null 固定
終了するときは[Ctrl]+[C]キーを押せだぜ☆(^~^)
エンキューするときはメッセージを打ち込んで[Enter]キーを押せだぜ☆(^◇^)
Enqueue? > usi
tamesi34_cs.cs Dump
    name_queues         [ENQUEUE_INDEX]    =[1116]
    lifeSpan_queues     [ENQUEUE_INDEX]    =[Durable]
    durable_lifeSpans   [ENQUEUE_INDEX]    =[True]
    autodelete_lifeSpans[ENQUEUE_INDEX]    =[False]
    passive_lifeSpans   [ENQUEUE_INDEX]    =[False]
    exclusive_lifeSpans [ENQUEUE_INDEX]    =[False]
    ----
    name_queues         [DEQUEUE_INDEX]    =[1117]
    lifeSpan_queues     [DEQUEUE_INDEX]    =[Durable]
    durable_lifeSpans   [DEQUEUE_INDEX]    =[True]
    autodelete_lifeSpans[DEQUEUE_INDEX]    =[False]
    passive_lifeSpans   [DEQUEUE_INDEX]    =[False]
    exclusive_lifeSpans [DEQUEUE_INDEX]    =[False]
    ----
    name_queues         [DELETEQUEUE_INDEX]=[]
    lifeSpan_queues     [DELETEQUEUE_INDEX]=[0]
    durable_lifeSpans   [DELETEQUEUE_INDEX]=[False]
    autodelete_lifeSpans[DELETEQUEUE_INDEX]=[False]
    passive_lifeSpans   [DELETEQUEUE_INDEX]=[False]
    exclusive_lifeSpans [DELETEQUEUE_INDEX]=[False]
    ----

(tamesi34_cs.cs?) GetChannel index = [0]
    name_queues[index] = [1116]
    passive_lifeSpans[index] = [False]
    durable_lifeSpans[index] = [True]
    exclusive_lifeSpans[index] = [False]
    autodelete_lifeSpans[index] = [False]
    nowait は仮に false 固定
    arguments は仮に null 固定
 Enqueue(^q^) usi
Enqueue? > <---- [interrupt!] Dequeue(^q^) usidaze
neko
tamesi34_cs.cs Dump
    name_queues         [ENQUEUE_INDEX]    =[1116]
    lifeSpan_queues     [ENQUEUE_INDEX]    =[Durable]
    durable_lifeSpans   [ENQUEUE_INDEX]    =[True]
    autodelete_lifeSpans[ENQUEUE_INDEX]    =[False]
    passive_lifeSpans   [ENQUEUE_INDEX]    =[False]
    exclusive_lifeSpans [ENQUEUE_INDEX]    =[False]
    ----
    name_queues         [DEQUEUE_INDEX]    =[1117]
    lifeSpan_queues     [DEQUEUE_INDEX]    =[Durable]
    durable_lifeSpans   [DEQUEUE_INDEX]    =[True]
    autodelete_lifeSpans[DEQUEUE_INDEX]    =[False]
    passive_lifeSpans   [DEQUEUE_INDEX]    =[False]
    exclusive_lifeSpans [DEQUEUE_INDEX]    =[False]
    ----
    name_queues         [DELETEQUEUE_INDEX]=[]
    lifeSpan_queues     [DELETEQUEUE_INDEX]=[0]
    durable_lifeSpans   [DELETEQUEUE_INDEX]=[False]
    autodelete_lifeSpans[DELETEQUEUE_INDEX]=[False]
    passive_lifeSpans   [DELETEQUEUE_INDEX]=[False]
    exclusive_lifeSpans [DELETEQUEUE_INDEX]=[False]
    ----

(tamesi34_cs.cs?) GetChannel index = [0]
    name_queues[index] = [1116]
    passive_lifeSpans[index] = [False]
    durable_lifeSpans[index] = [True]
    exclusive_lifeSpans[index] = [False]
    autodelete_lifeSpans[index] = [False]
    nowait は仮に false 固定
    arguments は仮に null 固定
 Enqueue(^q^) neko
Enqueue? > <---- [interrupt!] Dequeue(^q^) nekodaze
zou
tamesi34_cs.cs Dump
    name_queues         [ENQUEUE_INDEX]    =[1116]
    lifeSpan_queues     [ENQUEUE_INDEX]    =[Durable]
    durable_lifeSpans   [ENQUEUE_INDEX]    =[True]
    autodelete_lifeSpans[ENQUEUE_INDEX]    =[False]
    passive_lifeSpans   [ENQUEUE_INDEX]    =[False]
    exclusive_lifeSpans [ENQUEUE_INDEX]    =[False]
    ----
    name_queues         [DEQUEUE_INDEX]    =[1117]
    lifeSpan_queues     [DEQUEUE_INDEX]    =[Durable]
    durable_lifeSpans   [DEQUEUE_INDEX]    =[True]
    autodelete_lifeSpans[DEQUEUE_INDEX]    =[False]
    passive_lifeSpans   [DEQUEUE_INDEX]    =[False]
    exclusive_lifeSpans [DEQUEUE_INDEX]    =[False]
    ----
    name_queues         [DELETEQUEUE_INDEX]=[]
    lifeSpan_queues     [DELETEQUEUE_INDEX]=[0]
    durable_lifeSpans   [DELETEQUEUE_INDEX]=[False]
    autodelete_lifeSpans[DELETEQUEUE_INDEX]=[False]
    passive_lifeSpans   [DELETEQUEUE_INDEX]=[False]
    exclusive_lifeSpans [DELETEQUEUE_INDEX]=[False]
    ----

(tamesi34_cs.cs?) GetChannel index = [0]
    name_queues[index] = [1116]
    passive_lifeSpans[index] = [False]
    durable_lifeSpans[index] = [True]
    exclusive_lifeSpans[index] = [False]
    autodelete_lifeSpans[index] = [False]
    nowait は仮に false 固定
    arguments は仮に null 固定
 Enqueue(^q^) zou
Enqueue? > <---- [interrupt!] Dequeue(^q^) zoudaze

tamesi35a2_cs.exe

(0) name=[1117] lifeSpan=[Durable]
(1) name=[1116] lifeSpan=[Durable]
(tamesi35a2_cs.cs?) GetChannel index = [1]
    name_queues[index] = [1116]
    passive_lifeSpans[index] = [False]
    durable_lifeSpans[index] = [True]
    exclusive_lifeSpans[index] = [False]
    autodelete_lifeSpans[index] = [False]
    nowait は仮に false 固定
    arguments は仮に null 固定
(tamesi35a2_cs.cs?) GetChannel index = [0]
    name_queues[index] = [1117]
    passive_lifeSpans[index] = [False]
    durable_lifeSpans[index] = [True]
    exclusive_lifeSpans[index] = [False]
    autodelete_lifeSpans[index] = [False]
    nowait は仮に false 固定
    arguments は仮に null 固定
(tamesi35a2_cs.cs?) GetChannel index = [0]
    name_queues[index] = [1117]
    passive_lifeSpans[index] = [False]
    durable_lifeSpans[index] = [True]
    exclusive_lifeSpans[index] = [False]
    autodelete_lifeSpans[index] = [False]
    nowait は仮に false 固定
    arguments は仮に null 固定
(tamesi35a2_cs.cs?) GetChannel index = [0]
    name_queues[index] = [1117]
    passive_lifeSpans[index] = [False]
    durable_lifeSpans[index] = [True]
    exclusive_lifeSpans[index] = [False]
    autodelete_lifeSpans[index] = [False]
    nowait は仮に false 固定
    arguments は仮に null 固定

Windows10 なら動いてるのにな。

これを Ubuntu16.04 で実行すると

root@tk2-217-18401:/home/★user/shogi/csharp_service# rm tamesi34_cs.cs
root@tk2-217-18401:/home/★user/shogi/csharp_service# nano tamesi34_cs.cs
root@tk2-217-18401:/home/★user/shogi/csharp_service# rm tamesi35a2_cs.cs
root@tk2-217-18401:/home/★user/shogi/csharp_service# nano tamesi35a2_cs.cs
root@tk2-217-18401:/home/★user/shogi/csharp_service# mcs /r:RabbitMQ.Client.dll -define:UBUNTU tamesi34_cs.cs
root@tk2-217-18401:/home/★user/shogi/csharp_service# mcs /r:RabbitMQ.Client.dll -define:UBUNTU tamesi35a2_cs.cs
root@tk2-217-18401:/home/★user/shogi/csharp_service# ./tamesi35a2_cs.exe --msgqueue 1117 durable 1116 durable > ./tamesi35a2_cs.out.log 2> ./tamesi35a2_cs.err.log < /dev/null &
[1] 31008
root@tk2-217-18401:/home/★user/shogi/csharp_service# ./tamesi34_cs.exe 2> ./tamesi34_cs.err.log
削除したいキューがあれば名前を、無ければ空文字列を入れろだぜ☆(^~^)
キュー名を入力    : キューを削除します
空文字列で[Enter] : 次のステップへ進む
Name or empty ? >
エンキュー先のメッセージ・キューの名前を入れろだぜ☆(^~^)
Queue name? > 1116
エンキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆(^~^)
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが1人も接続していなかったら消す
(4) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(8) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > 1
デキュー先のメッセージ・キューの名前を入れろだぜ☆(^~^)
Queue name? > 1117
デキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆(^~^)
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが1人も接続していなかったら消す
(4) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(8) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > 1
tamesi34_cs.cs Dump
    name_queues         [ENQUEUE_INDEX]    =[1116]
    lifeSpan_queues     [ENQUEUE_INDEX]    =[Durable]
    durable_lifeSpans   [ENQUEUE_INDEX]    =[True]
    autodelete_lifeSpans[ENQUEUE_INDEX]    =[False]
    passive_lifeSpans   [ENQUEUE_INDEX]    =[False]
    exclusive_lifeSpans [ENQUEUE_INDEX]    =[False]
    ----
    name_queues         [DEQUEUE_INDEX]    =[1117]
    lifeSpan_queues     [DEQUEUE_INDEX]    =[Durable]
    durable_lifeSpans   [DEQUEUE_INDEX]    =[True]
    autodelete_lifeSpans[DEQUEUE_INDEX]    =[False]
    passive_lifeSpans   [DEQUEUE_INDEX]    =[False]
    exclusive_lifeSpans [DEQUEUE_INDEX]    =[False]
    ----
    name_queues         [DELETEQUEUE_INDEX]=[]
    lifeSpan_queues     [DELETEQUEUE_INDEX]=[0]
    durable_lifeSpans   [DELETEQUEUE_INDEX]=[False]
    autodelete_lifeSpans[DELETEQUEUE_INDEX]=[False]
    passive_lifeSpans   [DELETEQUEUE_INDEX]=[False]
    exclusive_lifeSpans [DELETEQUEUE_INDEX]=[False]
    ----

(tamesi34_cs.cs?) GetChannel index = [1]
    name_queues[index] = [1117]
    passive_lifeSpans[index] = [False]
    durable_lifeSpans[index] = [True]
    exclusive_lifeSpans[index] = [False]
    autodelete_lifeSpans[index] = [False]
    nowait は仮に false 固定
    arguments は仮に null 固定
終了するときは[Ctrl]+[C]キーを押せだぜ☆(^~^)
エンキューするときはメッセージを打ち込んで[Enter]キーを押せだぜ☆(^◇^)
Enqueue? > usi
tamesi34_cs.cs Dump
    name_queues         [ENQUEUE_INDEX]    =[1116]
    lifeSpan_queues     [ENQUEUE_INDEX]    =[Durable]
    durable_lifeSpans   [ENQUEUE_INDEX]    =[True]
    autodelete_lifeSpans[ENQUEUE_INDEX]    =[False]
    passive_lifeSpans   [ENQUEUE_INDEX]    =[False]
    exclusive_lifeSpans [ENQUEUE_INDEX]    =[False]
    ----
    name_queues         [DEQUEUE_INDEX]    =[1117]
    lifeSpan_queues     [DEQUEUE_INDEX]    =[Durable]
    durable_lifeSpans   [DEQUEUE_INDEX]    =[True]
    autodelete_lifeSpans[DEQUEUE_INDEX]    =[False]
    passive_lifeSpans   [DEQUEUE_INDEX]    =[False]
    exclusive_lifeSpans [DEQUEUE_INDEX]    =[False]
    ----
    name_queues         [DELETEQUEUE_INDEX]=[]
    lifeSpan_queues     [DELETEQUEUE_INDEX]=[0]
    durable_lifeSpans   [DELETEQUEUE_INDEX]=[False]
    autodelete_lifeSpans[DELETEQUEUE_INDEX]=[False]
    passive_lifeSpans   [DELETEQUEUE_INDEX]=[False]
    exclusive_lifeSpans [DELETEQUEUE_INDEX]=[False]
    ----

(tamesi34_cs.cs?) GetChannel index = [0]
    name_queues[index] = [1116]
    passive_lifeSpans[index] = [False]
    durable_lifeSpans[index] = [True]
    exclusive_lifeSpans[index] = [False]
    autodelete_lifeSpans[index] = [False]
    nowait は仮に false 固定
    arguments は仮に null 固定
[1]+  Exit 1                  ./tamesi35a2_cs.exe --msgqueue 1117 durable 1116 durable > ./tamesi35a2_cs.out.log 2> ./tamesi35a2_cs.err.log < /dev/null

プログラム以外の理由で落ちてるのか?

# rabbitmq-server
ERROR: node with name "rabbit" already running on "tk2-217-18401"
# nano tamesi34_cs.err.log
Unhandled Exception:
RabbitMQ.Client.Exceptions.OperationInterruptedException: The AMQP operation was interrupted: AMQP close-reason, initiated by Peer, code=406, text="PRECONDITION_FAILED - inequivalent arg 'durable' for queue '1116' in vhost '/': receiv$
  at RabbitMQ.Client.Impl.SimpleBlockingRpcContinuation.GetReply () <0x40d35fd0 + 0x00103> in <filename unknown>:0
  at RabbitMQ.Client.Impl.ModelBase.ModelRpc (RabbitMQ.Client.Impl.MethodBase method, RabbitMQ.Client.Impl.ContentHeaderBase header, System.Byte[] body) <0x40d33da0 + 0x000f9> in <filename unknown>:0
  at RabbitMQ.Client.Framing.Impl.v0_8.Model.QueueDeclare (System.String queue, Boolean passive, Boolean durable, Boolean exclusive, Boolean autoDelete, Boolean nowait, IDictionary arguments) <0x40d38f30 + 0x00113> in <filename unknow$
  at UsagiMQ.Program.GetChannel (Int32 index) <0x40cf98c0 + 0x0045c> in <filename unknown>:0
  at UsagiMQ.Program.Enqueue (System.String message) <0x40d3a030 + 0x00013> in <filename unknown>:0
  at UsagiMQ.Program.Main (System.String[] args) <0x40cf4d50 + 0x009a7> in <filename unknown>:0
[ERROR] FATAL UNHANDLED EXCEPTION: RabbitMQ.Client.Exceptions.OperationInterruptedException: The AMQP operation was interrupted: AMQP close-reason, initiated by Peer, code=406, text="PRECONDITION_FAILED - inequivalent arg 'durable' fo$
  at RabbitMQ.Client.Impl.SimpleBlockingRpcContinuation.GetReply () <0x40d35fd0 + 0x00103> in <filename unknown>:0
  at RabbitMQ.Client.Impl.ModelBase.ModelRpc (RabbitMQ.Client.Impl.MethodBase method, RabbitMQ.Client.Impl.ContentHeaderBase header, System.Byte[] body) <0x40d33da0 + 0x000f9> in <filename unknown>:0
  at RabbitMQ.Client.Framing.Impl.v0_8.Model.QueueDeclare (System.String queue, Boolean passive, Boolean durable, Boolean exclusive, Boolean autoDelete, Boolean nowait, IDictionary arguments) <0x40d38f30 + 0x00113> in <filename unknow$
  at UsagiMQ.Program.GetChannel (Int32 index) <0x40cf98c0 + 0x0045c> in <filename unknown>:0
  at UsagiMQ.Program.Enqueue (System.String message) <0x40d3a030 + 0x00013> in <filename unknown>:0
  at UsagiMQ.Program.Main (System.String[] args) <0x40cf4d50 + 0x009a7> in <filename unknown>:0

# nano tamesi35a2_cs.err.log
(tamesi35a2_cs.cs?) GetChannel index = [1]
    name_queues[index] = [1116]
    passive_lifeSpans[index] = [False]
    durable_lifeSpans[index] = [True]
    exclusive_lifeSpans[index] = [False]
    autodelete_lifeSpans[index] = [False]
    nowait は仮に false 固定
    arguments は仮に null 固定

Unhandled Exception:
RabbitMQ.Client.Exceptions.OperationInterruptedException: The AMQP operation was interrupted: AMQP close-reason, initiated by Peer, code=406, text="PRECONDITION_FAILED - inequivalent arg 'durable' for queue '1116' in vhost '/': received 'true' but current is 'f$
  at RabbitMQ.Client.Impl.SimpleBlockingRpcContinuation.GetReply () <0x41f435b0 + 0x00107> in <filename unknown>:0
  at RabbitMQ.Client.Impl.ModelBase.ModelRpc (RabbitMQ.Client.Impl.MethodBase method, RabbitMQ.Client.Impl.ContentHeaderBase header, System.Byte[] body) <0x41f413f0 + 0x00101> in <filename unknown>:0
  at RabbitMQ.Client.Framing.Impl.v0_8.Model.QueueDeclare (System.String queue, Boolean passive, Boolean durable, Boolean exclusive, Boolean autoDelete, Boolean nowait, IDictionary arguments) <0x41f45790 + 0x00113> in <filename unknown>:0
  at Program.GetChannel (Int32 index) <0x41f05890 + 0x00436> in <filename unknown>:0
  at Program.StartConsume () <0x41f057d0 + 0x0000f> in <filename unknown>:0
  at Program.Main (System.String[] args) <0x41f03270 + 0x0037b> in <filename unknown>:0
[ERROR] FATAL UNHANDLED EXCEPTION: RabbitMQ.Client.Exceptions.OperationInterruptedException: The AMQP operation was interrupted: AMQP close-reason, initiated by Peer, code=406, text="PRECONDITION_FAILED - inequivalent arg 'durable' for queue '1116' in vhost '/'$
  at RabbitMQ.Client.Impl.SimpleBlockingRpcContinuation.GetReply () <0x41f435b0 + 0x00107> in <filename unknown>:0
  at RabbitMQ.Client.Impl.ModelBase.ModelRpc (RabbitMQ.Client.Impl.MethodBase method, RabbitMQ.Client.Impl.ContentHeaderBase header, System.Byte[] body) <0x41f413f0 + 0x00101> in <filename unknown>:0
  at RabbitMQ.Client.Framing.Impl.v0_8.Model.QueueDeclare (System.String queue, Boolean passive, Boolean durable, Boolean exclusive, Boolean autoDelete, Boolean nowait, IDictionary arguments) <0x41f45790 + 0x00113> in <filename unknown>:0
  at Program.GetChannel (Int32 index) <0x41f05890 + 0x00436> in <filename unknown>:0
  at Program.StartConsume () <0x41f057d0 + 0x0000f> in <filename unknown>:0
  at Program.Main (System.String[] args) <0x41f03270 + 0x0037b> in <filename unknown>:0
# nano tamesi35a2_cs.out.log
(0) name=[1117] lifeSpan=[Durable]
(1) name=[1116] lifeSpan=[Durable]

うーむ、

RabbitMQ.Client.Framing.Impl.v0_8.Model.QueueDeclare (System.String queue, Boolean passive, Boolean durable, Boolean exclusive, Boolean autoDelete, Boolean nowait, IDictionary arguments)

この引数の順序はあってるんだろうか?

順序はあってるし、どうも UBUNTU だと false と言ってくる。 UBUNTU のときだけ全部 false にしてみるか?

UBUNTUのときだけ引数をfalse にするとどうか?

root@tk2-217-18401:/home/★user/shogi/csharp_service# ./tamesi35a2_cs.exe --msgqueue 1117 durable 1116 durable > ./tamesi35a2_cs.out.log 2> ./tamesi35a2_cs.err.log < /dev/null &
[3] 31371
root@tk2-217-18401:/home/★user/shogi/csharp_service# ./tamesi34_cs.exe 2> ./tamesi34_cs.err.log
削除したいキューがあれば名前を、無ければ空文字列を入れろだぜ☆(^~^)
キュー名を入力    : キューを削除します
空文字列で[Enter] : 次のステップへ進む
Name or empty ? >
エンキュー先のメッセージ・キューの名前を入れろだぜ☆(^~^)
Queue name? > 1116
エンキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆(^~^)
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが1人も接続していなかったら消す
(4) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(8) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > 1
デキュー先のメッセージ・キューの名前を入れろだぜ☆(^~^)
Queue name? > 1117
デキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆(^~^)
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが1人も接続していなかったら消す
(4) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(8) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > 1
tamesi34_cs.cs Dump
    name_queues         [ENQUEUE_INDEX]    =[1116]
    lifeSpan_queues     [ENQUEUE_INDEX]    =[Durable]
    durable_lifeSpans   [ENQUEUE_INDEX]    =[True]
    autodelete_lifeSpans[ENQUEUE_INDEX]    =[False]
    passive_lifeSpans   [ENQUEUE_INDEX]    =[False]
    exclusive_lifeSpans [ENQUEUE_INDEX]    =[False]
    ----
    name_queues         [DEQUEUE_INDEX]    =[1117]
    lifeSpan_queues     [DEQUEUE_INDEX]    =[Durable]
    durable_lifeSpans   [DEQUEUE_INDEX]    =[True]
    autodelete_lifeSpans[DEQUEUE_INDEX]    =[False]
    passive_lifeSpans   [DEQUEUE_INDEX]    =[False]
    exclusive_lifeSpans [DEQUEUE_INDEX]    =[False]
    ----
    name_queues         [DELETEQUEUE_INDEX]=[]
    lifeSpan_queues     [DELETEQUEUE_INDEX]=[0]
    durable_lifeSpans   [DELETEQUEUE_INDEX]=[False]
    autodelete_lifeSpans[DELETEQUEUE_INDEX]=[False]
    passive_lifeSpans   [DELETEQUEUE_INDEX]=[False]
    exclusive_lifeSpans [DELETEQUEUE_INDEX]=[False]
    ----

(tamesi34_cs.cs?) GetChannel index = [1]
    name_queues[index] = [1117]
    passive_lifeSpans[index] = [False]
    durable_lifeSpans[index] = [True]
    exclusive_lifeSpans[index] = [False]
    autodelete_lifeSpans[index] = [False]
    nowait は仮に false 固定
    arguments は仮に null 固定
終了するときは[Ctrl]+[C]キーを押せだぜ☆(^~^)
エンキューするときはメッセージを打ち込んで[Enter]キーを押せだぜ☆(^◇^)
Enqueue? > usi
tamesi34_cs.cs Dump
    name_queues         [ENQUEUE_INDEX]    =[1116]
    lifeSpan_queues     [ENQUEUE_INDEX]    =[Durable]
    durable_lifeSpans   [ENQUEUE_INDEX]    =[True]
    autodelete_lifeSpans[ENQUEUE_INDEX]    =[False]
    passive_lifeSpans   [ENQUEUE_INDEX]    =[False]
    exclusive_lifeSpans [ENQUEUE_INDEX]    =[False]
    ----
    name_queues         [DEQUEUE_INDEX]    =[1117]
    lifeSpan_queues     [DEQUEUE_INDEX]    =[Durable]
    durable_lifeSpans   [DEQUEUE_INDEX]    =[True]
    autodelete_lifeSpans[DEQUEUE_INDEX]    =[False]
    passive_lifeSpans   [DEQUEUE_INDEX]    =[False]
    exclusive_lifeSpans [DEQUEUE_INDEX]    =[False]
    ----
    name_queues         [DELETEQUEUE_INDEX]=[]
    lifeSpan_queues     [DELETEQUEUE_INDEX]=[0]
    durable_lifeSpans   [DELETEQUEUE_INDEX]=[False]
    autodelete_lifeSpans[DELETEQUEUE_INDEX]=[False]
    passive_lifeSpans   [DELETEQUEUE_INDEX]=[False]
    exclusive_lifeSpans [DELETEQUEUE_INDEX]=[False]
    ----

(tamesi34_cs.cs?) GetChannel index = [0]
    name_queues[index] = [1116]
    passive_lifeSpans[index] = [False]
    durable_lifeSpans[index] = [True]
    exclusive_lifeSpans[index] = [False]
    autodelete_lifeSpans[index] = [False]
    nowait は仮に false 固定
    arguments は仮に null 固定
[3]   Exit 1                  ./tamesi35a2_cs.exe --msgqueue 1117 durable 1116 durable > ./tamesi35a2_cs.out.log 2> ./tamesi35a2_cs.err.log < /dev/null

落ちているようだ。

(tamesi35a2_cs.cs?) GetChannel index = [1]
    name_queues[index] = [1116]
    passive_lifeSpans[index] = [False]
    durable_lifeSpans[index] = [True]
    exclusive_lifeSpans[index] = [False]
    autodelete_lifeSpans[index] = [False]
    nowait は仮に false 固定
    arguments は仮に null 固定

Unhandled Exception:
RabbitMQ.Client.Exceptions.OperationInterruptedException: The AMQP operation was interrupted: AMQP close-reason, initiated by Peer, code=406, text="PRECONDITION_FAILED - inequivalent arg 'durable' for queue '1116' in vhost '/': re$
  at RabbitMQ.Client.Impl.SimpleBlockingRpcContinuation.GetReply () <0x403416a0 + 0x00107> in <filename unknown>:0
  at RabbitMQ.Client.Impl.ModelBase.ModelRpc (RabbitMQ.Client.Impl.MethodBase method, RabbitMQ.Client.Impl.ContentHeaderBase header, System.Byte[] body) <0x4033f520 + 0x000fd> in <filename unknown>:0
  at RabbitMQ.Client.Framing.Impl.v0_8.Model.QueueDeclare (System.String queue, Boolean passive, Boolean durable, Boolean exclusive, Boolean autoDelete, Boolean nowait, IDictionary arguments) <0x40343880 + 0x00113> in <filename un$
  at Program.GetChannel (Int32 index) <0x40303890 + 0x00436> in <filename unknown>:0
  at Program.StartConsume () <0x403037d0 + 0x0000f> in <filename unknown>:0
  at Program.Main (System.String[] args) <0x40301270 + 0x0037b> in <filename unknown>:0
[ERROR] FATAL UNHANDLED EXCEPTION: RabbitMQ.Client.Exceptions.OperationInterruptedException: The AMQP operation was interrupted: AMQP close-reason, initiated by Peer, code=406, text="PRECONDITION_FAILED - inequivalent arg 'durable$
  at RabbitMQ.Client.Impl.SimpleBlockingRpcContinuation.GetReply () <0x403416a0 + 0x00107> in <filename unknown>:0
  at RabbitMQ.Client.Impl.ModelBase.ModelRpc (RabbitMQ.Client.Impl.MethodBase method, RabbitMQ.Client.Impl.ContentHeaderBase header, System.Byte[] body) <0x4033f520 + 0x000fd> in <filename unknown>:0
  at RabbitMQ.Client.Framing.Impl.v0_8.Model.QueueDeclare (System.String queue, Boolean passive, Boolean durable, Boolean exclusive, Boolean autoDelete, Boolean nowait, IDictionary arguments) <0x40343880 + 0x00113> in <filename un$
  at Program.GetChannel (Int32 index) <0x40303890 + 0x00436> in <filename unknown>:0
  at Program.StartConsume () <0x403037d0 + 0x0000f> in <filename unknown>:0
  at Program.Main (System.String[] args) <0x40301270 + 0x0037b> in <filename unknown>:0
RabbitMQ.Client.Exceptions.OperationInterruptedException

というのは、どういうエラーなのか?

(public class OperationInterruptedException)「www.rabbitmq.com」
https://www.rabbitmq.com/releases/rabbitmq-dotnet-client/v2.8.6/rabbitmq-dotnet-client-2.8.6-client-htmldoc/html/type-RabbitMQ.Client.Exceptions.OperationInterruptedException.html

Ubuntu16.04 だと、TCP接続がなぜ切れるのか?

Console.WriteLine( "("+i+") name=["+ name_queues[i] + "] lifeSpan=["+ lifeSpan_queues[i] + "]");

こういうテスト用の標準出力を全部 コメントアウトしてみよう。

終了するときは[Ctrl]+[C]キーを押せだぜ☆(^~^)
エンキューするときはメッセージを打ち込んで[Enter]キーを押せだぜ☆(^◇^)
Enqueue? > usi
tamesi34_cs.cs Dump
    name_queues         [ENQUEUE_INDEX]    =[1116]
    lifeSpan_queues     [ENQUEUE_INDEX]    =[Durable]
    durable_lifeSpans   [ENQUEUE_INDEX]    =[True]
    autodelete_lifeSpans[ENQUEUE_INDEX]    =[False]
    passive_lifeSpans   [ENQUEUE_INDEX]    =[False]
    exclusive_lifeSpans [ENQUEUE_INDEX]    =[False]
    ----
    name_queues         [DEQUEUE_INDEX]    =[1117]
    lifeSpan_queues     [DEQUEUE_INDEX]    =[Durable]
    durable_lifeSpans   [DEQUEUE_INDEX]    =[True]
    autodelete_lifeSpans[DEQUEUE_INDEX]    =[False]
    passive_lifeSpans   [DEQUEUE_INDEX]    =[False]
    exclusive_lifeSpans [DEQUEUE_INDEX]    =[False]
    ----
    name_queues         [DELETEQUEUE_INDEX]=[]
    lifeSpan_queues     [DELETEQUEUE_INDEX]=[0]
    durable_lifeSpans   [DELETEQUEUE_INDEX]=[False]
    autodelete_lifeSpans[DELETEQUEUE_INDEX]=[False]
    passive_lifeSpans   [DELETEQUEUE_INDEX]=[False]
    exclusive_lifeSpans [DELETEQUEUE_INDEX]=[False]
    ----

(tamesi34_cs.cs?) GetChannel index = [0]
    name_queues[index] = [1116]
    passive_lifeSpans[index] = [False]
    durable_lifeSpans[index] = [True]
    exclusive_lifeSpans[index] = [False]
    autodelete_lifeSpans[index] = [False]
    nowait は仮に false 固定
    arguments は仮に null 固定
[3]   Exit 1                  ./tamesi35a2_cs.exe --msgqueue 1117 durable 1116 durable > ./tamesi35a2_cs.out.log 2> ./tamesi35a2_cs.err.log < /dev/null

それでもダメ。

received 'true' but current is 'false'", classId=50, methodId=10, cause=
received 'true' but current is 'false'", classId=50, methodId=10, cause=

うーむ。

ググろう

「PRECONDITION_FAILED - inequivalent arg 'durable' for queue #370」(MassTransit/MassTransit)
https://github.com/MassTransit/MassTransit/issues/370

「Issue in establishing connection with Rabbit MQ」(stack overflow)
http://stackoverflow.com/questions/31762563/issue-in-establishing-connection-with-rabbit-mq

「Correct config using rabbitmq as celery backend」(stack overflow)
http://stackoverflow.com/questions/27623940/correct-config-using-rabbitmq-as-celery-backend

複数指定したい

--msgqueue 1117 durable 1116 durable

これを、

--enqueue durable autodelete --dequeue durable autodelete

みたいに、複数指定できるようにしたい。

こんな感じだろうか。

//--------------------------------------------------------------------------------
// tamesi35a2_cs.cs
//
// メッセージの末尾に daze を付けます。
// 標準入出力は、キーボード、画面には接続しないようにします。
// キューの名前はコマンドライン引数で指定してください。
// デキューは割込みを受け付けます。
//--------------------------------------------------------------------------------
// OS  : Windows 10
// IDE : Visual Studio 2015
//       Install : NuGet   : Install-Package RabbitMQ.Client -Version 4.1.1
//       Execute : Command : ./tamesi35a2_cs.exe --enqueue 1117 durable --dequeue 1116 durable
//
// OS  : Ubuntu 16.04
//       Compile : Command : mcs /r:RabbitMQ.Client.dll -define:UBUNTU tamesi35a2_cs.cs
//       Execute : Command : // バックグラウンドで実行する
//                         : nohup ./tamesi35a2_cs.exe --enqueue 1117 durable autodelete --dequeue 1116 durable autodelete > ./tamesi35a2_cs.out.log 2> ./tamesi35a2_cs.err.log < /dev/null &
//       Check   :         : // tamesi33a1_cs.exe を使う
//--------------------------------------------------------------------------------

// Ubuntu の RabbitMQ はソースのバージョンが古いのか、API が異なった。
// #define UBUNTU

using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Text;
using System.Threading;

/// <summary>
/// このプログラム内だけで使われる数字。AMQP-CPPでの実装とは異なる。
/// AMQP-CPPでの実装 : AMQP::durable=[1] AMQP::autodelete=[2] AMQP::passive=[8] AMQP::exclusive=[512]
/// </summary>
[Flags]
enum LifeSpanType
{
    /// <summary>
    /// RabbitMQが止まってもキューを残す
    /// </summary>
    Durable = 1,
    /// <summary>
    /// コンシューマーが1人も接続していなかったら消す
    /// </summary>
    Autodelete = 0x1 << 1,
    /// <summary>
    /// キューが存在するかどうかチェックするだけ。中身見ない時これ
    /// </summary>
    Passive = 0x1 << 2,
    /// <summary>
    /// この接続でだけ使える。この接続が切れたら消す
    /// </summary>
    Exclusive = 0x1 << 3
}

/// <summary>
/// 参照 : QueueDeclare http://docs.spring.io/spring-amqp-net/docs/1.0.x/api/html/Spring.Messaging.Amqp.Rabbit~Spring.Messaging.Amqp.Rabbit.Connection.CachedModel~QueueDeclare(String,Boolean,Boolean,Boolean,Boolean,Boolean,IDictionary).html
/// 参照 : EventingBasicConsumer https://www.rabbitmq.com/releases/rabbitmq-dotnet-client/v1.4.0/rabbitmq-dotnet-client-1.4.0-net-2.0-htmldoc/type-RabbitMQ.Client.Events.EventingBasicConsumer.html
/// 参照 : BasicConsume https://www.rabbitmq.com/releases/rabbitmq-dotnet-client/v1.4.0/rabbitmq-dotnet-client-1.4.0-net-2.0-htmldoc/type-RabbitMQ.Client.IModel.html#method-M:RabbitMQ.Client.IModel.BasicConsume(System.UInt16,System.String,System.Boolean,System.Collections.IDictionary,RabbitMQ.Client.IBasicConsumer)
/// 参照 : C#でconstな配列を実現する (もっとクールにプログラミング) http://pgnote.net/?p=885
/// </summary>
class Program
{
    const int ENQUEUE_INDEX = 0;
    const int DEQUEUE_INDEX = 1;
    const int NUM_INDEX = 2;
    const string HOST_NAME = "localhost";
    static string[] name_queues = new string[NUM_INDEX];
    /// <summary>
    /// キューの寿命(複合可能)
    /// durable    : RabbitMQが止まってもキューを残す
    /// autodelete : コンシューマーが1人も接続していなかったら消す
    /// passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
    /// exclusive  : この接続でだけ使える。この接続が切れたら消す
    /// </summary>
    static LifeSpanType[] lifeSpan_queues = new LifeSpanType[NUM_INDEX];
    static LifeSpanType LifeSpanString_To_Enum(string lifeSpan)
    {
        switch (lifeSpan)
        {
            case "durable": return LifeSpanType.Durable;
            case "autodelete": return LifeSpanType.Autodelete;
            case "passive": return LifeSpanType.Passive;
            case "exclusive": return LifeSpanType.Exclusive;
            default: throw new Exception("未対応のlifeSpan [" + lifeSpan + "]");
        }
    }
    static bool[]
        durable_lifeSpans = new bool[NUM_INDEX],
        autodelete_lifeSpans = new bool[NUM_INDEX],
        passive_lifeSpans = new bool[NUM_INDEX],
        exclusive_lifeSpans = new bool[NUM_INDEX];
    static void SetLifeSpan(int index_queue, string name_queue, LifeSpanType lifeSpan)
    {
        // 一旦クリアー
        durable_lifeSpans[index_queue] = false;
        autodelete_lifeSpans[index_queue] = false;
        passive_lifeSpans[index_queue] = false;
        exclusive_lifeSpans[index_queue] = false;

        // durable
        if (((int)lifeSpan & (int)LifeSpanType.Durable) == (int)LifeSpanType.Durable) { durable_lifeSpans[index_queue] = true; }
        // autodelete
        if (((int)lifeSpan & (int)LifeSpanType.Autodelete) == (int)LifeSpanType.Autodelete) { autodelete_lifeSpans[index_queue] = true; }
        // passive
        if (((int)lifeSpan & (int)LifeSpanType.Passive) == (int)LifeSpanType.Passive) { passive_lifeSpans[index_queue] = true; }
        // exclusive
        if (((int)lifeSpan & (int)LifeSpanType.Exclusive) == (int)LifeSpanType.Exclusive) { exclusive_lifeSpans[index_queue] = true; }
    }

    // 回転式バッファー
    static RotationBuffer rollingBuffer = new RotationBuffer();

    static ConnectionFactory GetFactory()
    {
        if (null == m_factory_)
        {
            m_factory_ = new ConnectionFactory() { HostName = HOST_NAME };
        }
        return m_factory_;
    }
    static ConnectionFactory m_factory_;

    static IConnection GetConnection()
    {
        if (null == m_connection_)
        {
            m_connection_ = GetFactory().CreateConnection();
        }
        return m_connection_;
    }
    static IConnection m_connection_;

    static bool IsConnected()
    {
        if (null != m_connection_)
        {
            return m_connection_.IsOpen;
        }
        return false;
    }

    static IModel GetChannel(int index)
    {
        if (null == m_channels_[index])
        {
            m_channels_[index] = GetConnection().CreateModel();

            // 引数が7つの QueueDeclare でエラーを吐くことがあるので、情報を出力しておく。
            Console.Error.WriteLine("(tamesi35a2_cs.cs?) GetChannel index = [" + index + "]");
            Console.Error.WriteLine("    name_queues[index] = [" + name_queues[index] + "]");
            Console.Error.WriteLine("    passive_lifeSpans[index] = [" + passive_lifeSpans[index] + "]");
            Console.Error.WriteLine("    durable_lifeSpans[index] = [" + durable_lifeSpans[index] + "]");
            Console.Error.WriteLine("    exclusive_lifeSpans[index] = [" + exclusive_lifeSpans[index] + "]");
            Console.Error.WriteLine("    autodelete_lifeSpans[index] = [" + autodelete_lifeSpans[index] + "]");
            Console.Error.WriteLine("    nowait は仮に false 固定");
            Console.Error.WriteLine("    arguments は仮に null 固定");

#if UBUNTU
            // Ubuntuでは何故か Spring.Messaging.Amqp.Rabbit の引数 7 つのやつになっている。
            // m_channels_[index].QueueDeclare(name_queues[index], false, false, false, false, false, null);
            // そして false になっているようだ。
            m_channels_[index].QueueDeclare(name_queues[index], passive_lifeSpans[index], durable_lifeSpans[index], exclusive_lifeSpans[index], autodelete_lifeSpans[index], false, null);
            //m_channels_[index].QueueDeclare(name_queues[index], false, false, false, false, false, null);
#else
            //m_channels_[index].QueueDeclare(name_queues[index], false, false, false, null);
            m_channels_[index].QueueDeclare(name_queues[index], durable_lifeSpans[index], exclusive_lifeSpans[index], autodelete_lifeSpans[index], null);
#endif
        }
        return m_channels_[index];
    }
    static IModel[] m_channels_ = new IModel[2];

    static EventingBasicConsumer GetConsumer(int index)
    {
        if (null == m_consumers_[index])
        {
#if UBUNTU
            // Ubuntuでは何故か v1.4.0 の引数が 0 個のやつになっている。調べたが引数が1個~6個のものは無かった。
            m_consumers_[index] = new EventingBasicConsumer();
#else
            m_consumers_[index] = new EventingBasicConsumer(GetChannel(index));
#endif

        }
        return m_consumers_[index];
    }
    static EventingBasicConsumer[] m_consumers_ = new EventingBasicConsumer[2];

    /// <summary>
    /// 受信できたときに割り込んでくる処理
    /// </summary>
#if UBUNTU
    public static BasicDeliverEventHandler GetDequeueHandler()
#else
    static EventHandler<BasicDeliverEventArgs> GetDequeueHandler()
#endif
    {
        if (null == m_dequeueHandler_)
        {
#if UBUNTU
            m_dequeueHandler_ = new BasicDeliverEventHandler((model, ea) =>
#else
            m_dequeueHandler_ = new EventHandler<BasicDeliverEventArgs>((model, ea) =>
#endif
            {
                byte[] body = ea.Body;
                string message = Encoding.UTF8.GetString(body);

                // 回転式バッファーに入れる
                rollingBuffer.PutMessage(message);
            });
        }

        return m_dequeueHandler_;
    }

#if UBUNTU
    static BasicDeliverEventHandler m_dequeueHandler_;
#else
    static EventHandler<BasicDeliverEventArgs> m_dequeueHandler_;
#endif

    /// <summary>
    /// 対応するオープンは無いけれど、開けたら閉める、を完璧に対応する必要がある。
    /// </summary>
    static void CloseChannel(int index)
    {
        if (null != m_channels_[index])
        {
            m_channels_[index].Close();
            m_channels_[index] = null;
        }
    }

    static void Enqueue(string message)
    {
        // 末尾に daze を付ける。
        message += "daze";

        IModel channel = GetChannel(ENQUEUE_INDEX);
        byte[] body = Encoding.UTF8.GetBytes(message);
        channel.BasicPublish("", name_queues[ENQUEUE_INDEX], null, body);

        // 対応するオープンは無いが、ちゃんと閉じないと、レシーブしてくれない。
        CloseChannel(ENQUEUE_INDEX);
    }

    static void StartConsume()
    {
        IModel channel = GetChannel(DEQUEUE_INDEX);
        EventingBasicConsumer consumer = GetConsumer(DEQUEUE_INDEX);
        // 受信できたときに割り込んでくる処理
        consumer.Received += GetDequeueHandler();

#if UBUNTU
        // Ubuntuでは何故か引数が 5 個のやつになっている。
        channel.BasicConsume( name_queues[DEQUEUE_INDEX], true, "", null, consumer);
#else
        channel.BasicConsume( name_queues[DEQUEUE_INDEX], true, consumer);
#endif

        // 終了はさせない
        // consumer.Received -= GetReceiveHandler();
        // CloseChannel(DEQUEUE_INDEX);
    }

    static void StartProduce()
    {
        // 無限ループ
        while (IsConnected())
        {
            string message = rollingBuffer.GetMessage();
            if (null != message)
            {
                // エンキューする
                Enqueue(message);
            }
            Thread.Sleep(20);
        }
    }

    static void Main(string[] args)
    {
        if (args.Length<1)
        {
            goto gt_Error1;
        }

        // 引数の解析
        {
            // 与件
            // 「--enqueue 1117 durable autodelete --dequeue 1116 durable autodelete」
            // 寿命は可変個数設定可能「durable」「autodelete」「passive」「exclusive」

            // 受け皿
            // 0:enqueue 1:dequeue

            // 記憶
            int m0 = 0; // 0:ニュートラル 1:enqueue 2:dequeue
            int m1 = 0; // 0:寿命カーソル

            // 解析器
            foreach (string a in args)
            {
                if (a.StartsWith("--"))
                {
                    switch (a)
                    {
                        case "--enqueue": m0=1; m1=0; break;
                        case "--dequeue": m0=2; m1=0; break;
                        default: goto gt_Error1;
                    }
                }
                else if (0 == m1) { name_queues[m0 - 1] = a; m1++; }
                else { lifeSpan_queues[m0 - 1] |= LifeSpanString_To_Enum(a); m1++; }
            }

            // 結果
            for (int i = 0; i < 2; i++)
            {
                Console.WriteLine( "("+i+") name=["+ name_queues[i] + "] lifeSpan=["+ lifeSpan_queues[i] + "]");
                SetLifeSpan(i, name_queues[i], lifeSpan_queues[i]);
            }
        }

        // 常時監視をスタート
        StartConsume();

        StartProduce();

        // このプログラムは、自分では接続を切らない。
        return;

        gt_Error1:
            throw new Exception(@"コマンドライン引数に「--msgqueue キュー名 寿命 キュー名 寿命」を指定しろだぜ☆(^~^)
エンキュー先、デキュー先の順番な☆(^▽^)
寿命は durable, autodelete, passive, exclusive のいずれかで、
複合指定にはまだ対応していないぜ☆(>_<)");
    }
}

/// <summary>
/// 回転式バッファー。
/// これはメイン・スレッドに置く。
/// デキューのスレッドでエンキューすることはできない。
/// デキュー処理は、回転式バッファーを仲介にしてエンキュー処理にメッセージを渡す。
/// </summary>
public class RotationBuffer
{
    public const int bufferSize = 100;
    public string[] buffer = new string[bufferSize];
    public int[] bufferCursors = new int[] { 0, 0 };
    public const int PUT_INDEX = 0;
    public const int GET_INDEX = 1;
    public void PutMessage(string message)
    {
        buffer[bufferCursors[PUT_INDEX]] = message;
        bufferCursors[PUT_INDEX]++;
        if (!(bufferCursors[PUT_INDEX] < bufferSize))
        {
            bufferCursors[PUT_INDEX] = 0;
        }
    }
    public string GetMessage()
    {
        if (null != buffer[bufferCursors[GET_INDEX]])
        {
            string message = buffer[bufferCursors[GET_INDEX]];
            buffer[bufferCursors[GET_INDEX]] = null;
            bufferCursors[GET_INDEX]++;
            if (!(bufferCursors[GET_INDEX] < bufferSize))
            {
                bufferCursors[GET_INDEX] = 0;
            }
            return message;
        }
        return null;
    }
}
root@tk2-217-18401:/home/★user/shogi/csharp_service# rm tamesi35a2_cs.cs
root@tk2-217-18401:/home/★user/shogi/csharp_service# nano tamesi35a2_cs.cs
root@tk2-217-18401:/home/★user/shogi/csharp_service# mcs /r:RabbitMQ.Client.dll -define:UBUNTU tamesi35a2_cs.cs
root@tk2-217-18401:/home/★user/shogi/csharp_service# ./tamesi35a2_cs.exe --enqueue 1117 durable autodelete --dequeue 1116 durable autodelete > ./tamesi35a2_cs.out.log 2> ./tamesi35a2_cs.err.log < /dev/null &
[4] 470
root@tk2-217-18401:/home/★user/shogi/csharp_service# ./tamesi34_cs.exe 2> ./tamesi34_cs.err.log
削除したいキューがあれば名前を、無ければ空文字列を入れろだぜ☆(^~^)
キュー名を入力    : キューを削除します
空文字列で[Enter] : 次のステップへ進む
Name or empty ? >
エンキュー先のメッセージ・キューの名前を入れろだぜ☆(^~^)
Queue name? > 1116
エンキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆(^~^)
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが1人も接続していなかったら消す
(4) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(8) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > 3
デキュー先のメッセージ・キューの名前を入れろだぜ☆(^~^)
Queue name? > 1117
デキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆(^~^)
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが1人も接続していなかったら消す
(4) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(8) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > 3
tamesi34_cs.cs Dump
    name_queues         [ENQUEUE_INDEX]    =[1116]
    lifeSpan_queues     [ENQUEUE_INDEX]    =[Durable, Autodelete]
    durable_lifeSpans   [ENQUEUE_INDEX]    =[True]
    autodelete_lifeSpans[ENQUEUE_INDEX]    =[True]
    passive_lifeSpans   [ENQUEUE_INDEX]    =[False]
    exclusive_lifeSpans [ENQUEUE_INDEX]    =[False]
    ----
    name_queues         [DEQUEUE_INDEX]    =[1117]
    lifeSpan_queues     [DEQUEUE_INDEX]    =[Durable, Autodelete]
    durable_lifeSpans   [DEQUEUE_INDEX]    =[True]
    autodelete_lifeSpans[DEQUEUE_INDEX]    =[True]
    passive_lifeSpans   [DEQUEUE_INDEX]    =[False]
    exclusive_lifeSpans [DEQUEUE_INDEX]    =[False]
    ----
    name_queues         [DELETEQUEUE_INDEX]=[]
    lifeSpan_queues     [DELETEQUEUE_INDEX]=[0]
    durable_lifeSpans   [DELETEQUEUE_INDEX]=[False]
    autodelete_lifeSpans[DELETEQUEUE_INDEX]=[False]
    passive_lifeSpans   [DELETEQUEUE_INDEX]=[False]
    exclusive_lifeSpans [DELETEQUEUE_INDEX]=[False]
    ----

(tamesi34_cs.cs?) GetChannel index = [1]
    name_queues[index] = [1117]
    passive_lifeSpans[index] = [False]
    durable_lifeSpans[index] = [True]
    exclusive_lifeSpans[index] = [False]
    autodelete_lifeSpans[index] = [True]
    nowait は仮に false 固定
    arguments は仮に null 固定
[4]   Exit 1                  ./tamesi35a2_cs.exe --enqueue 1117 durable autodelete --dequeue 1116 durable autodelete > ./tamesi35a2_cs.out.log 2> ./tamesi35a2_cs.err.log < /dev/null
(tamesi35a2_cs.cs?) GetChannel index = [1]
    name_queues[index] = [1116]
    passive_lifeSpans[index] = [False]
    durable_lifeSpans[index] = [True]
    exclusive_lifeSpans[index] = [False]
    autodelete_lifeSpans[index] = [True]
    nowait は仮に false 固定
    arguments は仮に null 固定

Unhandled Exception:
RabbitMQ.Client.Exceptions.OperationInterruptedException: The AMQP operation was interrupted: AMQP close-reason, initiated by Peer, code=406, text="PRECONDITION_FAILED - inequivalent arg 'durable' for queue '1116' in vhost '/': recei$
  at RabbitMQ.Client.Impl.SimpleBlockingRpcContinuation.GetReply () <0x40044590 + 0x00103> in <filename unknown>:0
  at RabbitMQ.Client.Impl.ModelBase.ModelRpc (RabbitMQ.Client.Impl.MethodBase method, RabbitMQ.Client.Impl.ContentHeaderBase header, System.Byte[] body) <0x40042520 + 0x000f9> in <filename unknown>:0
  at RabbitMQ.Client.Framing.Impl.v0_8.Model.QueueDeclare (System.String queue, Boolean passive, Boolean durable, Boolean exclusive, Boolean autoDelete, Boolean nowait, IDictionary arguments) <0x400467d0 + 0x00113> in <filename unkno$
  at Program.GetChannel (Int32 index) <0x400078d0 + 0x00436> in <filename unknown>:0
  at Program.StartConsume () <0x40007810 + 0x0000f> in <filename unknown>:0
  at Program.Main (System.String[] args) <0x40004270 + 0x003cb> in <filename unknown>:0
[ERROR] FATAL UNHANDLED EXCEPTION: RabbitMQ.Client.Exceptions.OperationInterruptedException: The AMQP operation was interrupted: AMQP close-reason, initiated by Peer, code=406, text="PRECONDITION_FAILED - inequivalent arg 'durable' f$
  at RabbitMQ.Client.Impl.SimpleBlockingRpcContinuation.GetReply () <0x40044590 + 0x00103> in <filename unknown>:0
  at RabbitMQ.Client.Impl.ModelBase.ModelRpc (RabbitMQ.Client.Impl.MethodBase method, RabbitMQ.Client.Impl.ContentHeaderBase header, System.Byte[] body) <0x40042520 + 0x000f9> in <filename unknown>:0
  at RabbitMQ.Client.Framing.Impl.v0_8.Model.QueueDeclare (System.String queue, Boolean passive, Boolean durable, Boolean exclusive, Boolean autoDelete, Boolean nowait, IDictionary arguments) <0x400467d0 + 0x00113> in <filename unkno$
  at Program.GetChannel (Int32 index) <0x400078d0 + 0x00436> in <filename unknown>:0
  at Program.StartConsume () <0x40007810 + 0x0000f> in <filename unknown>:0
  at Program.Main (System.String[] args) <0x40004270 + 0x003cb> in <filename unknown>:0

うーむ。

root@tk2-217-18401:/home/csg10/shogi/csharp_service# ./tamesi34_cs.exe 2> ./tamesi34_cs.err.log
削除したいキューがあれば名前を、無ければ空文字列を入れろだぜ☆(^~^)
キュー名を入力    : キューを削除します
空文字列で[Enter] : 次のステップへ進む
Name or empty ? >
エンキュー先のメッセージ・キューの名前を入れろだぜ☆(^~^)
Queue name? > 1116
エンキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆(^~^)
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが1人も接続していなかったら消す
(4) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(8) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > 3
デキュー先のメッセージ・キューの名前を入れろだぜ☆(^~^)
Queue name? > 1117
デキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆(^~^)
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが1人も接続していなかったら消す
(4) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(8) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > 3
tamesi34_cs.cs Dump
    name_queues         [ENQUEUE_INDEX]    =[1116]
    lifeSpan_queues     [ENQUEUE_INDEX]    =[Durable, Autodelete]
    durable_lifeSpans   [ENQUEUE_INDEX]    =[True]
    autodelete_lifeSpans[ENQUEUE_INDEX]    =[True]
    passive_lifeSpans   [ENQUEUE_INDEX]    =[False]
    exclusive_lifeSpans [ENQUEUE_INDEX]    =[False]
    ----
    name_queues         [DEQUEUE_INDEX]    =[1117]
    lifeSpan_queues     [DEQUEUE_INDEX]    =[Durable, Autodelete]
    durable_lifeSpans   [DEQUEUE_INDEX]    =[True]
    autodelete_lifeSpans[DEQUEUE_INDEX]    =[True]
    passive_lifeSpans   [DEQUEUE_INDEX]    =[False]
    exclusive_lifeSpans [DEQUEUE_INDEX]    =[False]
    ----
    name_queues         [DELETEQUEUE_INDEX]=[]
    lifeSpan_queues     [DELETEQUEUE_INDEX]=[0]
    durable_lifeSpans   [DELETEQUEUE_INDEX]=[False]
    autodelete_lifeSpans[DELETEQUEUE_INDEX]=[False]
    passive_lifeSpans   [DELETEQUEUE_INDEX]=[False]
    exclusive_lifeSpans [DELETEQUEUE_INDEX]=[False]
    ----

(tamesi34_cs.cs?) GetChannel index = [1]
    name_queues[index] = [1117]
    passive_lifeSpans[index] = [False]
    durable_lifeSpans[index] = [True]
    exclusive_lifeSpans[index] = [False]
    autodelete_lifeSpans[index] = [True]
    nowait は仮に false 固定
    arguments は仮に null 固定
[3]   Exit 1                  ./tamesi35a2_cs.exe --enqueue 1117 durable autodelete --dequeue 1116 durable autodelete > ./tamesi35a2_cs.out.log 2> ./tamesi35a2_cs.err.log < /dev/null

これでもダメか。

ググろう

PRECONDITION_FAILED - inequivalent arg 'durable

キューを削除しよう

root@tk2-217-18401:/home/csg10/shogi/csharp_service# rabbitmqctl list_queues
Listing queues ...
1117    0
1118    0
myqueue 0
1115    0
1114    0
1119    0
root@tk2-217-18401:/home/csg10/shogi/csharp_service# ./tamesi34_cs.exe 2> ./tamesi34_cs.err.log
削除したいキューがあれば名前を、無ければ空文字列を入れろだぜ☆(^~^)
キュー名を入力    : キューを削除します
空文字列で[Enter] : 次のステップへ進む
Name or empty ? > myqueue
削除するメッセージ・キューの寿命を、足し算して答えろだぜ☆(^~^)
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが1人も接続していなかったら消す
(4) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(8) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > 1
tamesi34_cs.cs Dump
    name_queues         [ENQUEUE_INDEX]    =[]
    lifeSpan_queues     [ENQUEUE_INDEX]    =[0]
    durable_lifeSpans   [ENQUEUE_INDEX]    =[False]
    autodelete_lifeSpans[ENQUEUE_INDEX]    =[False]
    passive_lifeSpans   [ENQUEUE_INDEX]    =[False]
    exclusive_lifeSpans [ENQUEUE_INDEX]    =[False]
    ----
    name_queues         [DEQUEUE_INDEX]    =[]
    lifeSpan_queues     [DEQUEUE_INDEX]    =[0]
    durable_lifeSpans   [DEQUEUE_INDEX]    =[False]
    autodelete_lifeSpans[DEQUEUE_INDEX]    =[False]
    passive_lifeSpans   [DEQUEUE_INDEX]    =[False]
    exclusive_lifeSpans [DEQUEUE_INDEX]    =[False]
    ----
    name_queues         [DELETEQUEUE_INDEX]=[myqueue]
    lifeSpan_queues     [DELETEQUEUE_INDEX]=[Durable]
    durable_lifeSpans   [DELETEQUEUE_INDEX]=[True]
    autodelete_lifeSpans[DELETEQUEUE_INDEX]=[False]
    passive_lifeSpans   [DELETEQUEUE_INDEX]=[False]
    exclusive_lifeSpans [DELETEQUEUE_INDEX]=[False]
    ----

(tamesi34_cs.cs?) GetChannel index = [2]
    name_queues[index] = [myqueue]
    passive_lifeSpans[index] = [False]
    durable_lifeSpans[index] = [True]
    exclusive_lifeSpans[index] = [False]
    autodelete_lifeSpans[index] = [False]
    nowait は仮に false 固定
    arguments は仮に null 固定
[myqueue]キューを削除したはずだぜ☆(^~^) result=[4294967295]
削除したいキューがあれば名前を、無ければ空文字列を入れろだぜ☆(^~^)
キュー名を入力    : キューを削除します
空文字列で[Enter] : 次のステップへ進む
# rabbitmqctl list_queues
Listing queues ...
1117    0
1118    0
1115    0
1114    0
1119    0
# ./tamesi34_cs.exe 2> ./tamesi34_cs.err.log
削除したいキューがあれば名前を、無ければ空文字列を入れろだぜ☆(^~^)
キュー名を入力    : キューを削除します
空文字列で[Enter] : 次のステップへ進む
Name or empty ? > 1114
削除するメッセージ・キューの寿命を、足し算して答えろだぜ☆(^~^)
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが1人も接続していなかったら消す
(4) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(8) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > 1
tamesi34_cs.cs Dump
    name_queues         [ENQUEUE_INDEX]    =[]
    lifeSpan_queues     [ENQUEUE_INDEX]    =[0]
    durable_lifeSpans   [ENQUEUE_INDEX]    =[False]
    autodelete_lifeSpans[ENQUEUE_INDEX]    =[False]
    passive_lifeSpans   [ENQUEUE_INDEX]    =[False]
    exclusive_lifeSpans [ENQUEUE_INDEX]    =[False]
    ----
    name_queues         [DEQUEUE_INDEX]    =[]
    lifeSpan_queues     [DEQUEUE_INDEX]    =[0]
    durable_lifeSpans   [DEQUEUE_INDEX]    =[False]
    autodelete_lifeSpans[DEQUEUE_INDEX]    =[False]
    passive_lifeSpans   [DEQUEUE_INDEX]    =[False]
    exclusive_lifeSpans [DEQUEUE_INDEX]    =[False]
    ----
    name_queues         [DELETEQUEUE_INDEX]=[1114]
    lifeSpan_queues     [DELETEQUEUE_INDEX]=[Durable]
    durable_lifeSpans   [DELETEQUEUE_INDEX]=[True]
    autodelete_lifeSpans[DELETEQUEUE_INDEX]=[False]
    passive_lifeSpans   [DELETEQUEUE_INDEX]=[False]
    exclusive_lifeSpans [DELETEQUEUE_INDEX]=[False]
    ----

(tamesi34_cs.cs?) GetChannel index = [2]
    name_queues[index] = [1114]
    passive_lifeSpans[index] = [False]
    durable_lifeSpans[index] = [True]
    exclusive_lifeSpans[index] = [False]
    autodelete_lifeSpans[index] = [False]
    nowait は仮に false 固定
    arguments は仮に null 固定
[1114]キューを削除したはずだぜ☆(^~^) result=[4294967295]
削除したいキューがあれば名前を、無ければ空文字列を入れろだぜ☆(^~^)
キュー名を入力    : キューを削除します
空文字列で[Enter] : 次のステップへ進む
# rabbitmqctl list_queues
Listing queues ...
1117    0
1118    0
1115    0
1119    0
# ./tamesi34_cs.exe 2> ./tamesi34_cs.err.log
削除したいキューがあれば名前を、無ければ空文字列を入れろだぜ☆(^~^)
キュー名を入力    : キューを削除します
空文字列で[Enter] : 次のステップへ進む
Name or empty ? > 1115
削除するメッセージ・キューの寿命を、足し算して答えろだぜ☆(^~^)
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが1人も接続していなかったら消す
(4) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(8) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > 1
tamesi34_cs.cs Dump
    name_queues         [ENQUEUE_INDEX]    =[]
    lifeSpan_queues     [ENQUEUE_INDEX]    =[0]
    durable_lifeSpans   [ENQUEUE_INDEX]    =[False]
    autodelete_lifeSpans[ENQUEUE_INDEX]    =[False]
    passive_lifeSpans   [ENQUEUE_INDEX]    =[False]
    exclusive_lifeSpans [ENQUEUE_INDEX]    =[False]
    ----
    name_queues         [DEQUEUE_INDEX]    =[]
    lifeSpan_queues     [DEQUEUE_INDEX]    =[0]
    durable_lifeSpans   [DEQUEUE_INDEX]    =[False]
    autodelete_lifeSpans[DEQUEUE_INDEX]    =[False]
    passive_lifeSpans   [DEQUEUE_INDEX]    =[False]
    exclusive_lifeSpans [DEQUEUE_INDEX]    =[False]
    ----
    name_queues         [DELETEQUEUE_INDEX]=[1115]
    lifeSpan_queues     [DELETEQUEUE_INDEX]=[Durable]
    durable_lifeSpans   [DELETEQUEUE_INDEX]=[True]
    autodelete_lifeSpans[DELETEQUEUE_INDEX]=[False]
    passive_lifeSpans   [DELETEQUEUE_INDEX]=[False]
    exclusive_lifeSpans [DELETEQUEUE_INDEX]=[False]
    ----

(tamesi34_cs.cs?) GetChannel index = [2]
    name_queues[index] = [1115]
    passive_lifeSpans[index] = [False]
    durable_lifeSpans[index] = [True]
    exclusive_lifeSpans[index] = [False]
    autodelete_lifeSpans[index] = [False]
    nowait は仮に false 固定
    arguments は仮に null 固定
[1115]キューを削除したはずだぜ☆(^~^) result=[4294967295]
削除したいキューがあれば名前を、無ければ空文字列を入れろだぜ☆(^~^)
キュー名を入力    : キューを削除します
空文字列で[Enter] : 次のステップへ進む
# rabbitmqctl list_queues
Listing queues ...
1117    0
1118    0
1119    0

なんかキューは durable だけ指定すれば全部消せた。

キューを全部消して、durable+autodelete で行けた?

# ./tamesi35a2_cs.exe --enqueue 1113 durable autodelete --dequeue 1112 durable autodelete > ./tamesi35a2_cs.out.log 2> ./tamesi35a2_cs.err.log < /dev/null &
[3] 2613
root@tk2-217-18401:/home/csg10/shogi/csharp_service# ./tamesi34_cs.exe
削除したいキューがあれば名前を、無ければ空文字列を入れろだぜ☆(^~^)
キュー名を入力    : キューを削除します
空文字列で[Enter] : 次のステップへ進む
Name or empty ? >
エンキュー先のメッセージ・キューの名前を入れろだぜ☆(^~^)
Queue name? > 1112
エンキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆(^~^)
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが1人も接続していなかったら消す
(4) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(8) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > 3
デキュー先のメッセージ・キューの名前を入れろだぜ☆(^~^)
Queue name? > 1113
デキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆(^~^)
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが1人も接続していなかったら消す
(4) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(8) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > 3
tamesi34_cs.cs Dump
    name_queues         [ENQUEUE_INDEX]    =[1112]
    lifeSpan_queues     [ENQUEUE_INDEX]    =[Durable, Autodelete]
    durable_lifeSpans   [ENQUEUE_INDEX]    =[True]
    autodelete_lifeSpans[ENQUEUE_INDEX]    =[True]
    passive_lifeSpans   [ENQUEUE_INDEX]    =[False]
    exclusive_lifeSpans [ENQUEUE_INDEX]    =[False]
    ----
    name_queues         [DEQUEUE_INDEX]    =[1113]
    lifeSpan_queues     [DEQUEUE_INDEX]    =[Durable, Autodelete]
    durable_lifeSpans   [DEQUEUE_INDEX]    =[True]
    autodelete_lifeSpans[DEQUEUE_INDEX]    =[True]
    passive_lifeSpans   [DEQUEUE_INDEX]    =[False]
    exclusive_lifeSpans [DEQUEUE_INDEX]    =[False]
    ----
    name_queues         [DELETEQUEUE_INDEX]=[]
    lifeSpan_queues     [DELETEQUEUE_INDEX]=[0]
    durable_lifeSpans   [DELETEQUEUE_INDEX]=[False]
    autodelete_lifeSpans[DELETEQUEUE_INDEX]=[False]
    passive_lifeSpans   [DELETEQUEUE_INDEX]=[False]
    exclusive_lifeSpans [DELETEQUEUE_INDEX]=[False]
    ----

(tamesi34_cs.cs?) GetChannel index = [1]
    name_queues[index] = [1113]
    passive_lifeSpans[index] = [False]
    durable_lifeSpans[index] = [True]
    exclusive_lifeSpans[index] = [False]
    autodelete_lifeSpans[index] = [True]
    nowait は仮に false 固定
    arguments は仮に null 固定
終了するときは[Ctrl]+[C]キーを押せだぜ☆(^~^)
エンキューするときはメッセージを打ち込んで[Enter]キーを押せだぜ☆(^◇^)
Enqueue? > usi
tamesi34_cs.cs Dump
    name_queues         [ENQUEUE_INDEX]    =[1112]
    lifeSpan_queues     [ENQUEUE_INDEX]    =[Durable, Autodelete]
    durable_lifeSpans   [ENQUEUE_INDEX]    =[True]
    autodelete_lifeSpans[ENQUEUE_INDEX]    =[True]
    passive_lifeSpans   [ENQUEUE_INDEX]    =[False]
    exclusive_lifeSpans [ENQUEUE_INDEX]    =[False]
    ----
    name_queues         [DEQUEUE_INDEX]    =[1113]
    lifeSpan_queues     [DEQUEUE_INDEX]    =[Durable, Autodelete]
    durable_lifeSpans   [DEQUEUE_INDEX]    =[True]
    autodelete_lifeSpans[DEQUEUE_INDEX]    =[True]
    passive_lifeSpans   [DEQUEUE_INDEX]    =[False]
    exclusive_lifeSpans [DEQUEUE_INDEX]    =[False]
    ----
    name_queues         [DELETEQUEUE_INDEX]=[]
    lifeSpan_queues     [DELETEQUEUE_INDEX]=[0]
    durable_lifeSpans   [DELETEQUEUE_INDEX]=[False]
    autodelete_lifeSpans[DELETEQUEUE_INDEX]=[False]
    passive_lifeSpans   [DELETEQUEUE_INDEX]=[False]
    exclusive_lifeSpans [DELETEQUEUE_INDEX]=[False]
    ----

(tamesi34_cs.cs?) GetChannel index = [0]
    name_queues[index] = [1112]
    passive_lifeSpans[index] = [False]
    durable_lifeSpans[index] = [True]
    exclusive_lifeSpans[index] = [False]
    autodelete_lifeSpans[index] = [True]
    nowait は仮に false 固定
    arguments は仮に null 固定
 Enqueue(^q^) usi
Enqueue? > <---- [interrupt!] Dequeue(^q^) usidaze

usidaze が返ってきている。C#では成功か?

じゃあ

-Windows10 ではデフォルトで durable
-Ubuntu ではデフォルトで durable+autodelete

か。そんなん分からんなあ。

C++でもテストしたいが

root@tk2-217-18401:/home/★user/shogi/csharp_service# jobs
[1]-  Stopped                 nano tamesi34_cs.err.log
[2]+  Stopped                 nano tamesi35a2_cs.out.log
[3]   Running                 ./tamesi35a2_cs.exe --enqueue 1113 durable autodelete --dequeue 1112 durable autodelete > ./tamesi35a2_cs.out.log 2> ./tamesi35a2_cs.err.log < /dev/null &
root@tk2-217-18401:/home/★user/shogi/csharp_service# kill %3
root@tk2-217-18401:/home/★user/shogi/csharp_service# jobs
[1]-  Stopped                 nano tamesi34_cs.err.log
[2]+  Stopped                 nano tamesi35a2_cs.out.log
[3]   Terminated              ./tamesi35a2_cs.exe --enqueue 1113 durable autodelete --dequeue 1112 durable autodelete > ./tamesi35a2_cs.out.log 2> ./tamesi35a2_cs.err.log < /dev/null

ジョブを止めておく。

tamesi35a2.cpp をバックグラウンドで動かそう。標準入出力は捨てることが重要。

./tamesi35a2_cs.exe --enqueue 1113 durable autodelete --dequeue 1112 durable autodelete > ./tamesi35a2_cs.out.log 2> ./tamesi35a2_cs.err.log < /dev/null &
root@tk2-217-18401:~# ./tamesi35a2_cs.exe --enqueue 1113 durable autodelete --dequeue 1112 durable autodelete > ./tamesi35a2_cs.out.log 2> ./tamesi35a2_cs.err.log < /dev/null &
[1] 12555
root@tk2-217-18401:~# jobs
[1]+  Exit 127                ./tamesi35a2_cs.exe --enqueue 1113 durable autodelete --dequeue 1112 durable autodelete > ./tamesi35a2_cs.out.log 2> ./tamesi35a2_cs.err.log < /dev/null
root@tk2-217-18401:~# jobs

あれっ? これ、_cs だぜ。そして カレント・ディレクトリが違う。

root@tk2-217-18401:~# cd /home/★user/shogi/cpp_service
root@tk2-217-18401:/home/★user/shogi/cpp_service# ls
consume.cpp               tamesi29a11_enqueueTcp.cpp
consume.exe               tamesi29a11_enqueueTcp.exe
loop28_cpp.cpp            tamesi29a7_main.cpp
loop28_cpp.exe            tamesi29a7_main.exe
publish.cpp               tamesi29a8_example_consume.cpp
publish.exe               tamesi29a9_main.cpp
tamesi28.d                tamesi29a9_main.exe
tamesi29a10_main.cpp      tamesi33a2_cpp.cpp
tamesi29a10_main.exe      tamesi33a2_cpp.err.log
tamesi29a11_enqueueA.cpp  tamesi33a2_cpp.exe
tamesi29a11_enqueueA.exe  tamesi35a2_cpp.cpp
tamesi29a11_enqueueC.cpp  tamesi35a2_cpp.err.log
tamesi29a11_enqueueC.exe  tamesi35a2_cpp.exe
tamesi29a11_enqueue.exe
root@tk2-217-18401:/home/★user/shogi/cpp_service# ./tamesi35a2_cpp.exe --msgqueue 1117 durable 1116 durable 2> ./tamesi35a2_cpp.err.log &
[1] 12608
root@tk2-217-18401:/home/★user/shogi/cpp_service# Welcome! AMQP::durable=[1] AMQP::autodelete=[2] AMQP::passive=[8] AMQP::exclusive=[512]

プロンプトが返ってきてしまった。困るぜ。[Entery]キーを押してみる。

# jobs
[1]+  Running                 ./tamesi35a2_cpp.exe --msgqueue 1117 durable 1116 durable 2> ./tamesi35a2_cpp.err.log &

動いているようだ。

記事が長くなってきたので次へ。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2