locked
Call UdpClient.Send() inside System.Timers.Timer event does not work RRS feed

  • Question

  • Hi,

    in my application I receive an UDP stream and I forward it on two other UDP endpoints.
    My problem is that I need to put a delay between the two Send(), so I thought to use a Timer. This is my code (where I show only the relevant methods):

    public void Start()
    {      
      Socket udpSocket = m_udpLocalClient.Client;
    
      udpSocket.ReceiveBufferSize = BUFFER_SIZE;
    
      // Enable broadcast
      udpSocket.SetSocketOption(SocketOptionLevel.Socket,
                                SocketOptionName.Broadcast,
                                1);
      // Enable reusing address
      udpSocket.SetSocketOption(SocketOptionLevel.Socket,
                                SocketOptionName.ReuseAddress,
                                true);
    
      // Bind the socket to the local endpoint and listen for incoming connections.
      udpSocket.Bind(new IPEndPoint(IPAddress.Any, m_ListenPort));
    
      m_udpLocalClient.JoinMulticastGroup(IPAddress.Parse(m_ListenIP));
    
      udpSocket.BeginReceiveFrom(m_data,
                                 0,
                                 m_data.Length,
                                 SocketFlags.None,
                                 ref m_ListenEndPoint,
                                 new AsyncCallback(ReceiveData), 
                                 m_udpLocalClient);
    }
    
    public void TimerCallback(object sender, ElapsedEventArgs e)
    {
      byte[] Data = new byte[m_BytesReceived];
    
      m_Timer.Stop();
    
      m_Mutex.WaitOne();
      m_Buffer.Get(Data);
      m_Mutex.ReleaseMutex();
    
      //Send Packets lo LocalHost EndPoint
      m_udpLocalClient.Send(Data, Data.Length, m_LocalEndPoint);
    
    }    
    
    void ReceiveData(IAsyncResult iar)
    {
      int recv = 0;
    
      try
      {
        UdpClient udpReceiver = (UdpClient)iar.AsyncState;
    
        recv = udpReceiver.Client.EndReceiveFrom(iar, ref m_ListenEndPoint);
        if (recv == 0)  return;
    
        //Forward packets on the FwEndpoint
        m_udpFwClient.Send(m_data, recv, m_FwEndPoint);
    
        //Send Packets lo LocalHost EndPoint
        //m_udpLocalClient.Send(m_data, recv, m_LocalEndPoint);
    
        m_Mutex.WaitOne();
        m_Buffer.Put(m_data);
        m_Mutex.ReleaseMutex();
    
        if (!m_Timer.Enabled) m_Timer.Start();
    
        udpReceiver.Client.BeginReceiveFrom(m_data, 
                                            0,
                                            m_data.Length, 
                                            SocketFlags.None,
                                            ref m_ListenEndPoint,
                                            new AsyncCallback(ReceiveData),
                                            udpReceiver);
      }
      catch (Exception e)
      {
      }
    
    }    

    NOTE. The m_Buffer member is a CircularBuffer from CodePlex.

    The problem here is that the call to:

    m_udpLocalClient.Send(Data, Data.Length, m_LocalEndPoint);

    into the TimerCallBack seems to not have effect, while if I uncomment the same one into the ReceiveData it works.

    I also try to use the async method BeginSend() but with no success. Where I wrong?

    Regards,
    Daniele.

    Tuesday, June 19, 2012 2:16 PM

