I bump to this while investing #44175. The original repro with CopyToAsync_AllDataCopied_Large was time sensitive and I could never reproduce it with running under debugger or strace. The code bellow is simplification based on my observation and fails for me on my Ubuntu18.04 system:
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
namespace stream
{
class Program
{
internal static (NetworkStream ClientStream, NetworkStream ServerStream) GetConnectedTcpStreams()
{
using (Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
listener.Bind(new IPEndPoint(IPAddress.Loopback, 0));
listener.Listen(1);
var clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
clientSocket.Connect(listener.LocalEndPoint);
Socket serverSocket = listener.Accept();
Console.WriteLine("GetConnectedTcpStreams: {0} {1} client {2} server {3}", clientSocket.LocalEndPoint, clientSocket.RemoteEndPoint, clientSocket.Handle, serverSocket.Handle);
serverSocket.NoDelay = true;
clientSocket.NoDelay = true;
Console.WriteLine("buffers = {0} and {1}", clientSocket.SendBufferSize, serverSocket.SendBufferSize);
// NetworkStream streams own socket -> should call Shutdown and write out all data.
return (new NetworkStream(clientSocket, ownsSocket: true), new NetworkStream(serverSocket, ownsSocket: true));
}
}
static async Task Main(string[] args)
{
int count = 1024 * 1024;
(NetworkStream client, NetworkStream server) = GetConnectedTcpStreams();
byte[] dataToCopy = new byte[count];
using (client)
{
// don't read quite yet.
await client.WriteAsync(dataToCopy, default);
// Should flush data! (not needed for repro)
client.Socket.Shutdown(SocketShutdown.Send);
Console.WriteLine("Write is done!");
}
// Attempt to read from server stream would fail here.
}
}
}
produces
furt@ubu18:~/projects/stream$ ~/dotnet-5.x/dotnet run
GetConnectedTcpStreams: 127.0.0.1:37446 127.0.0.1:41689 client 32 server 34
buffers = 2626560 and 2626560
Write is done!
and capture:
14:26:16.455441 IP localhost.37446 > localhost.41689: Flags [S], seq 2286837102, win 65495, options [mss 65495,sackOK,TS val 99816607 ecr 0,nop,wscale 7], length 0
14:26:16.455449 IP localhost.41689 > localhost.37446: Flags [S.], seq 2347911387, ack 2286837103, win 65483, options [mss 65495,sackOK,TS val 99816607 ecr 99816607,nop,wscale 7], length 0
14:26:16.455456 IP localhost.37446 > localhost.41689: Flags [.], ack 1, win 512, options [nop,nop,TS val 99816607 ecr 99816607], length 0
14:26:16.465777 IP localhost.37446 > localhost.41689: Flags [.], seq 1:32742, ack 1, win 512, options [nop,nop,TS val 99816617 ecr 99816607], length 32741
14:26:16.465783 IP localhost.41689 > localhost.37446: Flags [.], ack 32742, win 381, options [nop,nop,TS val 99816617 ecr 99816617], length 0
14:26:16.465799 IP localhost.37446 > localhost.41689: Flags [P.], seq 32742:65483, ack 1, win 512, options [nop,nop,TS val 99816617 ecr 99816617], length 32741
14:26:16.469147 IP localhost.41689 > localhost.37446: Flags [R.], seq 1, ack 65483, win 512, options [nop,nop,TS val 99816620 ecr 99816617], length 0
App above writes ~ 64K and then sends RST and discards rest of the data. There is no failure shown to the caller.
Of course read from server would now fail in way #44175 describes e.g. "SocketException : Connection reset by peer"
Same code runs ok on Windows and all data are written followed by FIN.
I think crucial part of this is fact that the large write is successful. Since there is space in socket buffer data are written to kernel but since server is not reading data, that will fill and close TCP windows. When Shutdown is called, it panics and sends RST instead of waiting for data to be flushed gracefully.
cc: @geoffkizer @stephentoub @tmds
I bump to this while investing #44175. The original repro with CopyToAsync_AllDataCopied_Large was time sensitive and I could never reproduce it with running under debugger or strace. The code bellow is simplification based on my observation and fails for me on my Ubuntu18.04 system:
produces
App above writes ~ 64K and then sends RST and discards rest of the data. There is no failure shown to the caller.
Of course read from server would now fail in way #44175 describes e.g. "SocketException : Connection reset by peer"
Same code runs ok on Windows and all data are written followed by FIN.
I think crucial part of this is fact that the large write is successful. Since there is space in socket buffer data are written to kernel but since server is not reading data, that will fill and close TCP windows. When Shutdown is called, it panics and sends RST instead of waiting for data to be flushed gracefully.
cc: @geoffkizer @stephentoub @tmds