locked
C# Setting UDP Socket Receive Timeout

    Question

  • Hi All:

     

    I filanny got my UDP send/receive code to work. Now i need to set a timeout on the receive side so that my application doesn't hand indefinitely if there is no response. The reveives shouldn't take any longer than about 15 seconds to return. If they haven't returned in that 15 second timeframe, i'd like to break out of waiting for the receive and then send the command again. I'd like to do this 3 times. I know it involves a while or dowhile loop in some sort of way. How do i specify a receive timeout of 15 seconds then break and send again if nothing has been received during that time? Here is my send/receive code:

     

    Code Snippet

    int GroupPort = 15000;

    UdpClient udp = new UdpClient();

    IPEndPoint groupEP = new IPEndPoint(IPAddress.Parse("255.255.255.255"), GroupPort);

    //IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, GroupPort);

    Socket mListener = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

    IPEndPoint end = new IPEndPoint(IPAddress.Any, GroupPort);

    mListener.Bind(end);

    string str4 = "Is anyone out there?";

    byte[] sendBytes4 = Encoding.ASCII.GetBytes(str4);

    udp.Send(sendBytes4, sendBytes4.Length, groupEP);

    byte[] buff = new byte[1024];

     

    //had to send Receive command more than once because I have 2 network cards and the first response i get back in the echo from my other network card

    mListener.Receive(buff, 0, buff.Length, SocketFlags.None);

    mListener.Receive(buff, 0, buff.Length, SocketFlags.None);

     

    string returnData = Encoding.ASCII.GetString(buff);

    Log(Loglevels.Detail, "Response: " + returnData);

     

    Monday, May 21, 2007 3:22 PM

Answers

  • I didn't look over your code extensively, but here is some psuedo code of how I would do it... 

    Code Snippet

    for(int curAttempt = 0; curAttempt < maxAttempts; curAttempt ++)

    {

     

    //send message

     

    try

    {

    //try to receive message

    }

    catch(SocketException   ex)

    {

    if(ex.ErrorCode != SocketError.TimedOut)

    throw new Exception("Unexpected Socket error", ex);

    if(curAttempt < maxAttempts-1)

    continue;//continue with the next attempt

    else

    throw new SomeExceptionOfYourChoice("Failed to receive a response", ex);

    }      

     

    //process received message here

     

    }

     

     

    Monday, May 21, 2007 9:53 PM
    Moderator
  • Just a reminder that your code above needs to capture the return value from Socket.Receive(...).  That return value is the number of bytes in the buffer that are valid (the incoming message may not fill the entire buffer).  If you don't do this, you are likely to get garbage at the end of the string returned by Encoding.ASCII.GetString(...).

     

    Code Snippet

    int count = mListener.Receive(buff, 0, buff.Length, SocketFlags.None); 

    string returnData = Encoding.ASCII.GetString(buff, 0, count);

     

    Tuesday, May 22, 2007 4:13 PM
    Moderator