All replies

  • As suggested to me, at the time the timer is executing, I've already told the socket to wait for more data to come in, so it is blocked.

    So I try to use another UdpClient to send out datagrams but this still not work:

    public void Start()
    {
       .....
       m_udpFwClient = new UdpClient();
       m_udpLocalSender = new UdpClient();
       m_udpLocalReceiver = new UdpClient(AddressFamily.InterNetwork);
       ....
    }
    
    public void TimerCallback(object sender, ElapsedEventArgs e)
    {
      byte[] Data = new byte[m_BytesReceived];
    
      m_Timer.Stop();
    
      m_Mutex.WaitOne();
      m_Buffer.Get(Data);
      m_Mutex.ReleaseMutex();
    
      //Send Packets lo LocalHost EndPoint
      m_udpLocalSender.Send(Data, Data.Length, m_LocalEndPoint);
    
    } 


    So I try to change the code to call the UdpClient.Send() before to put the socket in the receiving state but neither this works:

    public void TimerCallback(object sender, ElapsedEventArgs e)
    {
      m_Timer.Stop();
      m_TimerRaised = true;
    }
    
    
    void ReceiveData(IAsyncResult iar)
    {
    
      try
      {
        UdpClient udpLocalClient = (UdpClient)iar.AsyncState;
    
        recv = udpLocalClient.Client.EndReceiveFrom(iar, ref m_ListenEndPoint);
        if (recv == 0)  return;
    
        //Forward packets on the FwEndpoint
        m_udpFwClient.Send(m_data, recv, m_FwEndPoint);
    
        m_Mutex.WaitOne();
        m_Buffer.Put(m_data);
        m_Mutex.ReleaseMutex();
    
        if (!m_Timer.Enabled) 
        {
          if (!m_TimerRaised)
          {
            m_Timer.Start();
            Debug.WriteLine("HandleReceiveData -> StartTimer");
          }
          else
          {
            byte[] Data = new byte[m_BytesReceived];
    
            m_Mutex.WaitOne();
            m_Buffer.Get(Data);
            m_Mutex.ReleaseMutex();
    
            udpLocalClient.Send(Data, Data.Length, m_LocalEndPoint);
    
            m_TimerRaised = false;
          }
        }
    
        udpLocalClient.Client.BeginReceiveFrom(m_data, 
                                            0,
                                            m_data.Length, 
                                            SocketFlags.None,
                                            ref m_ListenEndPoint,
                                            new AsyncCallback(ReceiveData),
                                            udpLocalClient );
      }
      catch (Exception)
      {
      }
    
    }


    Tuesday, June 19, 2012 3:16 PM
  • I not sure why you need to wait between sending to the two endpoints.  I think understanding the requirement better might help solve the problem.

    A connection consists of 3 items

    1) A Source IP address

    2) A Destination IP address

    3) A port number

    YOu can't have two connections at the same time with all three parameters being the same.

    When you make a connection between two computers you need to do two things

    1)  Create an endpoint which is the source IP address of the connection

    2) Make a connection to another computer.  to make a connection in the Net Library yhou either need to use the connect Method() or use the Listen method().  The listen method makes a connection and sets up the client to recevie messages.

    I don't see in your code where you are making the connection (or listening).  If you are using the same client for both connections you have to close the first connection and then establish the 2nd connection.  If you use two clients then you don't have to close the connection.  When you use two client you need to use two different post number, otherwise, you will get an error when you set your endpoint (source IP address) becaue the port number is already being used.


    jdweng

    Tuesday, June 19, 2012 5:16 PM
  • Come on Joel. :-(  The program is clearly using UDP, there are no connection.

    http://www.alanjmcf.me.uk/ Please follow-up in the newsgroup. If I help, please vote and/or mark the question answered. Available for contract programming.

    Tuesday, June 19, 2012 8:36 PM
  • Is the timer method definately getting run? Have you put a breakpoint on it and checked?

    Is the destination of the second copy the same machine??

    ((Why are you using a mutex rather than a Monitor?


    http://www.alanjmcf.me.uk/ Please follow-up in the newsgroup. If I help, please vote and/or mark the question answered. Available for contract programming.

    Tuesday, June 19, 2012 8:41 PM
  • Alan: UDP does make a connection, you just don't get the ACK that you would in TCP.  Even with Multicast you can't have two listeners on the same IP address registering for the same Port Number.  I'm not 100% sure in windows, but with multicast listener is still makes a virtual connection using two endpoints (the IP address of the computer and the loopback address).  When I was developing multicast on an embedded processor we used a virtual connection for multicast listener to keep the socket code common for TCP, UDP, and UDP multicast.

    jdweng

    Wednesday, June 20, 2012 1:43 AM
  • Hi Joel and Alan,
    first of all thanks for your replies!

    You can't have two connections at the same time with all three parameters being the same.

    I perfectly know this, for me was implied! :-)

    Is the destination of the second copy the same machine??

    Yes, it is.

    Why are you using a mutex rather than a Monitor?

     Ehm...maybe because when I code in plain c++ I'm habituated to use it! :-)

    Ok, let me explain what I try to do (maybe its not a "great" solution). 

    I have and audio RTP stream (a multicast on UDP) and I have to split (forward) it into two other streams (without the need to go inside the RTP details):

                                  | -> 224.1.13:7000
    224.1.13:8000 --->|
                                  | -> 127.0.0.1:9000

    The two streams are play-backed simultaneously but they are not in sync (maybe due to my repeater code), and so I put a timer between them to try to adjust them.

    Now, regarding the last piece of code I posted, I put a Debug after the Send() call:

    int bytesSent = udpLocalClient.Send(Data, Data.Length, m_LocalEndPoint);
    Debug.WriteLine("Send " + bytesSent + " to " + m_LocalEndPoint.ToString());

    and the output is :

    Send = 8192 to 127.0.0.1:9000

    but, on this connection (where another software is listening) no data arrives!
    I want to notice that (like I said in the first post) the following code, without the timer, works (apart from the fact that they are not in sync):

    void ReceiveData(IAsyncResult iar)
    {
    
      try
      {
        UdpClient udpLocalClient = (UdpClient)iar.AsyncState;
    
        m_BytesReceived = udpLocalClient.Client.EndReceiveFrom(iar, ref m_ListenEndPoint);
        if (m_BytesReceived == 0) return;
    
        //Invio i pacchetti verso l'endpoint del Client
        m_udpFwClient.Send(m_data, m_BytesReceived, m_FwEndPoint);
    
        //Invio i pacchetti verso gli stream in ascolto per questo programma
        udpLocalClient.Send(m_data, recv, m_LocalEndPoint);
    
        udpLocalClient.Client.BeginReceiveFrom(m_data, 
                                            0,
                                            m_data.Length, 
                                            SocketFlags.None,
                                            ref m_ListenEndPoint,
                                            new AsyncCallback(ReceiveData),
                                            udpLocalClient);
      }
      catch (Exception)
      {
      }
    
    }


    Thanks again for your attention!
    Daniele.

    Wednesday, June 20, 2012 7:37 AM
  • There are few things I would try

    1)  Run the dos Command IPCONFIG /all.  Verify the mask is set correctly.  You may have more than one ethernet card and the data is being blocked by a mask.  A PC with more then one ethernet card you need to get the correct endpoint (each ethernet card has it own endpoint).  If you connect to to the wrong endpoint you will not received any data.  Normal practive with Mutlicast is the recevie should use the Loopback address 127.0.0.1 as the end point.

    2) Run wireshark to help determine where the problem is located.  Verify if the problem is on the send or recevie end of the connection.

    3) If there is any routers in the path make sure the Multicast forwarding is set, otherwise, the routers will not forward the multicast messages.


    jdweng

    Wednesday, June 20, 2012 9:07 AM
  • 1)  Run the dos Command IPCONFIG /all.  Verify the mask is set correctly.  You may have more than one ethernet card and the data is being blocked by a mask.  A PC with more then one ethernet card you need to get the correct endpoint (each ethernet card has it own endpoint).  If you connect to to the wrong endpoint you will not received any data.  Normal practive with Mutlicast is the recevie should use the Loopback address 127.0.0.1 as the end point.

    In this PC I have only one eth card (exept the VirtualBox one which is disabled).
    For testing, I'm using another application binded on 127.0.0.1:9000 on which I expect to receive packets sent to this endpoint.

    2) Run wireshark to help determine where the problem is located.  Verify if the problem is on the send or recevie end of the connection.

    I try to use wireshark but (as I know) it cannot snif packets sent to localhost.

    3) If there is any routers in the path make sure the Multicast forwarding is set, otherwise, the routers will not forward the multicast messages.

    Routers are not a problem, multicast works.

    I repeat, If I use the last code I posted the packets are sent, so I think there is no network settings mistakes.
    I also try to use the lock keyword instead of Mutex, but it has not changed the situation.

    What I cannot understand is why the Send() seems to do its work but the packets doesn't arrive at destination!

    Regards,
    Daniele.

    Wednesday, June 20, 2012 9:41 AM
  • I would try to simplify the code and just write a simple receive method the just listens for the packets to make sure that part of the code works.

    Wireshark should be able to trace virtual connections inside a PC as well as external connections.


    jdweng

    Wednesday, June 20, 2012 9:52 AM
  • Just a quick note that you have to aware the data size received by UdpClient is incorrectly padded with nulls up to current received data size. See this article for more information.

    EDIT: Oops, and that's apparently for .NET v1.1... You can check to see if the problem still exist. (I have no idea it's fixed or not now)

    • Edited by cheong00 Wednesday, June 20, 2012 9:57 AM
    Wednesday, June 20, 2012 9:53 AM
  • Hi,

    I made a lot of tests...and there is something strange...

    Look at the following code (where I leave only the localhost send):

    void ReceiveData(IAsyncResult iar)
    {      
      int byteSent = 0;
      UdpClient udpReceiver = null;
    
      try
      {
        udpReceiver = (UdpClient)iar.AsyncState;
    
        m_BytesReceived = udpReceiver.Client.EndReceiveFrom(iar, ref m_ListenEndPoint);    
        if (m_BytesReceived == 0) return;
    
        lock (m_Buffer)
        {
          m_Buffer.Write(m_data, m_BytesReceived);
        }
    
        if (++m_debug_counter >= 10)
        {
          lock (m_Buffer)
          {
            m_dataBuffered = m_Buffer.Read(m_BytesReceived);
            byteSent = udpReceiver.Send(m_dataBuffered, m_BytesReceived, m_LocalEndPoint);                    
          }          
        }
      }
      catch (System.Net.Sockets.SocketException es)
      {
        Debug.WriteLine("Exception " + es.Message);
      }
      catch (Exception e)
      {
        Debug.WriteLine("Exception " + e.Message);
      }
      finally
      { 
        udpReceiver.Client.BeginReceiveFrom(m_data,
                                            0,
                                            m_data.Length,
                                            SocketFlags.None,
                                            ref m_ListenEndPoint,
                                            new AsyncCallback(ReceiveData),
                                            udpReceiver);      
      }
    }

    where the buffer members are defined as 

    const int BUFFER_SIZE = 8192;
    
    private byte[] m_data = new byte[BUFFER_SIZE];
    private byte[] m_dataBuffered = new byte[BUFFER_SIZE * 2];

    Ok, now look at the m_debug_counter variable (here used to delay the Send() call).....if I put a value from 1 to 7 all works, if I put a value >= 8 I have the mistake: the packets seems to be sent but I cannot get them on the receiver side.

    Where is the mistake?!

    Regards,
    Daniele.

    Wednesday, June 20, 2012 3:17 PM
  • What firewall software is installed on the PC?


    http://www.alanjmcf.me.uk/ Please follow-up in the newsgroup. If I help, please vote and/or mark the question answered. Available for contract programming.

    Thursday, June 21, 2012 10:49 AM
    1. The UDP protocol does NOT use connections.
    2. "yhou either need to use the connect Method() or use the Listen method()."  What!!!

    http://msdn.microsoft.com/en-us/library/system.net.sockets.udpclient.aspx "The UdpClient class provides simple methods for sending and receiving connectionless UDP datagrams in blocking synchronous mode. Because UDP is a connectionless transport protocol, you do not need to establish a remote host connection prior to sending and receiving data."


    http://www.alanjmcf.me.uk/ Please follow-up in the newsgroup. If I help, please vote and/or mark the question answered. Available for contract programming.

    Thursday, June 21, 2012 10:52 AM
  • Hi,

    I'm really sorry if I waste your time, but maybe I found the problem.
    Using wireshark I noted that the packets forwarded are not the same as the packet received!!

    So I change the way I buffer packets and now it seems to work!

    My great fault was to use (as a receiver application) an RTP player.
    Since the packets was not correctly sent, the RPT payload was broken and my player (obviously) does not recognize nothing!

    Sorry again and thanks a lot four your time!

    Daniele.

    Thursday, June 21, 2012 12:01 PM
  • Ahh. :-) We should have asked what the local receiver was.

    http://www.alanjmcf.me.uk/ Please follow-up in the newsgroup. If I help, please vote and/or mark the question answered. Available for contract programming.

    Thursday, June 21, 2012 12:03 PM