0

Is there a way to have a simple webserver send messages to Unity?

At the moment, we're doing a GET using UnityWebRequest.Get() in the update method. Here's the code:

// Update is called once per frame
void Update () {
    StartCoroutine(GetData());
}

IEnumerator GetData()
{
    UnityWebRequest uwr = UnityWebRequest.Get(url);
    yield return uwr.Send();

    if (uwr.isError)
    {
        Debug.Log(uwr.error);
    }else
    {
        Debug.Log((float.Parse(uwr.downloadHandler.text) / 100));
        fnumber = ((float.Parse(uwr.downloadHandler.text) / 100));
        transform.position.Set(oldX, fnumber, oldZ);
    }
}

However, this throws this error:

CANNOT RESOLVE DESTINATION HOST

I found this bug-report, which states, it would have been fixed, which doesn't seem so.

So, is there a way to have the server send messages to Unity?

Thanks

Reaper
  • 740
  • 1
  • 7
  • 18
  • Why don't you just open up socket connection on a different thread and feed your Unity's thread with some delegates? – mrogal.ski Mar 31 '17 at 09:32
  • Sorry, could you explain this a little deeper? Would be great. – Reaper Mar 31 '17 at 09:33
  • Just create a `Thread` and use `Socket` inside to connect to your web server. Then in your `Thread`' s main method simply send/receive data and transmit them to other parts of your application. – mrogal.ski Mar 31 '17 at 09:54
  • Why would you need to call this in update? You'll run like hundreds of coroutines at the same time. This can be a reason. – Vladyslav Melnychenko Mar 31 '17 at 10:23
  • Because we need to get the data as often as possible. – Reaper Mar 31 '17 at 11:01
  • Check m.rogalski's answer. That will likely solve your problem. What happens when you call that function from the Start function only. Is it throwing the-same error? – Programmer Mar 31 '17 at 18:53

3 Answers3

3

Expanding my comment to be more descriptive and concrete, my suggestion is to create a simple Thread that will serve as the communication manager with your server.

First of all you have to implement this class to call methods on Unity's UI thread.

When you implement this just create a class :

public class CommunicationManager
{
    static readonly object syncRoot = new object();

    static CommunicationManager _Instance;
    public static CommunicationManager Instance
    {
        get
        {
            if ( _Instance == null )
            {
                lock ( syncRoot )
                {
                    if ( _Instance == null )
                    {
                        _Instance = new CommunicationManager();
                    }
                }
            }
            return _Instance;
        }
    }

    volatile bool working = false;

    Queue<Message> _messages;

    private CommunicationManager()
    {
        _messages = new Queue<Message>();
        InitializeCommunication();
    }

    void InitializeCommunication()
    {
        Thread t = new Thread(CommunicationLoop);
        t.Start();
    }

    void CommunicationLoop()
    {
        Socket s = null;
        try
        {
            Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            s.Connect(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 1337));
            working = true;
        }
        catch(Exception ex)
        {
            working = false;
        }
        while ( working )
        {
            lock ( syncRoot )
            {
                while ( _messages.Count > 0 )
                {
                    Message message = _messages.Dequeue();
                    MessageResult result = message.Process(s);
                    result.Notify();
                }
            }
            Thread.Sleep(100);
        }
    }

    public void EnqueueMessage(Message message)
    {
        lock ( syncRoot )
        {
            _messages.Enqueue( message );
        }
    }
}

Now you have to make Message and MessageResult objects :

public class Message
{
    const string REQUEST_SCHEME = "{0} {1} HTTP/1.1\r\nHost: {hostname}\r\nContent-Length: 0\r\n\r\n";
    string request;
    Action<MessageResult> whenCompleted;

    public Message(string requestUri, string requestType, Action<MessageResult> whenCompleted)
    {
        request = string.Format(REQUEST_SCHEME, requestType, requestUri);
        this.whenCompleted = whenCompleted;
    }

    public MessageResult Process(Socket s)
    {
        IPEndPoint endPoint = (IPEndPoint)s.RemoteEndPoint;
        IPAddress ipAddress = endPoint.Address;
        request = request.Replace("{hostname}", ipAddress.ToString());
        s.Send(Encoding.UTF8.GetBytes(request));

        // receive header here which should look somewhat like this :
        /*
                HTTP/1.1 <status>
                Date: <date>
                <some additional info>
                Accept-Ranges: bytes
                Content-Length: <CONTENT_LENGTH>
                <some additional info>
                Content-Type: <content mime type>
         */
         // when you receive this all that matters is <CONTENT_LENGTH>
         int contentLength = <CONTENT_LENGTH>;
         byte[] msgBuffer = new byte[contentLength];
         if (s.Receive(msgBuffer) != contentLength )
         {
             return null;
         }

         return new MessageResult(msgBuffer, whenCompleted);
    }
}

And lastly create MessageResult object :

public class MessageResult
{
    Action<MessageResult> notifier;
    byte[] messageBuffer;

    public MessageResult(byte[] message, Action<MessageResult> notifier)
    {
        notifier = notifier;
        messageBuffer = message;
    }

    public void Notify()
    {
        UnityThread.executeInUpdate(() =>
        {
            notifier(this);
        });
    }
}

This method will run aside from your main application so that wont produce any freezes and whatever.

To use this you can just call something like this :

Message message = new Message("/index.html", "GET", IndexDownloaded);
CommunicationManager.Instance.EnqueueMessage(message);

// ...
public void IndexDownloaded(MessageResult result)
{
    // result available here
}

For more informations read this wikipedia article about HTTP.

Community
  • 1
  • 1
mrogal.ski
  • 5,828
  • 1
  • 21
  • 30
  • I like this. Is this the only workaround? Instead of rolling your own webrequest, Can't `HttpWebRequest` solve this problem? – Programmer Mar 31 '17 at 18:47
  • I think you can use `HttpWebRequest` too but haven't checked that so I couldn't use it in my answer. :) – mrogal.ski Mar 31 '17 at 19:15
0

This is simple way to get request from RESTful server :D.

private string m_URL = "https://jsonblob.com/api/d58d4507-15f7-11e7-a0ba-014f05ea0ed4";

IEnumerator Start () {
    var webRequest = new WWW (m_URL);
    yield return webRequest;
    if (webRequest.error != null) {
        Debug.Log (webRequest.error);
    } else {
        Debug.Log (webRequest.text);
    }
}
Cổ Chí Tâm
  • 330
  • 1
  • 7
-3

Try to use the WWW call instead the UnityWebRequest which is broken. What version of unity are you using ?

mrogal.ski
  • 5,828
  • 1
  • 21
  • 30