stackprobe7s_memo

何処にも披露する見込みの無いものを書き落とす場所

ミューテックスだけを使ってプロセス間通信を行う(C#)

説明

特徴

  • ミューテックスだけを使ってプロセス間通信できるんじゃね?と思って試してみたかっただけの代物。
  • ソケットとか共有メモリとかファイルとか一切使えないけど「ミューテックス」だけは使えるという特殊な状況下でプロセス間通信したい時に使えるかも。
  • 遅くて、実用的ではない。
    • コンパクトさを目指したので、多少速くする余地はあると思う。
    • 数十バイト程度の数回の通信なら気にならないと思う。

仕様

  • 送受信データは文字列
  • '\0' は送れない。
  • 一方通行 (クライアント ⇒ サーバー)

使い方

  1. サーバー側プロセスで MRecver.MRecv(ident, recved) を実行しておく。
    • 引数
      • ident -- ミューテックスの名前に使用する。ぶつかりにくく無難な文字列 (UUIDなど) にすること。クライアント側と同じであること。
      • recved -- 文字列を受信する度に呼ばれる。
    • ブロッキングモードで動作する。
      • 停止するには別スレッドで MRecver.MRecvEnd = true; を実行する。
  2. クライアント側プロセスで MSender.MSend(ident, message) を実行する。
    • 引数
      • ident -- サーバー側と同じ文字列
      • message -- 送信するメッセージ

サーバー側 (受信側)

public class MRecver
{
	public bool MRecvEnd;

	public void MRecv(string ident, Action<string> recved)
	{
		Mutex[] hdls = new Mutex[6];
		try
		{
			for (int i = 0; i < hdls.Length; i++)
				hdls[i] = new Mutex(false, ident + i);

			hdls[3].WaitOne();

			MemoryStream mem = new MemoryStream();
			byte chr = 0;
			int waitCount = 1;

			for (int i = 0, c = 0; !this.MRecvEnd; )
			{
				int n = (c + 1) % 3;

				hdls[3 + n].WaitOne();
				hdls[3 + c].ReleaseMutex();

				bool nBit = hdls[n].WaitOne(0);

				if (nBit)
					hdls[n].ReleaseMutex();

				if (waitCount <= 0)
				{
					if (!nBit)
						chr |= (byte)(1 << i);

					if (8 <= ++i)
					{
						if (chr == 0)
						{
							recved(Encoding.UTF8.GetString(mem.ToArray()));
							mem = new MemoryStream();
							waitCount = 1;
						}
						else
							mem.WriteByte(chr);

						i = chr = 0;
					}
				}
				else
				{
					if (nBit)
					{
						i = 0;
						Thread.Sleep(waitCount);
						if (waitCount < 100) waitCount++;
					}
					else if (8 <= ++i)
						i = waitCount = 0;
				}
				c = n;
			}
		}
		finally
		{
			foreach (Mutex hdl in hdls)
			{
				try { hdl.ReleaseMutex(); }
				catch { }
				try { hdl.Close(); }
				catch { }
			}
		}
	}
}

クライアント側 (送信側)

public class MSender
{
	public void MSend(string ident, string message)
	{
		Mutex[] hdls = new Mutex[6];
		try
		{
			for (int i = 0; i < hdls.Length; i++)
				hdls[i] = new Mutex(false, ident + i);

			hdls[3].WaitOne();

			bool[] flgs = new bool[3];
			int c = 0;

			foreach (byte[] bMes in new byte[][]
			{
				new byte[] { 0xff },
				Encoding.UTF8.GetBytes(message.Replace("\0", "")),
				new byte[] { 0x00 }
			})
			{
				for (int i = 0; i / 8 < bMes.Length; i++)
				{
					int n = (c + 1) % 3;

					hdls[3 + n].WaitOne();
					hdls[3 + c].ReleaseMutex();

					if ((bMes[i / 8] & (1 << (i % 8))) != 0)
					{
						if (!flgs[n])
						{
							hdls[n].WaitOne();
							flgs[n] = true;
						}
					}
					else if (flgs[n])
					{
						hdls[n].ReleaseMutex();
						flgs[n] = false;
					}
					c = n;
				}
			}
		}
		finally
		{
			foreach (Mutex hdl in hdls)
			{
				try { hdl.ReleaseMutex(); }
				catch { }
				try { hdl.Close(); }
				catch { }
			}
		}
	}
}