コンピュータ将棋ソフトとの対局サーバーを立てよう＜その１１＞

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

# バグの原因を調べよう
コンシューマの受信ループを　別スレッドにしてから　おかしくなった気がする。

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

試すだけ試そう。

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

```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 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 にする

ちょっと待て。

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

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

tamesi35a2_cpp.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 : コンシューマーが１人も接続していなかったら消す
(4) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(8) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > 1
デキュー先のメッセージ・キューの名前を入れろだぜ☆（＾～＾）
Queue name? > 1117
デキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆（＾～＾）
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが１人も接続していなかったら消す
(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

```csharp
// 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

```csharp
// 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>
        /// コンシューマーが１人も接続していなかったら消す
        /// </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 : コンシューマーが１人も接続していなかったら消す
        /// 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 : コンシューマーが１人も接続していなかったら消す
(" + ((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 : コンシューマーが１人も接続していなかったら消す
(" + ((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 : コンシューマーが１人も接続していなかったら消す
(" + ((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 : コンシューマーが１人も接続していなかったら消す
        /// 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
```

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

```csharp
//--------------------------------------------------------------------------------
// 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>
    /// コンシューマーが１人も接続していなかったら消す
    /// </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 : コンシューマーが１人も接続していなかったら消す
    /// 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 : コンシューマーが１人も接続していなかったら消す
(" + ((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 : コンシューマーが１人も接続していなかったら消す
(" + ((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 : コンシューマーが１人も接続していなかったら消す
(" + ((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 コマンドライン引数指定版

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

```csharp
//--------------------------------------------------------------------------------
// 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>
    /// コンシューマーが１人も接続していなかったら消す
    /// </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 : コンシューマーが１人も接続していなかったら消す
    /// 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 : コンシューマーが１人も接続していなかったら消す
(4) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(8) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > 0
デキュー先のメッセージ・キューの名前を入れろだぜ☆（＾～＾）
Queue name? > 1117
デキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆（＾～＾）
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが１人も接続していなかったら消す
(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
```

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

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

```
削除したいキューがあれば名前を、無ければ空文字列を入れろだぜ☆（＾～＾）
キュー名を入力    : キューを削除します
空文字列で[Enter] : 次のステップへ進む
Name or empty ? >
エンキュー先のメッセージ・キューの名前を入れろだぜ☆（＾～＾）
Queue name? > 1117
エンキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆（＾～＾）
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが１人も接続していなかったら消す
(4) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(8) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > 0
デキュー先のメッセージ・キューの名前を入れろだぜ☆（＾～＾）
Queue name? > 1116
デキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆（＾～＾）
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが１人も接続していなかったら消す
(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 が４つ入っている。

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

```
削除したいキューがあれば名前を、無ければ空文字列を入れろだぜ☆（＾～＾）
キュー名を入力    : キューを削除します
空文字列で[Enter] : 次のステップへ進む
Name or empty ? >
エンキュー先のメッセージ・キューの名前を入れろだぜ☆（＾～＾）
Queue name? > 1117
エンキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆（＾～＾）
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが１人も接続していなかったら消す
(4) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(8) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > 1
デキュー先のメッセージ・キューの名前を入れろだぜ☆（＾～＾）
Queue name? > 1116
デキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆（＾～＾）
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが１人も接続していなかったら消す
(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 : コンシューマーが１人も接続していなかったら消す
(4) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(8) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > 1
デキュー先のメッセージ・キューの名前を入れろだぜ☆（＾～＾）
Queue name? > 1117
デキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆（＾～＾）
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが１人も接続していなかったら消す
(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 : コンシューマーが１人も接続していなかったら消す
(4) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(8) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > 1
デキュー先のメッセージ・キューの名前を入れろだぜ☆（＾～＾）
Queue name? > 1117
デキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆（＾～＾）
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが１人も接続していなかったら消す
(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 : コンシューマーが１人も接続していなかったら消す
(4) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(8) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > 1
デキュー先のメッセージ・キューの名前を入れろだぜ☆（＾～＾）
Queue name? > 1117
デキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆（＾～＾）
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが１人も接続していなかったら消す
(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 : コンシューマーが１人も接続していなかったら消す
(4) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(8) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > 1
デキュー先のメッセージ・キューの名前を入れろだぜ☆（＾～＾）
Queue name? > 1117
デキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆（＾～＾）
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが１人も接続していなかったら消す
(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
```

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

こんな感じだろうか。

```csharp
//--------------------------------------------------------------------------------
// 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>
    /// コンシューマーが１人も接続していなかったら消す
    /// </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 : コンシューマーが１人も接続していなかったら消す
    /// 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 : コンシューマーが１人も接続していなかったら消す
(4) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(8) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > 3
デキュー先のメッセージ・キューの名前を入れろだぜ☆（＾～＾）
Queue name? > 1117
デキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆（＾～＾）
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが１人も接続していなかったら消す
(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 : コンシューマーが１人も接続していなかったら消す
(4) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(8) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > 3
デキュー先のメッセージ・キューの名前を入れろだぜ☆（＾～＾）
Queue name? > 1117
デキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆（＾～＾）
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが１人も接続していなかったら消す
(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 : コンシューマーが１人も接続していなかったら消す
(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 : コンシューマーが１人も接続していなかったら消す
(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 : コンシューマーが１人も接続していなかったら消す
(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 : コンシューマーが１人も接続していなかったら消す
(4) passive    : キューが存在するかどうかチェックするだけ。中身見ない時これ
(8) exclusive  : この接続でだけ使える。この接続が切れたら消す
Number ? > 3
デキュー先のメッセージ・キューの名前を入れろだぜ☆（＾～＾）
Queue name? > 1113
デキュー先のメッセージ・キューの寿命を、足し算して答えろだぜ☆（＾～＾）
(1) durable    : RabbitMQが止まってもキューを残す
(2) autodelete : コンシューマーが１人も接続していなかったら消す
(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 が返ってきている。Ｃ＃では成功か？

じゃあ

-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 &
```

動いているようだ。

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