0

I am trying to ping one of my servers using C# Ping to see whether it is available or not, but I am receiving a weird error and I'm not sure what's causing it

On first execution of the code, it returns an error but states that the operation was completed successfully

System.Net.Sockets.SocketException (0x80004005): The operation completed successfully.\r\n\r\n at System.Net.Sockets.Socket..ctor (System.Net.Sockets.AddressFamily addressFamily, System.Net.Sockets.SocketType socketType, System.Net.Sockets.ProtocolType protocolType) [0x00069]

Each time the code is executed after the first, it will return a different error.

"An attempt was made to access a socket in a way forbidden by its access permissions."

System.Net.Sockets.SocketException (0x80004005)

The code I am using to ping is as follows which is pretty much pulled straight from MSDN. https://msdn.microsoft.com/en-us/library/system.net.networkinformation.pingreply(v=vs.110).aspx

  public static bool pingHost(string nameOrAddress)
    {
        bool pingable = false;

        if (nameOrAddress == "127.0.0.1")
        {
            return true; 
        }

        try
        {
            System.Net.NetworkInformation.Ping pingSender = new System.Net.NetworkInformation.Ping();
            PingOptions options = new PingOptions();

            // Use the default Ttl value which is 128,
            // but change the fragmentation behavior.
            options.DontFragment = true;

            // Create a buffer of 32 bytes of data to be transmitted.
            string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
            byte[] buffer = Encoding.ASCII.GetBytes(data);
            int timeout = 3;//3 seconds?
            PingReply reply = pingSender.Send(nameOrAddress, timeout);
            if (reply.Status == IPStatus.Success)
            {
                pingable = true;
            } else
            {
                pingable = false;
            }
        }
        catch (PingException e)
        {
            Debug.Log("Error pinging: " + e.Message + " - " + e.StackTrace);
            // Discard PingExceptions and return false;
        }
        return pingable;
    }

I am able to ping the server from my terminal on my machine, but not through code. Why am I receiving this error?


EDIT

I have tested the code in a standard C# application and it works fine. This issue must be something related to Unity.

Unity has its own Ping class but can't be called from outside the main thread. The code below tries to utilise this, but it always returns false and -1 for the time.

Ping should definitely complete within 3 seconds? I have tried increasing this time to 10 seconds but still returns false. The address is definitely pingable as I can ping it from the terminal.

IEnumerator ping()
    {
        while (true)
        {
            UnityEngine.Ping pinger = new UnityEngine.Ping("ADDRESS");

            yield return new WaitForSeconds(3.0f);

            Debug.Log("Ping finished? " + pinger.isDone + " - " + pinger.time);

            yield return new WaitForSeconds(2.0f);
        }

    }

As this doesn't work, my next question is how does the Unity ping actually work? My servers are in a secure environment with only specific open ports. If it is doing something unexpected other than the standard ping, it may not be able to.


EDIT 2

 IEnumerator ping()
{
    while (true)
    {
        WaitForSeconds f = new WaitForSeconds(0.05f);
        Ping p = new Ping("ADDR");

        while (!p.isDone)
        {
            yield return f;
        }
        PingFinished(p);

    }

}

This code seems to work nicely, but only works from the main thread. I will need a solution that is able to be started from a separate task or thread.

jjmcc
  • 795
  • 2
  • 11
  • 27
  • Is your code running in a reduced security context or a sandbox? – Ron Beyer Apr 06 '18 at 14:13
  • @RonBeyer Not that I'm aware of. I'm using the Unity game engine and this is running on a separate thread. This is also running on my home machine with admin privileges. – jjmcc Apr 06 '18 at 14:14
  • Try setting your timeout to `3000` instead of `3`, the value is in milliseconds. – Ron Beyer Apr 06 '18 at 14:16
  • @RonBeyer Strange, I've just realised that the first time the code runs it returns an error, but says `System.Net.Sockets.SocketException (0x80004005): The operation completed successfully.`. Each time after that, it returns the permissions error. The timeout increase also didn't fix the issue, but thanks for pointing it out. – jjmcc Apr 06 '18 at 14:19
  • https://stackoverflow.com/a/11804416/4645236 – M Y Apr 06 '18 at 14:27
  • @hellyale I have tried. I've just edited my post and it seems to be a problem with Unity rather than the code provided as this code works fine in a standard C# console application. – jjmcc Apr 06 '18 at 14:30
  • Is there a reason you are using `System.Net.NetworkInformation.Ping` instead of `UnityEngine.Ping`? I am not sure if `UnityEngine.Ping` can run off the main thread, but if Unity's Ping works, you can set up a locking mechanism on your worker thread to wait for it to finish on the main thread. – Ed Marty Apr 06 '18 at 14:35
  • @EdMarty No reason, other than the examples I saw on the documentation used `System.Net.NetworkInformation.Ping`. I will try the `UnityEngine.Ping` – jjmcc Apr 06 '18 at 14:38
  • @EdMarty Pinging with the `UnityEngine.Ping` doesn't seem to work, unless I'm doing it wrong? I have added some code to my OP. – jjmcc Apr 06 '18 at 15:00
  • @EdMarty Would you mind posting an example of how I can create this locking mechanism so that the ping is performed on the main thread, but started from a separate task/thread. I'll accept that as the answer. – jjmcc Apr 06 '18 at 16:00
  • Why do you need to ping from another Thread? – Programmer Apr 08 '18 at 10:02

2 Answers2

0

This seems to work, but only from the main thread.

 IEnumerator ping()
    {
        while (true)
        {
            WaitForSeconds f = new WaitForSeconds(0.05f);
            Ping p = new Ping("ADDR");

            while (!p.isDone)
            {
                yield return f;
            }
            PingFinished(p);

        }

    }
jjmcc
  • 795
  • 2
  • 11
  • 27
0

Here's a general idea of how to do it on/off the main thread:

(Caution: untested)

using System.Threading;
class Test : MonoBehaviour {
    Ping result;
    private object pingLock;

    void Start() {
        pingLock = new object();
        new Thread(ThingRunningOffMainThread).Start();
        StartCoroutine(DoPing());
    }

    IEnumerator DoPing()
    {
        WaitForSeconds f = new WaitForSeconds(0.05f);
        Ping p = new Ping("ADDR");

        while (!p.isDone)
        {
            yield return f;
        }
        Monitor.Enter(pingLock);
        result = p;
        Monitor.Pulse(pingLock);
        Monitor.Exit(pingLock);
    }


    void ThingRunningOffMainThread() {
        if (result == null) {
            Monitor.Enter(pingLock);
            while (result == null) {
                Monitor.Wait(pingLock);
            }
            Monitor.Exit(pingLock);
        }
        PingFinished(pingLock);
    }
}

To invoke from a background thread to the main thread, look at Main Thread Dispatchers, for example:

https://github.com/PimDeWitte/UnityMainThreadDispatcher/blob/master/UnityMainThreadDispatcher.cs

Ed Marty
  • 39,590
  • 19
  • 103
  • 156