我遇到的问题是我尝试将数据写入远程TCP服务器,连接被切断,但我无法检测到何时发生这种情况。
我目前的假设是WriteAsync
正在缓冲数据,但我还假设FlushAsync
应该强制它通过线路(并导致IOException
)。
我尝试这样做的真正原因是我需要做一些低级别的HTTP流量,并且我想在发送请求正文之前验证HTTP标头是否正确发送。
我有一个简单的单元测试来说明我面临的问题。
public sealed class SocketDisconnectTests
{
[Fact]
public async Task ShouldNotWriteIfDisconnected()
{
var listener = new TcpListener(IPAddress.IPv6Any, 0);
listener.Server.SetSocketOption(
SocketOptionLevel.IPv6,
SocketOptionName.IPv6Only,
false);
listener.Start();
try
{
// do not await here, let it happen asynchronously
var readAndCloseTask = ReadAndCloseAsync(listener);
using (var clientSocket = new Socket(
SocketType.Stream,
ProtocolType.Tcp))
{
clientSocket.NoDelay = true;
await clientSocket.ConnectAsync(
"localhost",
((IPEndPoint) listener.LocalEndpoint).Port);
using (var clientStream = new NetworkStream(
clientSocket,
true))
{
var buf = new byte[] {1, 2, 3, 4};
await clientStream.WriteAsync(buf, 0, buf.Length);
await clientStream.ReadAsync(buf, 0, buf.Length);
Assert.Equal(4, buf[0]);
Assert.Equal(3, buf[1]);
Assert.Equal(2, buf[2]);
Assert.Equal(1, buf[3]);
// ensure remote closed the connection
await readAndCloseTask;
// this assertion always fails as WriteAsync and
// FlushAsync complete without exception
await Assert.ThrowsAsync<IOException>(async () =>
{
await clientStream.WriteAsync(buf, 0, buf.Length);
await clientStream.FlushAsync();
});
}
}
}
finally
{
listener.Stop();
}
}
private static async Task ReadAndCloseAsync(TcpListener listener)
{
using (var socket = await listener.AcceptSocketAsync())
{
socket.NoDelay = true;
using (var stream = new NetworkStream(socket, true))
{
var buffer = new byte[4];
await stream.ReadAsync(buffer, 0, buffer.Length);
var reverse = buffer.Reverse().ToArray();
await stream.WriteAsync(reverse, 0, reverse.Length);
}
}
}
}
答案 0 :(得分:0)
当您使用socket.NoDelay
时,您将关闭Nagle algorithm,否则会确保/验证是否已收到数据。
通过设置socket.NoDelay
,您说您不需要收件人在收到数据后会回复的ACK。如果套接字被告知不要查找该ACK,为什么它会报告缺少一个?
修改强>
根据已经应用的调整,socket.NoDelay
没有效果。根据OP中的注释,执行Web连接不需要显式尝试管理TCP级连接池,因为WebClient
和HttpWebRequest
都内置了连接管理器。对于{{{} 1}},您可以通过将HttpWebRequest
属性设置为KeepAlive
来关闭连接池(按default开启)。
false
也使用类似的connection pooling,因此不需要自己滚动。
鉴于已经处理了连接池,确保服务器在发送主体之前接收头信息的目的最好是通过简单地发出2个Web请求,一个具有基本头部详细信息而另一个具有主体。 1号成功后。不应该需要在TCP级别工作。