c# - NetworkStream.ReadTimeout 在应用夏令时和时钟向后移动时无法阻止 NetworkStream.Read(...)

标签 c# sockets datetime tcpclient networkstream

我希望有人可以解释这种行为,或者它是否可能是 .NET 中的错误。

由于夏令时而及时向后移动意味着 NetworkStream 不注意其属性 ReadTimeout,并且在此代码的情况下会导致循环旋转。 (这只是一个证明它正在发生的例子)。

要重现我看到的问题,您需要设置一个使用夏令时的时区,例如英国。

  1. 将您的时区设置为 UTC London,并确保选中夏令时。
  2. 将日期改回 2017 年 10 月 29 日
  3. 将时间设置为凌晨 01:58:50
  4. 运行下面的代码,观察它在凌晨 2 点应用夏令时时旋转,如果应用正确,时间应该回到凌晨 1 点。
  5. 请务必等待,它最多可能需要 30 秒才能开始旋转。

编辑:经过深入调查,1 小时后,它停止旋转,行为恢复正常并遵守 ReadTimeout。

如有任何想法,我们将不胜感激!

客户端代码:

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");
                }
            }
        }
    }
}

服务器代码:

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);
            }
        }
    }

最佳答案

the OS documentation中有一些非常重要的注意事项.NET ReadTimeout 属性中未提及的内容:

SO_RCVTIMEO and SO_SNDTIMEO

When using the recv function, if no data arrives during the period specified in SO_RCVTIMEO, the recv function completes. In Windows versions prior to Windows 2000, any data received subsequently fails with WSAETIMEDOUT. In Windows 2000 and later, if no data arrives within the period specified in SO_RCVTIMEO, the recv function returns WSAETIMEDOUT, 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.

目前,在每次超时后,您都在循环并尝试在同一个套接字上进行另一个接收操作。但是这个注释非常清楚地表明您需要创建一个新连接。

关于c# - NetworkStream.ReadTimeout 在应用夏令时和时钟向后移动时无法阻止 NetworkStream.Read(...),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48710174/

相关文章:

c# - C#中使用属性缓存方法的返回结果

c# - 通过在任何类之外声明的反射访问委托(delegate)?

c# - 在 Windows CE 的托管 C# 代码中加载 native DLL 作为调试模块

java - 监听 HTTP 请求

java - 组播数据开销?

python - 给定日期时间列的 Pandas groupby week

c# - Rebus:2 个进程中的 2 个处理程序。击球不一致且交替

c++ - Windows C/C++ 套接字编程

php - 如何找到 2 个日期时间对象之间的天数差异作为日期?

java - 将 java.util.Date 转换为什么 “java.time” 类型?