Skip to content

Instantly share code, notes, and snippets.

@amimaro
Last active December 18, 2023 12:31
  • Star 21 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save amimaro/10e879ccb54b2cacae4b81abea455b10 to your computer and use it in GitHub Desktop.
Listen for Http Requests with Unity
using UnityEngine;
using UnityEngine.Networking;
using System;
using System.IO;
using System.Net;
using System.Threading;
public class UnityHttpListener : MonoBehaviour
{
private HttpListener listener;
private Thread listenerThread;
void Start ()
{
listener = new HttpListener ();
listener.Prefixes.Add ("http://localhost:4444/");
listener.Prefixes.Add ("http://127.0.0.1:4444/");
listener.AuthenticationSchemes = AuthenticationSchemes.Anonymous;
listener.Start ();
listenerThread = new Thread (startListener);
listenerThread.Start ();
Debug.Log ("Server Started");
}
void Update ()
{
}
private void startListener ()
{
while (true) {
var result = listener.BeginGetContext (ListenerCallback, listener);
result.AsyncWaitHandle.WaitOne ();
}
}
private void ListenerCallback (IAsyncResult result)
{
var context = listener.EndGetContext (result);
Debug.Log ("Method: " + context.Request.HttpMethod);
Debug.Log ("LocalUrl: " + context.Request.Url.LocalPath);
if (context.Request.QueryString.AllKeys.Length > 0)
foreach (var key in context.Request.QueryString.AllKeys) {
Debug.Log ("Key: " + key + ", Value: " + context.Request.QueryString.GetValues (key) [0]);
}
if (context.Request.HttpMethod == "POST") {
Thread.Sleep (1000);
var data_text = new StreamReader (context.Request.InputStream,
context.Request.ContentEncoding).ReadToEnd ();
Debug.Log (data_text);
}
context.Response.Close ();
}
}
@lejeanf
Copy link

lejeanf commented Jan 12, 2023

To get arround that problem I randomize the port on Awake. you can send an event with the new port used so that whatever script who needs it can grab the event.

@vitosbat
Copy link

vitosbat commented Jan 12, 2023

I found this solution for me now - just added to Start method:

EditorApplication.playModeStateChanged += delegate(PlayModeStateChange state) 

        {
            if (state == PlayModeStateChange.ExitingPlayMode)
            {
                listener.Stop();
                listenerThread.Abort();
                Debug.Log("Server stoped");
            }
        };

But recommend to add checking of listening to StartListener method, cause it throws an error at finish playmode:

while (true)
        {
            if (listener.IsListening)
            {
                var result = listener.BeginGetContext(ListenerCallback, listener);
                result.AsyncWaitHandle.WaitOne();
            }
        }

@PikaChokeMe
Copy link

Would it not be better or possible to do this with a coroutine instead of an entirely separate System.Thread?

@PikaChokeMe
Copy link

using UnityEngine;
using UnityEngine.Networking;
using System;
using System.IO;
using System.Net;
using System.Threading.Tasks;

public class UnityHttpListener : MonoBehaviour
{

  private HttpListener listener;

  void Start()
  {
    listener = new HttpListener();
    listener.Prefixes.Add("http://localhost:4444/");
    listener.Prefixes.Add("http://127.0.0.1:4444/");
    listener.AuthenticationSchemes = AuthenticationSchemes.Anonymous;
    listener.Start();
    
    StartCoroutine("startListenerCoroutine");
		
    Debug.Log("Server Started");
  }

  private System.Collections.IEnumerator startListenerCoroutine()
  {
    while(listener.IsListening)
    {
      Task<HttpListenerContext> task = listener.GetContextAsync();
      yield return new WaitUntil(() => task.IsCompleted);
      ProcessRequest(task.Result);
    }
  }

  private void ProcessRequest(HttpListenerContext context)
  {
    Debug.Log("Method: " + context.Request.HttpMethod);
    Debug.Log("LocalUrl: " + context.Request.Url.LocalPath);

    if (context.Request.QueryString.AllKeys.Length > 0) {
      foreach (var key in context.Request.QueryString.AllKeys) {
        Debug.Log ("Key: " + key + ", Value: " + context.Request.QueryString.GetValues(key)[0]);
      }
    }

    if (context.Request.HttpMethod == "POST") {	
      var data_text = new StreamReader (context.Request.InputStream, context.Request.ContentEncoding).ReadToEnd();
      Debug.Log (data_text);
    }

    context.Response.StatusCode = 200;
    context.Response.StatusCode = "OK";
    context.Response.Close ();
  }
  
  void OnApplicationQuit() {
    listener.Stop();
    Debug.Log("Server stoped");
  }  
}

https://gist.github.com/PikaChokeMe/7604fa19596ea6f66019898a948a8dd2

I will say that I don't know the performance implications of this over the dedicated thread; however.

@TenebreGaming
Copy link

TenebreGaming commented Dec 18, 2023

ThatOtherVRGuy Check out the IPManager class in this thread on StackExchange. It will grab the IP address for you.

https://stackoverflow.com/questions/51975799/how-to-get-ip-address-of-device-in-unity-2018

You can also bind to ip address 127.0.0.1 (localhost) or simply or use a wildcard (though it's not always a good thing to bind to all IP addresses on a system.)

@TenebreGaming
Copy link

I would also like to comment on the topic of treads vs. coroutines. Coroutines are great for in-game things that you can guarantee will happen quickly and/or can be sliced neatly into sections to spread across frames. The nature of HTTP, however, cannot guarantee any sort of timing and from my experience should generally be done in a separate thread. This, of course, does depend a lot on what you're doing with it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment