locked
Why i'm getting exception: A blocking operation was interrupted by a call to WSACancelBlockingCall? RRS feed

  • Question

  • I have this method:

    public DateTime GetNetworkTime()
                {
                    DateTime networkDateTime = DateTime.Now;
                    try
                    {
                    IPAddress[] addresses = null;
                    //default Windows time server
                    const string ntpServer = "time.windows.com";
                    const string ntpServer1 = "time.nist.gov";
                    const string ntpServer2 = "time-nw.nist.gov";
                    const string ntpServer3 = "time-a.nist.gov";
                    const string ntpServer4 = "time-b.nist.gov";
                    List<string> ntpServersList = new List<string>();
                    ntpServersList.Add(ntpServer);
                    ntpServersList.Add(ntpServer1);
                    ntpServersList.Add(ntpServer2);
                    ntpServersList.Add(ntpServer3);
                    ntpServersList.Add(ntpServer4);
        
                    // NTP message size - 16 bytes of the digest (RFC 2030)
                    var ntpData = new byte[48];
        
                    //Setting the Leap Indicator, Version Number and Mode values
                    ntpData[0] = 0x1B; //LI = 0 (no warning), VN = 3 (IPv4 only), Mode = 3 (Client Mode)
        
                    for (int i = 0; i < ntpServersList.Count; i++)
                    {
                        addresses = Dns.GetHostEntry(ntpServersList[i]).AddressList;
                        if (addresses.Length > 0)
                        {
                            break;
                        }
                    }
                    
        
                    //The UDP port number assigned to NTP is 123
                    var ipEndPoint = new IPEndPoint(addresses[0], 123);
                    //NTP uses UDP
                    var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
        
                    socket.Connect(ipEndPoint);
                    socket.Send(ntpData);
                        Thread th = new Thread(()=>
                        {
                           socket.Receive(ntpData);
                           flag.Set();
                        });
                        th.IsBackground = true;
                        th.Start();
        
                        //Block the current thread for 5 seconds
                        flag.WaitOne(5000, false);
                    socket.Close();
        
                    //Offset to get to the "Transmit Timestamp" field (time at which the reply 
                    //departed the server for the client, in 64-bit timestamp format."
                    const byte serverReplyTime = 40;
        
                    //Get the seconds part
                    ulong intPart = BitConverter.ToUInt32(ntpData, serverReplyTime);
        
                    //Get the seconds fraction
                    ulong fractPart = BitConverter.ToUInt32(ntpData, serverReplyTime + 4);
        
                    //Convert From big-endian to little-endian
                    intPart = SwapEndianness(intPart);
                    fractPart = SwapEndianness(fractPart);
        
                    var milliseconds = (intPart * 1000) + ((fractPart * 1000) / 0x100000000L);
        
                    //**UTC** time
                    networkDateTime = (new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc)).AddMilliseconds((long)milliseconds);
                    }
                    catch(Exception err)
                    {
                        MessageBox.Show("error" + err.ToString());
                    }
                    return networkDateTime.ToLocalTime();
                }





    In the top of Form1 i did:

    AutoResetEvent flag;




    In the constructor:

    flag = new AutoResetEvent(false);



    Then in the method above the GetNetworkTime() the part i changed was:

    Thread th = new Thread(()=>
                        {
                           socket.Receive(ntpData);
                           flag.Set();
                        });
                        th.IsBackground = true;
                        th.Start();
        
                        //Block the current thread for 5 seconds
                        flag.WaitOne(5000, false);
    

    Before using the flag and the Thread the program was hang/freeze and i did DEBUG > Break All (Pause)
    And it stopped on the line:

        socket.Receive(ntpData);



    So i added this flag and thread code.
    And now when running my program i'm getting this exception on the line:

        socket.Receive(ntpData);

        




    SocketException
    A blocking operation was interrupted by a call to WSACancelBlockingCall

    System.Net.Sockets.SocketException was unhandled
          HResult=-2147467259
          Message=A blocking operation was interrupted by a call to WSACancelBlockingCall
          Source=System
          ErrorCode=10004
          NativeErrorCode=10004
          StackTrace:
               at System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags)
               at System.Net.Sockets.Socket.Receive(Byte[] buffer)
               at TestDateTime.Form1.<>c__DisplayClass1.<GetNetworkTime>b__0() in d:\C-Sharp\TestDateTime\TestDateTime\TestDateTime\Form1.cs:line 125
               at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
               at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
               at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
               at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
               at System.Threading.ThreadHelper.ThreadStart()
          InnerException: 

    The problem i think is that i'm doing:

    socket.Close();

    But i'm not sure how to handle this exception. To remove the socket.Close(); ? To move it to another place in the method ?

    To change all the Thread part of the code ?

    Monday, September 30, 2013 2:57 PM

Answers

  • Receive is a blocking call.  Therefore if you were to break while waiting on data you would see the break on this line.  That is the correct behavior.  Now if this call were occurring on a UI thread then the UI would freeze which is probably not what you want.  You can make the call async by either moving the recv to another thread or using the async version.

    Your specific problem, as you theorized, is that you're closing the socket while it is still trying to recv data.  You don't want to do that.  It appears that you're trying to call the first time server that you can get the address of.  All the code related to contacting the server (including socket creation) should be inside the worker thread.  The socket itself can be wrapped in a using block to ensure it gets cleaned up after the response is received.  Keeping with your original code you could do this:

    //Find the server to call
    IPEndPoint ipEndPoint;
    
    Task.Factory.StartNew<byte[]>(() => GetTimeFromServer(ipEndPoint))
       .ContinueWith(t => {
    
       //Do something with the results
    });
    
    byte[] GetTimeFromServer ( IPEndPoint endpoint )
    {
       //Set up data to send
       var sendData = new byte[48];
       var buffer = new byte[48];
    
       using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
       {
          socket.Connect(endpoint);
          socket.Send(sendData);
    				
          //Note that this needs to handle the fact that the entire buffer may not come back in a single call
          socket.Receive(buffer);
    
          return buffer;
       };
    }

    If you are using .NET v4.5 then you can instead use async and await to accomplish the same thing.  The net effect is that the socket lifetime and work is on a secondary thread.  Your main thread (where you found the server and made the call can decide how to wait for the results and what to do when it gets returned.

    Michael Taylor
    http://msmvps.com/blogs/p3net

    • Marked as answer by Chocolade1972 Saturday, October 5, 2013 7:58 PM
    Monday, September 30, 2013 6:28 PM