Search Unity

  1. Unity 2019.1 is now released.
    Dismiss Notice

Can a Unity program read from a network stream?

Discussion in 'Connected Games' started by Dirt-e, Jan 12, 2019.

  1. Dirt-e

    Dirt-e

    Joined:
    Jan 11, 2019
    Posts:
    5
    Hey there :)


    Yes, my question really is that simple.

    I have a done a few C# projects a while ago, but this is my first time with Unity, so I think I’m overlooking something simple here: I would like my program to read data from a network stream. Just a few strings. I can make it do that by creating a TcpListener, with a few lines of C# code.

    Code (CSharp):
    1. TcpListener listener = null;
    2.  
    3. listener = new TcpListener(IPAddress.Parse(„127.0.0.1“), 48001);
    4. TcpClient client = listener.AcceptTcpClient();
    5. StreamReader reader = new StreamReader(client.GetStream());
    …that way I can read from a network stream :) awesome! But I run this code is in the start() function of an object :( probably not such a good idea.

    My problem is:
    1. Anytime I do that, the program freezes as it waits for incoming connections :-(
    2. It unfreezes ONLY when there actually IS a connection (UI unresponsive)
    3. Other objects cannot access the reader objects. Only the one in who’s start() function I have created it and it can only do so as long as it’s IN that function.
    I have solved this problem in another c# program by letting a BackgroundWorker object run the Server to keep the UI responsive, but that doesn’t seem to work in Unity, right?

    Basically, I want the reader object to be available to all other objects in the program. Or at least be able to call something like…

    Code (CSharp):
    1. if(reader.peek() != null)
    2. {
    3.     reader.readline();
    4.     //do something
    5. }
    6. Else
    7. {
    8.     //do nothing
    9. }
    …any time I like.

    Where or how shall I instantiate my TcpListener/client/reader objects to be able to use the object? Or are there other objects available to read from a network stream that I am unaware of?
     
  2. doctorpangloss

    doctorpangloss

    Joined:
    Feb 20, 2013
    Posts:
    121
    You need to accept connections and read data in a loop in a new Thread.

    As you finish reading the data, you have to put the data into a ConcurrentQueue.

    Poll the queue until it's empty in the Update function of a MonoBehaviour.
     
    Dirt-e and Joe-Censored like this.
  3. Dirt-e

    Dirt-e

    Joined:
    Jan 11, 2019
    Posts:
    5
    Thank you very much for your help :)


    That was actually the path I initially pursued. I created a thread…

    Code (CSharp):
    1. Thread t = new Thread(run_server_run);
    2. t.start();
    And inside „run_server_run()“ I could do anything I wanted :) I spun up a server, connected a client and happily send and received data :) However, none of that was available from outside the thread :-(

    My attempts at solving this were:
    1. Raising an event inside the thread and have an object outside subscribe to it. The data was contained in the event Arguments. It worked (at least in a C# WinForm application), but I still felt like a (data-)trafficker smuggling illegal immigrant-data across the heavily guarded thread-border.
    2. Pipes ?!? Didn’t pursue that too far though… just an idea.
    3. MSMQ to get the data out and have my parent process read the messages. Not really what I would call „real-time“ though. Also didn’t pursue that too far.
    So, I take it that a „concurrent queue“ is the way to go to get data out of another thread? I will read into this and see how far I get before bothering you guys with further questions.

    Greets,..

    Dirt-e
     
    Last edited: Jan 15, 2019
  4. Dirt-e

    Dirt-e

    Joined:
    Jan 11, 2019
    Posts:
    5
    Haaaa-le-lu-jaah!!!!


    It works ...!!! :)))) Awesome!

    In case some other beginner stumbles across the same problem:

    I created a game object called "ServerObject" which has a script called "server". This script creates the concurrent queue, then starts another thread (RunServerRun).
    Code (CSharp):
    1. public class Server : MonoBehaviour
    2. {
    3.     public static ConcurrentQueue<string> Queue = new ConcurrentQueue<string>();
    4.  
    5.     void Start()
    6.     {
    7.         Thread t = new Thread(RunServerRun);
    8.         t.Start();
    9.     }
    10. }

    This new thread creates the TCPListener and waits for connections. As soon as there are any, the thread will receive the data (string) and put it inside the queue. :)

    Code (CSharp):
    1. void RunServerRun()
    2.     {
    3.         TcpListener listener = new TcpListener(IPAddress.Parse("127.0.0.1"), 48001);
    4.         listener.Start();
    5.         Debug.Log("Server started.");
    6.  
    7.         while (true)
    8.         {
    9.             Debug.Log("Waiting for client connection...");
    10.  
    11.             TcpClient client = listener.AcceptTcpClient();
    12.             Debug.Log("Client connected :-)");
    13.  
    14.             StreamReader reader = new StreamReader(client.GetStream());
    15.             StreamWriter writer = new StreamWriter(client.GetStream());
    16.  
    17.             string s = string.Empty;
    18.                
    19.             while (true)
    20.             {
    21.                 s = reader.ReadLine();
    22.                 if (s == "exit")    break;
    23.                 else                Queue.Enqueue(s);
    24.             }
    25.             reader.Close();
    26.             writer.Close();
    27.             client.Close();
    28.         }
    29.     }
    Now all other objects in the program can read from that queue by calling:

    Code (CSharp):
    1. void Update()
    2.     {
    3.         if (Server.Queue.TryDequeue(out string s))
    4.         {
    5.             Debug.Log(s);
    6.         }
    7.     }
    Again,... Thanks a lot for the help!!
     
    Last edited: Jan 22, 2019
  5. doctorpangloss

    doctorpangloss

    Joined:
    Feb 20, 2013
    Posts:
    121
    Yes, that's exactly what you have to do.

    Accept connections and read data in a loop in a new Thread.

    As you finish reading the data, you have to put the data into a ConcurrentQueue.

    Poll the queue until it's empty in the Update function of a MonoBehaviour.