如何关闭超时的 TcpClient?
How to close the TcpClient with a timeout?
我正在使用 .NET Core 并希望通过 TCP 发送消息。为此,我使用 TcpClient
class 并创建了自定义服务。这个解决方案目前有效,不确定我是否可以改进它
class MyTcpService : IMyTcpService
{
private readonly TcpClient tcpClient = new TcpClient();
public async Task Send(byte[] bytesToSend)
{
if (!tcpClient.Connected) // Check if client was closed before
{
await tcpClient.ConnectAsync("127.0.0.1", 5000); // Read values from config
}
NetworkStream networkStream = tcpClient.GetStream();
// Send the message
await networkStream.WriteAsync(bytesToSend, 0, bytesToSend.Length);
// Read the response
byte[] responseBuffer = new byte[1024]; // Read value from config
int amountOfResponseBytes = await networkStream.ReadAsync(responseBuffer, 0, responseBuffer.Length);
string responseMessage = Encoding.ASCII.GetString(responseBuffer, 0, amountOfResponseBytes);
// Close the connection with a timeout if true
if (true) // Read value from config
{
networkStream.Close(1000); // Read value from config
tcpClient.Close();
}
// Handle the response message here
// ...
}
}
我想将 IMyTcpService
作为临时服务注入。我想知道如何超时关闭客户端? Socket
class 有一个 Close
方法接受超时参数
但我找不到 TcpClient
的等价物,只是因为它的 NetworkStream
。
目前您正在等待 WriteAsync
和 ReadAsync
电话。因此,您对 networkStream.Close(1000)
的调用超时应该没有影响,并且连接将始终立即关闭,因为没有数据等待 sent/received。您没有为写入或读取指定超时,这意味着它们不会 return 直到数据传输完成。
I would like to know how to close the client with a timeout?
不清楚你为什么想要这个或者你想用这个实现什么。 TcpClient is simply a wrapper around a NetworkStream which in turn wraps around a Socket。因此,在 TcpClient
和 NetworkStream
上处理超时没有多大意义。
资源管理:
在您当前的示例中,我首先建议您将 TcpClient
保留在 Send
方法中,而不是 class 字段中。如果您不需要在其他地方使用 TcpClient
(期望您不需要,因为您在 Send
函数中关闭它),您应该缩小它的范围以便于资源管理。在这样做时,我建议您使用 using statement 以避免忘记正确处理您的资源。这适用于所有实现 IDisposable
接口的类型(当然也有例外)。
处理超时:
要处理您共享的这段代码中的超时,我建议您在写入和读取操作而不是关闭操作上配置超时,因为您的代码是非常连续的。一个可能看起来像的例子:
class MyTcpService : IMyTcpService
{
public async Task Send(byte[] bytesToSend)
{
string responseMessage;
using (tcpClient = new TcpClient())
{
if (shouldUseTimeout) // From config
{
tcpClient.ReceiveTimeout = 1000; // From config
tcpClient.SendTimeout = 1000; // From config
}
await tcpClient.ConnectAsync("127.0.0.1", 5000); // Read values from config
NetworkStream networkStream = tcpClient.GetStream();
// Send the message
await networkStream.WriteAsync(bytesToSend, 0, bytesToSend.Length);
// Read the response
byte[] responseBuffer = new byte[1024]; // Read value from config
int amountOfResponseBytes = await networkStream.ReadAsync(responseBuffer, 0, responseBuffer.Length);
responseMessage = Encoding.ASCII.GetString(responseBuffer, 0, amountOfResponseBytes);
}
// The tcpClient is now properly closed and disposed of
// Handle the response message here
// responseMessage...
}
}
更新回复评论 13/10/2020:
hey, thanks for your reply :) I tried to improve your answer, what do you think about this snippet? pastebin.com/7kTvtTv2
来自你的https://pastebin.com/7kTvtTv2:
public async Task<string> Send(byte[] messageToSend)
{
string responseMessage;
using (TcpClient tcpClient = new TcpClient())
{
await tcpClient.ConnectAsync("127.0.0.1", 5000); // From config
NetworkStream networkStream = tcpClient.GetStream();
await networkStream.WriteAsync(messageToSend, 0, messageToSend.Length);
await networkStream.FlushAsync();
tcpClient.Client.Shutdown(SocketShutdown.Send); // shutdown gracefully
byte[] responseBuffer = new byte[256]; // This can be of any size
StringBuilder stringBuilder = new StringBuilder();
int amountOfResponseBytes;
do
{
amountOfResponseBytes = await networkStream.ReadAsync(responseBuffer, 0, responseBuffer.Length);
string responseData = Encoding.ASCII.GetString(responseBuffer, 0, amountOfResponseBytes);
stringBuilder.Append(responseData);
} while (amountOfResponseBytes > 0);
responseMessage = stringBuilder.ToString();
}
return responseMessage;
}
我觉得不错。只有一些小意见:
await networkStream.FlushAsync()
- 当我查看 remarks for the Flush
method 时,这似乎是不必要的,但我还没有测试它:
The Flush method implements the Stream.Flush method; however, because NetworkStream is not buffered, it has no effect on network streams. Calling the Flush method does not throw an exception
tcpClient.Client.Shutdown(SocketShutdown.Send)
- 此方法只是告诉 Socket
不再允许 writing/sending 数据。由于您的 TcpClient
仅保留在 Send
方法中,因此不会在任何地方共享,因此似乎也没有必要。如果您无法完全控制何时使用 Socket
,我认为 Shutdown
方法最相关。
do { ... } while (...)
- 我觉得不错。请记住,如果您正在处理 ASCII 字符,responseBuffer
需要是 8 的倍数,这样您就不会最终尝试解码部分字符。
超时处理去哪儿了?您是忘记添加超时处理了还是不再相关了?目前,如果您有大量数据要发送或接收,或者网络速度很慢,WriteAsync
和 ReadAsync
调用可能会花费很长时间。
我正在使用 .NET Core 并希望通过 TCP 发送消息。为此,我使用 TcpClient
class 并创建了自定义服务。这个解决方案目前有效,不确定我是否可以改进它
class MyTcpService : IMyTcpService
{
private readonly TcpClient tcpClient = new TcpClient();
public async Task Send(byte[] bytesToSend)
{
if (!tcpClient.Connected) // Check if client was closed before
{
await tcpClient.ConnectAsync("127.0.0.1", 5000); // Read values from config
}
NetworkStream networkStream = tcpClient.GetStream();
// Send the message
await networkStream.WriteAsync(bytesToSend, 0, bytesToSend.Length);
// Read the response
byte[] responseBuffer = new byte[1024]; // Read value from config
int amountOfResponseBytes = await networkStream.ReadAsync(responseBuffer, 0, responseBuffer.Length);
string responseMessage = Encoding.ASCII.GetString(responseBuffer, 0, amountOfResponseBytes);
// Close the connection with a timeout if true
if (true) // Read value from config
{
networkStream.Close(1000); // Read value from config
tcpClient.Close();
}
// Handle the response message here
// ...
}
}
我想将 IMyTcpService
作为临时服务注入。我想知道如何超时关闭客户端? Socket
class 有一个 Close
方法接受超时参数
但我找不到 TcpClient
的等价物,只是因为它的 NetworkStream
。
目前您正在等待 WriteAsync
和 ReadAsync
电话。因此,您对 networkStream.Close(1000)
的调用超时应该没有影响,并且连接将始终立即关闭,因为没有数据等待 sent/received。您没有为写入或读取指定超时,这意味着它们不会 return 直到数据传输完成。
I would like to know how to close the client with a timeout?
不清楚你为什么想要这个或者你想用这个实现什么。 TcpClient is simply a wrapper around a NetworkStream which in turn wraps around a Socket。因此,在 TcpClient
和 NetworkStream
上处理超时没有多大意义。
资源管理:
在您当前的示例中,我首先建议您将 TcpClient
保留在 Send
方法中,而不是 class 字段中。如果您不需要在其他地方使用 TcpClient
(期望您不需要,因为您在 Send
函数中关闭它),您应该缩小它的范围以便于资源管理。在这样做时,我建议您使用 using statement 以避免忘记正确处理您的资源。这适用于所有实现 IDisposable
接口的类型(当然也有例外)。
处理超时:
要处理您共享的这段代码中的超时,我建议您在写入和读取操作而不是关闭操作上配置超时,因为您的代码是非常连续的。一个可能看起来像的例子:
class MyTcpService : IMyTcpService
{
public async Task Send(byte[] bytesToSend)
{
string responseMessage;
using (tcpClient = new TcpClient())
{
if (shouldUseTimeout) // From config
{
tcpClient.ReceiveTimeout = 1000; // From config
tcpClient.SendTimeout = 1000; // From config
}
await tcpClient.ConnectAsync("127.0.0.1", 5000); // Read values from config
NetworkStream networkStream = tcpClient.GetStream();
// Send the message
await networkStream.WriteAsync(bytesToSend, 0, bytesToSend.Length);
// Read the response
byte[] responseBuffer = new byte[1024]; // Read value from config
int amountOfResponseBytes = await networkStream.ReadAsync(responseBuffer, 0, responseBuffer.Length);
responseMessage = Encoding.ASCII.GetString(responseBuffer, 0, amountOfResponseBytes);
}
// The tcpClient is now properly closed and disposed of
// Handle the response message here
// responseMessage...
}
}
更新回复评论 13/10/2020:
hey, thanks for your reply :) I tried to improve your answer, what do you think about this snippet? pastebin.com/7kTvtTv2
来自你的https://pastebin.com/7kTvtTv2:
public async Task<string> Send(byte[] messageToSend)
{
string responseMessage;
using (TcpClient tcpClient = new TcpClient())
{
await tcpClient.ConnectAsync("127.0.0.1", 5000); // From config
NetworkStream networkStream = tcpClient.GetStream();
await networkStream.WriteAsync(messageToSend, 0, messageToSend.Length);
await networkStream.FlushAsync();
tcpClient.Client.Shutdown(SocketShutdown.Send); // shutdown gracefully
byte[] responseBuffer = new byte[256]; // This can be of any size
StringBuilder stringBuilder = new StringBuilder();
int amountOfResponseBytes;
do
{
amountOfResponseBytes = await networkStream.ReadAsync(responseBuffer, 0, responseBuffer.Length);
string responseData = Encoding.ASCII.GetString(responseBuffer, 0, amountOfResponseBytes);
stringBuilder.Append(responseData);
} while (amountOfResponseBytes > 0);
responseMessage = stringBuilder.ToString();
}
return responseMessage;
}
我觉得不错。只有一些小意见:
await networkStream.FlushAsync()
- 当我查看 remarks for the Flush
method 时,这似乎是不必要的,但我还没有测试它:
The Flush method implements the Stream.Flush method; however, because NetworkStream is not buffered, it has no effect on network streams. Calling the Flush method does not throw an exception
tcpClient.Client.Shutdown(SocketShutdown.Send)
- 此方法只是告诉 Socket
不再允许 writing/sending 数据。由于您的 TcpClient
仅保留在 Send
方法中,因此不会在任何地方共享,因此似乎也没有必要。如果您无法完全控制何时使用 Socket
,我认为 Shutdown
方法最相关。
do { ... } while (...)
- 我觉得不错。请记住,如果您正在处理 ASCII 字符,responseBuffer
需要是 8 的倍数,这样您就不会最终尝试解码部分字符。
超时处理去哪儿了?您是忘记添加超时处理了还是不再相关了?目前,如果您有大量数据要发送或接收,或者网络速度很慢,WriteAsync
和 ReadAsync
调用可能会花费很长时间。