0

I'm trying to send a UDP command to a device and receive a UDP response from that same device. The sending works fine. I can see the datagram depart (via WireShark). I can also see the datagram return from the device (again, via WireShark). The turnaround time between command departure and response reception is about 15 milliseconds.

Code

Byte[] button_click(Byte[] command) 
{
    // Device exists at a particular IP address and listens for UDP commands on a particular port
    IPEndPoint SendingEndpoint = new IPEndPoint(DEVICE_IP, DEVICE_PORT);

    // Device always sends from port 32795 to whatever port the command originated from on my machine
    IPEndPoint ReceivingEndpoint = new IPEndPoint(DEVICE_IP, 32795);

    // Sending client
    sendingClient = new UdpClient();
    sendingClient.Connect(SendingEndpoint);

    // Receiving client
    receivingClient = new UdpClient();
    receivingClient.Client.ReceiveTimeout = RECEIVE_TIMEOUT; // timeout after 4 seconds
    receivingClient.Connect(receivingEndpoint);

    // Send command and wait for response
    Byte[] response = null;
    try
    {
        sendingClient.Connect(DEVICE_IP, DEVICE_PORT);
        sendingClient.Send(command, command.Length);
        response = receivingClient.Receive(ref receivingEndpoint);
    }
    catch (SocketException e)
    {
        // If we timeout, discard SocketException and return null response
    }

    return response;
}

Problem

I cannot capture the received datagram in my application. When I run the above code, I get the following 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."

There are similar posts on StackOverflow, but none of them appear to address my situation. And I've verified that my packets are not being swept up in my firewall.

What am I doing wrong?

Alex Johnson
  • 958
  • 8
  • 23

2 Answers2

0

If you use the sendingClient to receive, then you can get the proper message. The reason is that the IP is consisted of Host+Port+Protocol, when the sending point connect to the device and send the message, the device receive the Endpoint and the UDP paired with the sending Endpoint. When receive client try to receive the message, there is nothing happen since UDP is Peer to Peer, and the receive client's port must be different to the send client, as a result, receive client get nothing. The following is my sample code for your reference.

        IPAddress address;
        IPAddress.TryParse("127.0.0.1", out address);
        IPEndPoint recPoint = new IPEndPoint(address, 13154);
        // IPEndPoint sendPoint = new IPEndPoint(address, 9999);
        UdpClient send = new UdpClient(9999);
        send.Connect(recPoint);
        Byte[] response = null;
        Byte[] command = System.Text.Encoding.Default.GetBytes("NO one");
        try
        {
            send.Send(command, command.Length);
            response = send.Receive(ref recPoint);
        }
        catch(Exception ex) {
            Console.WriteLine(ex.ToString());
        }

According to Alex's answer, I update a full example code for reference.

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Net;

namespace console
{
    class Program
    {
        static void Main(string[] args)
        {
            IPAddress address;
            IPAddress.TryParse("192.168.14.173", out address);
            IPEndPoint recPoint = new IPEndPoint(address, 13154);
            IPEndPoint recAnyPoint = new IPEndPoint(IPAddress.Any, 13154);
            IPEndPoint ipPoint = new IPEndPoint(IPAddress.Parse("192.168.14.174"), 13154);

            // IPEndPoint sendPoint = new IPEndPoint(address, 9999);
            UdpClient send = new UdpClient();
            send.ExclusiveAddressUse = false;
            // no need to use the low level socketoption 
            // send.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
            send.Client.Bind(recAnyPoint);
            send.Connect(ipPoint);
            UdpClient receive = new UdpClient();

            receive.ExclusiveAddressUse = false;
            // receive.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
            receive.Client.Bind(recPoint);
            receive.Connect(ipPoint);
            Byte[] response = null;
            Byte[] command = System.Text.Encoding.Default.GetBytes("NO one");
            try
            {
                send.Send(command, command.Length);
                response = receive.Receive(ref ipPoint);
                Console.WriteLine(System.Text.Encoding.Default.GetString(response));
            }
            catch(Exception ex) {
                Console.WriteLine(ex.ToString());
            }
        }
    }
}
FrankX
  • 167
  • 1
  • 7
  • Thanks for your response. Sending and receiving clients can definitely live on the same port (which is a good thing, because my device requires it). I'll explain in my own answer. – Alex Johnson Sep 14 '18 at 16:12
  • yes, it can live on same port, in your example, you use IP + port to differ from each, well, I haven't think of this trick. – FrankX Sep 17 '18 at 00:53
0

I solved the problem. The solution required two things:

  1. The sending and receiving clients had to use the same local port
  2. The sending client had to use an IPEndPoint declared with IPAddress.Any and the receiving client had to use an IPEndPoint declared with the exact IP address of my local machine

Code

// Create endpoints
IPEndPoint DeviceEndPoint = new IPEndPoint(DEVICE_IP, DEVICE_PORT);
IPEndPoint localEndPointAny = new IPEndPoint(IPAddress.Any, LOCAL_PORT); // helps satisfy point 2
IPEndPoint localEndPointExplicit = new IPEndPoint(IPAddress.Parse(GetLocalIPAddress()), LOCAL_PORT);  // helps satisfy point 2
IPEndPoint incomingEndPoint = null; // Later populated with remote sender's info

// Create sending client
UdpClient sendingClient = new UdpClient();
sendingClient.ExclusiveAddressUse = false; // Going to use same port for outgoing and incoming (helps satisfy point 1)
sendingClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); // helps satisfy point 1
sendingClient.Client.Bind(localEndPointAny); // Any outgoing IP address will do

// Create receiving client
UdpClient receivingClient = new UdpClient();
receivingClient.Client.ReceiveTimeout = RECEIVE_TIMEOUT; // 4000 milliseconds
receivingClient.ExclusiveAddressUse = false; // Going to use same port for outgoing and incoming (helps satisfy point 1)
receivingClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); // helps satisfy point 1
receivingClient.Client.Bind(localEndPointExplicit); // Must explicitly give machine's outgoing IP address

The code for getting a local IP address can be found here.

Alex Johnson
  • 958
  • 8
  • 23