クライアント アプリケーションが閉じられるまで、クライアントからサーバーへの接続が開いたままになるクライアント/サーバー アプリケーションで作業しています。
クライアントがデータを読み取っている間にサーバー アプリケーションが予期せずダウンした場合、クライアントはこれを例外として扱い、例外をキャッチして例外を引数としてイベントを発生させます。
このシステムが機能することをテストする必要があると思われるテストを作成しましたが、テストしているオブジェクトは、ブレークポイントを設定して続行しない限り、ソケットが閉じていることを登録していないようです。
テストの重要な部分は次のようになります。
StreamingMonitor sm = new StreamingMonitor();
bool errored = false;
string msg = "";
sm.ErrorOccurred += (s, a) =>
{
errored = true;
msg = a.Exception.Message;
};
sm.Enabled = true;
client = listener.AcceptTcpClient();
client.GetStream().Write(BitConverter.GetBytes(10000), 0, 4);
client.Close();
while(!errored)
{}
Assert.AreEqual("A request to send or receive data was disallowed because the socket had already been shut down in that direction with a previous shutdown call", msg);
TcpListener
オブジェクトlistener
はループバック アドレスをリッスンしています。
StreamingMonitor
は、有効になると、取得するデータの長さのリッスンを開始します。データの長さは常に、符号付き 32 ビット整数に収まると見なされます。
メッセージの長さが受信されると、このメソッドが呼び出されます。
private void GotMessageLength(IAsyncResult asyncResult)
{
try
{
client.Client.EndReceive(asyncResult);
if(firstMessage)
{
firstMessage = false;
if (Connected != null)
{
Connected(this, new EventArgs());
}
}
int msgLen = BitConverter.ToInt32(messageLength, 0);
byte[] message = new byte[msgLen];
List<byte> lbMessage = new List<byte>();
int bytesReturned = client.Client.Receive(message);
int remaining = (msgLen < bytesReturned) ? bytesReturned - msgLen : msgLen - bytesReturned;
if(remaining > 0)
{
if (bytesReturned > 0)
{
for (int i = 0; i < bytesReturned; i++)
{
lbMessage.Add(message[i]);
}
}
while(remaining > 0)
{
if(!client.Connected)
{
throw new SocketException((int)SocketError.Shutdown);
}
bytesReturned = client.Client.Receive(message);
remaining = (remaining < bytesReturned) ? bytesReturned - remaining : remaining - bytesReturned;
if (bytesReturned > 0)
{
for (int i = 0; i < bytesReturned; i++)
{
lbMessage.Add(message[i]);
}
}
}
message = lbMessage.ToArray();
}
MessageReceived(this, new MessageReceivedEventArgs(message));
if (Enabled)
{
client.Client.BeginReceive(messageLength, 0, 4, SocketFlags.None, GotMessageLength, null);
}
}
catch (SocketException ex)
{
if(ErrorOccurred != null)
{
ErrorOccurred(this, new ErrorEventArgs(ex));
}
}
catch (ObjectDisposedException)
{
}
}
このメソッドは、指定されたバイト数を読み取るまで、ネットワーク ストリームからデータを読み取ります。リモート接続が閉じると、ソケット例外が発生します。
ただし、単体テストは無限ループに陥り、エラーが発生するのを待ちます。これは、 のソケットがStreamingMonitor
もう一方の端が閉じられたことを認識しないためです。
SteamingMonitor
サーバーがなくなったことをどのように認識できますか?
これはループバック アドレスで可能ですか?
すべてのコードで申し訳ありませんが、メソッドを削減する方法が思いつきませんでした。