关闭 TcpClient 和 NetworkStream 的问题
Problems with closing TcpClient and NetworkStream
目前我正在开发一个简单的客户端服务器聊天程序(想要使用 C# 进行客户端服务器通信)。到目前为止,除了与服务器正确断开连接外,它一直有效。
在客户端上,我在此处使用此代码关闭连接:
client.Client.Disconnect(false); // client is the TcpClient
client.Close();
在服务器上有一个线程循环等待来自客户端的消息:
private void StartChat()
{
int requestCount = 0;
byte[] bytesFrom = new byte[10025];
string dataFromClient = null;
string rCount = null;
while (true)
{
try
{
requestCount++;
NetworkStream stream = tcpClient.GetStream();
int bufferSize = (int)tcpClient.ReceiveBufferSize;
if (bufferSize > bytesFrom.Length)
{
bufferSize = bytesFrom.Length;
}
stream.Read(bytesFrom, 0, bufferSize);
dataFromClient = System.Text.Encoding.UTF8.GetString(bytesFrom);
dataFromClient = dataFromClient.Substring(0, dataFromClient.IndexOf("$"));
rCount = Convert.ToString(requestCount);
string message = client.Name + " says: " + dataFromClient;
program.Broadcast(message);
}
catch(Exception ex) when (ex is ObjectDisposedException || ex is InvalidOperationException || ex is System.IO.IOException)
{
program.UserDisconnected(client);
break;
}
catch(ArgumentOutOfRangeException ex)
{
Debug.WriteLine(ex.ToString());
break;
}
catch(Exception ex)
{
Debug.WriteLine(ex.ToString());
break;
}
}
如果客户端与上面显示的代码断开连接,该函数将一直获取流并产生这样的输出:
[=13=][=13=][=13=][=13=][=13=][=13=][=13=] [and so on]
在这种情况下,将抛出 ArgumentOutOfRangeException
,因为没有 $
的索引。我添加了一个 break
来避免无休止地执行循环。
令人惊讶的是 ObjectDisposedException
不会被抛出。此外,System.IO.IOException
不会被抛出,但它应该被抛出,因为流已关闭,因此连接已被拒绝。
如果我只是关闭连接到服务器的客户端应用程序,服务器不会停止循环,它只是等待一个流,该流永远不会出现,因为客户端断开连接。
那么我如何检测客户端是否已断开连接或无法访问?我关闭客户端连接的方式是否是关闭连接的正确方式?
感谢您的帮助!
更新:
private void StartChat()
{
int requestCount = 0;
byte[] bytesFrom = new byte[10025];
string dataFromClient = null;
string rCount = null;
while (true)
{
try
{
requestCount++;
NetworkStream stream = tcpClient.GetStream();
stream.ReadTimeout = 4000;
int bufferSize = (int)tcpClient.ReceiveBufferSize;
if (bufferSize > bytesFrom.Length)
{
bufferSize = bytesFrom.Length;
}
// Wait for a client message. If no message is recieved within the ReadTimeout a IOException will be thrown
try
{
int bytesRead = stream.Read(bytesFrom, 0, bufferSize);
stream.Flush();
if (bytesRead == 0)
{
throw new System.IO.IOException("Connection seems to be refused or closed.");
}
}
catch (System.IO.IOException)
{
byte[] ping = System.Text.Encoding.UTF8.GetBytes("%");
stream.WriteTimeout = 1;
stream.Write(ping, 0, ping.Length);
continue;
}
dataFromClient = System.Text.Encoding.ASCII.GetString(bytesFrom);
dataFromClient = dataFromClient.Substring(0, dataFromClient.IndexOf("$"));
rCount = Convert.ToString(requestCount);
string message = client.Name + " says: " + dataFromClient;
program.Broadcast(message);
}
catch(Exception ex) when (ex is ObjectDisposedException || ex is InvalidOperationException || ex is System.IO.IOException)
{
Debug.WriteLine(ex.ToString());
program.UserDisconnected(client);
break;
}
catch(ArgumentOutOfRangeException ex)
{
Debug.WriteLine(ex.ToString());
}
catch(Exception ex)
{
Debug.WriteLine(ex.ToString());
break;
}
}
}
您应该检查 stream.Read
的 return 值 - 它 return 是 实际 读取的字节数。
当客户端断开连接时,这将为0,当它读取数据时,读取的字节数通常小于缓冲区大小。
int bytes = stream.Read(bytesFrom, 0, bufferSize);
if (bytes == 0)
{
// client has disconnected
break;
}
dataFromClient = System.Text.Encoding.UTF8.GetString(bytesFrom, 0, bytes);
针对您最近的评论,客户端终止连接时可能会发生三种情况:
- 客户端正确关闭连接,你收到0字节
- 发生了可检测的事情,导致异常
- 客户端是"gone"但是没有信号发送到你这边
在最后一种情况下,服务器仍然会像存在活动连接一样工作,直到它尝试发送数据,这显然会失败。这就是为什么许多协议使用连接超时 and/or 保持活动机制的原因。
int bytesRead = stream.Read(...); if (bytesRead == 0) // 客户端断开连接
目前我正在开发一个简单的客户端服务器聊天程序(想要使用 C# 进行客户端服务器通信)。到目前为止,除了与服务器正确断开连接外,它一直有效。
在客户端上,我在此处使用此代码关闭连接:
client.Client.Disconnect(false); // client is the TcpClient
client.Close();
在服务器上有一个线程循环等待来自客户端的消息:
private void StartChat()
{
int requestCount = 0;
byte[] bytesFrom = new byte[10025];
string dataFromClient = null;
string rCount = null;
while (true)
{
try
{
requestCount++;
NetworkStream stream = tcpClient.GetStream();
int bufferSize = (int)tcpClient.ReceiveBufferSize;
if (bufferSize > bytesFrom.Length)
{
bufferSize = bytesFrom.Length;
}
stream.Read(bytesFrom, 0, bufferSize);
dataFromClient = System.Text.Encoding.UTF8.GetString(bytesFrom);
dataFromClient = dataFromClient.Substring(0, dataFromClient.IndexOf("$"));
rCount = Convert.ToString(requestCount);
string message = client.Name + " says: " + dataFromClient;
program.Broadcast(message);
}
catch(Exception ex) when (ex is ObjectDisposedException || ex is InvalidOperationException || ex is System.IO.IOException)
{
program.UserDisconnected(client);
break;
}
catch(ArgumentOutOfRangeException ex)
{
Debug.WriteLine(ex.ToString());
break;
}
catch(Exception ex)
{
Debug.WriteLine(ex.ToString());
break;
}
}
如果客户端与上面显示的代码断开连接,该函数将一直获取流并产生这样的输出:
[=13=][=13=][=13=][=13=][=13=][=13=][=13=] [and so on]
在这种情况下,将抛出 ArgumentOutOfRangeException
,因为没有 $
的索引。我添加了一个 break
来避免无休止地执行循环。
令人惊讶的是 ObjectDisposedException
不会被抛出。此外,System.IO.IOException
不会被抛出,但它应该被抛出,因为流已关闭,因此连接已被拒绝。
如果我只是关闭连接到服务器的客户端应用程序,服务器不会停止循环,它只是等待一个流,该流永远不会出现,因为客户端断开连接。
那么我如何检测客户端是否已断开连接或无法访问?我关闭客户端连接的方式是否是关闭连接的正确方式?
感谢您的帮助!
更新:
private void StartChat()
{
int requestCount = 0;
byte[] bytesFrom = new byte[10025];
string dataFromClient = null;
string rCount = null;
while (true)
{
try
{
requestCount++;
NetworkStream stream = tcpClient.GetStream();
stream.ReadTimeout = 4000;
int bufferSize = (int)tcpClient.ReceiveBufferSize;
if (bufferSize > bytesFrom.Length)
{
bufferSize = bytesFrom.Length;
}
// Wait for a client message. If no message is recieved within the ReadTimeout a IOException will be thrown
try
{
int bytesRead = stream.Read(bytesFrom, 0, bufferSize);
stream.Flush();
if (bytesRead == 0)
{
throw new System.IO.IOException("Connection seems to be refused or closed.");
}
}
catch (System.IO.IOException)
{
byte[] ping = System.Text.Encoding.UTF8.GetBytes("%");
stream.WriteTimeout = 1;
stream.Write(ping, 0, ping.Length);
continue;
}
dataFromClient = System.Text.Encoding.ASCII.GetString(bytesFrom);
dataFromClient = dataFromClient.Substring(0, dataFromClient.IndexOf("$"));
rCount = Convert.ToString(requestCount);
string message = client.Name + " says: " + dataFromClient;
program.Broadcast(message);
}
catch(Exception ex) when (ex is ObjectDisposedException || ex is InvalidOperationException || ex is System.IO.IOException)
{
Debug.WriteLine(ex.ToString());
program.UserDisconnected(client);
break;
}
catch(ArgumentOutOfRangeException ex)
{
Debug.WriteLine(ex.ToString());
}
catch(Exception ex)
{
Debug.WriteLine(ex.ToString());
break;
}
}
}
您应该检查 stream.Read
的 return 值 - 它 return 是 实际 读取的字节数。
当客户端断开连接时,这将为0,当它读取数据时,读取的字节数通常小于缓冲区大小。
int bytes = stream.Read(bytesFrom, 0, bufferSize);
if (bytes == 0)
{
// client has disconnected
break;
}
dataFromClient = System.Text.Encoding.UTF8.GetString(bytesFrom, 0, bytes);
针对您最近的评论,客户端终止连接时可能会发生三种情况:
- 客户端正确关闭连接,你收到0字节
- 发生了可检测的事情,导致异常
- 客户端是"gone"但是没有信号发送到你这边
在最后一种情况下,服务器仍然会像存在活动连接一样工作,直到它尝试发送数据,这显然会失败。这就是为什么许多协议使用连接超时 and/or 保持活动机制的原因。
int bytesRead = stream.Read(...); if (bytesRead == 0) // 客户端断开连接