All replies

  • Setting the receive timeout is as simple as doing one of the following (equivalent to each other):

     

    Code Snippet

    mListener.ReceiveTimeout = 15000;//15 seconds

    //or 

    mListener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 15000);

     

     Then just do a normal receive.  If you don't get a response within 15 seconds, a SocketException is thrown.  You should be able to check the SocketException.ErrorCode property for a value of SocketError.TimedOut and then retry as needed. 

    Monday, May 21, 2007 6:41 PM
    Moderator
  • thanks. i'll give this a try. i'm not that experienced with C# so i know i'll have to figure out a while loop of some sort to try sendig the command 3 times and if each receive attempt times out then move on to the next try.
    Monday, May 21, 2007 7:41 PM
  • i tried to set up a while loop that tries to send/receive 3 times and then throws an exception. the problem is that on the first attempt if i don't get a response the code throws an exception and fails instead of continuing on inside the loop. how do i get my code to continue if there is no response during the 15 second timeout setting? Here is my code:

     

    Code Snippet

    int GroupPort = 15000;

    UdpClient udp = new UdpClient();

    IPEndPoint groupEP = new IPEndPoint(IPAddress.Parse("255.255.255.255"), GroupPort);

    //IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, GroupPort);

    Socket mListener = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

    IPEndPoint end = new IPEndPoint(IPAddress.Any, GroupPort);

    mListener.Bind(end);

    mListener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 15000);

    string str5 = "Is anyone out there?";

    byte[] sendBytes5 = Encoding.ASCII.GetBytes(str5);

    string returnData = String.Empty;

    bool answer = false;

    while(!answer)

    {

    for(int trys = 1; trys < 4; trys++)

    {

       udp.Send(sendBytes5, sendBytes5.Length, groupEP);

       byte[] buff = new byte[1024];

       mListener.Receive(buff, 0, buff.Length, SocketFlags.None);

       //had to send Receive command more than once because I have 2 network cards and the first response i get back in the echo from my other network card

       mListener.Receive(buff, 0, buff.Length, SocketFlags.None);

       mListener.Receive(buff, 0, buff.Length, SocketFlags.None);

    //this code throws the system exception ""A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond" on the above line after 15 seconds

       returnData = Encoding.ASCII.GetString(buff);

       if(returnData != String.Empty)

       {

          Log(Loglevels.Detail, "Response: " + returnData);

          string queryStr = null;

          try

          {

          queryStr = returnData.Substring(returnData.IndexOf("Pass or Fail:"));

          queryStr = queryStr.Substring(queryStr.IndexOf("")+14,4);

          }

          catch (Exception e1)

          {

             e1=e1;

             Log(Loglevels.Detail, "\t\tWarning: Result could not be found...");

          }

          if(queryStr == "Fail")

          {

             Log(Loglevels.Detail, "FIX YOUR CODE!!");

          }

          else

          {

             Log(Loglevels.Detail, "YOU ROCK!!");

             Log(Loglevels.Detail, "Test Result: "+queryStr);

             answer = true;

          }

       }

       else

       {

          if(trys == 3)

          {

             Log(Loglevels.Detail, "No Response. Timeout");

             throw new Exception("Response Timeout");

          }

       }

    }

     

     

    Monday, May 21, 2007 8:17 PM
  • I didn't look over your code extensively, but here is some psuedo code of how I would do it... 

    Code Snippet

    for(int curAttempt = 0; curAttempt < maxAttempts; curAttempt ++)

    {

     

    //send message

     

    try

    {

    //try to receive message

    }

    catch(SocketException   ex)

    {

    if(ex.ErrorCode != SocketError.TimedOut)

    throw new Exception("Unexpected Socket error", ex);

    if(curAttempt < maxAttempts-1)

    continue;//continue with the next attempt

    else

    throw new SomeExceptionOfYourChoice("Failed to receive a response", ex);

    }      

     

    //process received message here

     

    }

     

     

    Monday, May 21, 2007 9:53 PM
    Moderator
  • thanks Jon. i will try this code in the morning and reply with my findings!!
    Monday, May 21, 2007 11:21 PM
  • Just a reminder that your code above needs to capture the return value from Socket.Receive(...).  That return value is the number of bytes in the buffer that are valid (the incoming message may not fill the entire buffer).  If you don't do this, you are likely to get garbage at the end of the string returned by Encoding.ASCII.GetString(...).

     

    Code Snippet

    int count = mListener.Receive(buff, 0, buff.Length, SocketFlags.None); 

    string returnData = Encoding.ASCII.GetString(buff, 0, count);

     

    Tuesday, May 22, 2007 4:13 PM
    Moderator
  • JOn:

     

    Many thanks to you and everyone else who chimed in to help me with my problem. My code now works fine. I am attaching it so you can see the modifications I made. I had to go in and find the exception code that cases the program to fail and use it:

     

    Code Snippet

    int GroupPort = 15000;

    int maxTrys = 3;

    int currentTrys;

     

    UdpClient udp = new UdpClient();

    IPEndPoint groupEP = new IPEndPoint(IPAddress.Parse("255.255.255.255"), GroupPort);

    //IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, GroupPort);

    Socket mListener = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

    IPEndPoint end = new IPEndPoint(IPAddress.Any, GroupPort);

    mListener.Bind(end);

    mListener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 15000);

    string str5 = "Is anyone out there?";

    string returnData = null;

    byte[] sendBytes5 = Encoding.ASCII.GetBytes(str5);

    for(currentTrys = 0; currentTrys < maxTrys; currentTrys++)

    {

       Log(Loglevels.Detail, "Attempting to send UDP command...");

       //udp.Send(sendBytes4, sendBytes4.Length, groupEP);

       udp.Send(sendBytes5, sendBytes5.Length, groupEP);

       byte[] buff = new byte[1024];

       try

       {

          mListener.Receive(buff, 0, buff.Length, SocketFlags.None);

          //had to send Receive command more than once because I have 2 network cards and the first response i get back is the echo from my other    network card

          mListener.Receive(buff, 0, buff.Length, SocketFlags.None);

          mListener.Receive(buff, 0, buff.Length, SocketFlags.None);

       }

       catch (SocketException e)

       {

          if(e.ErrorCode != 0x274c)

          {

             throw new Exception("Unexpected Socket Error: " + e.Message);

          }

          if(currentTrys < maxTrys - 1)

          {

             Log(Loglevels.Detail, "No response to command. Sending command again...");

             continue; //continue with next attempt

          }

          else

          {

             Log(Loglevels.Detail, "Receive Response Timeout: " + e.Message);

             throw new Exception("Command Receive Response Timed Out: " + e.Message);

          }

       }

       returnData = Encoding.ASCII.GetString(buff);

       Log(Loglevels.Detail, "Response: " + returnData);

       string queryStr = null;

       try

       {

          queryStr = returnData.Substring(returnData.IndexOf("Pass or Fail:"));

          queryStr = queryStr.Substring(queryStr.IndexOf("")+14,4);

       }

       catch (Exception e1)

       {

          e1=e1;

          Log(Loglevels.Detail, "\t\tWarning: Result could not be found...");

       }

       if(queryStr == "Fail")

       {

          Log(Loglevels.Detail, "Test FAILED!!");

          throw new Exception("Test FAILED");

       }

       else

       {

          Log(Loglevels.Detail, "Test PASSED!!");

          Log(Loglevels.Detail, "Test Result: "+queryStr);

          break;

       }

    }

     

     

    Tuesday, May 22, 2007 4:29 PM
  • Thanks Jon. I will add the variable count as well.
    Tuesday, May 22, 2007 4:31 PM