我正在开发一个客户端/服务器应用程序,其中从客户端到服务器的连接保持打开状态,直到客户端应用程序关闭。
如果服务器应用程序意外关闭,而客户端正在读取数据,我希望客户端将此视为异常,但随后捕获异常并引发异常作为参数的事件。
我已经编写了一个测试,我认为应该测试这个系统是否正常工作,但我正在测试的对象似乎没有注册套接字是关闭的,除非我放入一个断点然后继续。
测试的重要部分如下:
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
意识到服务器已经消失?
这是否可以在环回地址上使用?
对不起所有的代码,我想不出如何削减方法。
答案 0 :(得分:3)
我可以就可能有用的区域提供一些一般性的指示。
环回(或仅使用localhost)通常与真实网络的行为方式不同。在每次调用socket api时发送/接收多少数据的场景。所以总是测试或真正的网络连接。
套接字api只会在尝试发送给它时发现对方是否断开连接(我认为这是正确的)。所以某种心跳功能派上用场了=)
编辑:您还可以通过尝试接收来确定另一方是否已断开连接以获取SocketException(对我的一些旧代码进行了一些基本测试)。
protected void ReceiveCallback(IAsyncResult ar)
{
var so = (StateObject)ar.AsyncState;
if (!so.Socket.Connected) return;
try
{
int read = so.Socket.EndReceive(ar);
if (read > 0)
ProcessBuffer(so, so.Buffer, read);
so.Socket.BeginReceive(so.Buffer, 0, so.Buffer.Length, SocketFlags.None, ReceiveCallback, so);
}
catch (SocketException e)
{
Trace.WriteLine("[Networking]::NetBase.ReceiveCallback: SocketException");
Output.WriteLine(e.Message);
RaiseDisconnected();
}
catch (ObjectDisposedException e)
{
Trace.WriteLine("[Networking]::NetBase.ReceiveCallback: ObjectDisposedException");
Output.WriteLine(e.Message);
RaiseDisconnected();
}
}
如果对方由于某种原因崩溃,这将调用我的断开连接功能。 希望它有所帮助