I'm hoping someone can explain this behaviour, or if it's maybe a bug in .NET.
Moving backwards in time due to daylight savings means that NetworkStream pays no attention to its property ReadTimeout, and in the case of this code causes the loops to spin. (This is just an example to prove it's happening).
To reproduce the problem I am seeing, you will need to be set to a timezone that uses daylight savings, eg The United Kingdom.
Edit: After deeping investigation, after 1 hour, it stops spinning and the behaviour returns to normal and honours the ReadTimeout.
Any thoughts would be appreciated!
Client code:
class Program
{
static bool running = false;
static void Main(string[] args)
{
running = true;
Task.Factory.StartNew(() => Run());
Console.ReadKey();
running = false;
}
static void Run()
{
TcpClient connection = new TcpClient("127.0.0.1", 1234);
while (running)
{
if (connection != null && connection.Connected)
{
try
{
NetworkStream stream = connection.GetStream();
stream.ReadTimeout = 1000;
byte[] buffer = new byte[1024];
int readCount = stream.Read(buffer, 0, 1024); // Should block here for the ReadTimeout duration if nothing received
// However when daylight savings is applied and time moves backwards an hour, the stream.ReadTimeout = 1000;
// is not honoured and it falls through and spins
if (readCount > 0)
{
Console.WriteLine("Received some data");
//process read here
}
else
{
Console.WriteLine("ReadTimeout was not honoured");
}
}
catch (IOException)
{
Console.WriteLine("Read timed out");
}
}
}
}
}
Server Code:
class Program
{
static bool running = false;
public static void Main()
{
TcpListener server = null;
try
{
// Set the TcpListener on port 13000.
Int32 port = 5000;
IPAddress localAddr = IPAddress.Parse("192.168.1.69");
// TcpListener server = new TcpListener(port);
server = new TcpListener(localAddr, port);
// Start listening for client requests.
server.Start();
// Enter the listening loop.
while (true)
{
Console.Write("Waiting for a connection... ");
// Perform a blocking call to accept requests.
// You could also user server.AcceptSocket() here.
TcpClient client = server.AcceptTcpClient();
Console.WriteLine("Connected!");
// Get a stream object for reading and writing
NetworkStream stream = client.GetStream();
running = true;
Task.Factory.StartNew(() => Run(stream));
Console.ReadKey();
// Shutdown and end connection
client.Close();
}
}
catch (SocketException e)
{
Console.WriteLine("SocketException: {0}", e);
}
finally
{
// Stop listening for new clients.
server.Stop();
}
Console.WriteLine("\nHit enter to continue...");
Console.Read();
}
static async Task Run(NetworkStream stream)
{
byte[] stuffToSend = Encoding.ASCII.GetBytes("Stuff to send");
while (running)
{
stream.Write(stuffToSend, 0, stuffToSend.Length);
await Task.Delay(1000);
}
}
}
There are some very important notes in the OS documentation that aren't mentioned in the .NET ReadTimeout
property:
SO_RCVTIMEO
andSO_SNDTIMEO
When using the
recv
function, if no data arrives during the period specified inSO_RCVTIMEO
, therecv
function completes. In Windows versions prior to Windows 2000, any data received subsequently fails withWSAETIMEDOUT
. In Windows 2000 and later, if no data arrives within the period specified inSO_RCVTIMEO
, therecv
function returnsWSAETIMEDOUT
, and if data is received,recv
returns SUCCESS.If a send or receive operation times out on a socket, the socket state is indeterminate, and should not be used; TCP sockets in this state have a potential for data loss, since the operation could be canceled at the same moment the operation was to be completed.
Currently, after each timeout you are looping and trying another receive operation on the same socket. But this note makes it very clear that you need to create a new connection.
Please read this. https://msdn.microsoft.com/en-us/library/ms973825.aspx
In general, if you are dealing with absolute elapsed time, such as measuring a timeout, performing arithmetic, or doing comparisons of different DateTime values, you should try and use a Universal time value if possible so that you get the best possible accuracy without effects of time zone and/or daylight savings having an impact.
Just adjust the thread to use universal time.
Edit 1.
I failed to understand your question. You did not want a workaround. Its a known bug related to how .net manages the, and related discussion has been done before here.
Cache.Add absolute expiration - UTC based or not?
During one hour at the end of daylight savings time, your local time is ambiguous, so you can get unexpected results, ie the absolute expiration can be one hour longer than expected.
Edit 2. Added another discussion thread more in context with the OP question.
Here is the sourced code : http://referencesource.microsoft.com/#mscorlib/system/io/stream.cs,f956b0c07e86df64
[ComVisible(false)]
public virtual int ReadTimeout {
get {
Contract.Ensures(Contract.Result<int>() >= 0);
throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_TimeoutsNotSupported"));
}
set {
throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_TimeoutsNotSupported"));
}
}
[ComVisible(false)]
public virtual int WriteTimeout {
get {
Contract.Ensures(Contract.Result<int>() >= 0);
throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_TimeoutsNotSupported"));
}
set {
throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_TimeoutsNotSupported"));
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.