TCP messages can be received over localhost (in windows and editor) and to testing app on android device over local network, but not with a Unity built android app.
I am using Unity 5.6.0f3 on windows 10 to build a googleVR app for android, that waits for messages sent via TCP sockets (using the TCPListener class), from Max 7 with the mxj net.tcp.send object. This all works fine over localhost and I can receive messages sent from Unity to Max using the Max/Unity interoperability toolkit, but when I deploy the app to my phone the messages fail to send. A third-party testing app receives the messages on the phone fine and prints them to screen.
using UnityEngine; using System.Collections; using System; using System.Text; using System.Net; using System.Net.Sockets; using System.IO;
public class JitReceive : MonoBehaviour {
public int portNo;
public int maxObjects;
public int pReps = 1;
public int nPlanetOffset = 0;
public GameObject Sphere1;
public GameObject Scripts;
public GameObject rollercoaster;
//struct for pointing to various objects
private struct objectList {
public string objName;
public GameObject objPointer;
public void set(string s, GameObject gp) {
objName = s;
objPointer = gp;
}
}
private int numObjects;
private objectList[] o;
//custom calls pointer
JitCustomEvents jitCustom;
private TcpClient incoming_client;
private NetworkStream netStream;
private TcpListener server;
private bool waiting;
public string wSphere;
// Use this for initialization
void Start () {
if (portNo == 0) portNo = 32005;
if (maxObjects == 0) maxObjects = 1024;
waiting = false;
server = new TcpListener(IPAddress.Parse("192.168.0.9"), portNo);
server.Start();
numObjects = 0;
o = new objectList[maxObjects];
jitCustom = (JitCustomEvents)GetComponent("JitCustomEvents");
}
// Update is called once per frame
void Update () {
string s;
string[] values;
if (server.Pending()) {
incoming_client = server.AcceptTcpClient();
netStream = incoming_client.GetStream();
waiting = true;
}
while (waiting && netStream.DataAvailable) {
try {
int numread = 0;
byte[] tmpbuf = new byte[1024];
numread = netStream.Read(tmpbuf, 0, tmpbuf.Length);
s = Encoding.ASCII.GetString(tmpbuf, 0, numread);
s = s.Replace("\n","");
values = s.Split(';');
if (values.Length > 1) {
for (int i = 0; i < (values.Length-1); i++) {
Parse(values[i]);
}
}
else Parse(values[0]);
}
//Called when netStream fails to read from the stream.
catch (IOException e) {
waiting = false;
netStream.Close();
incoming_client.Close();
}
//Called when netStream has been closed already.
catch (ObjectDisposedException e) {
waiting = false;
incoming_client.Close();
}
}
}
void Parse(string toParse) {
GameObject target = null;
bool found = false;
int pNum = 1;
string[] values = toParse.Split (' ');
Debug.Log (values [2]);
wSphere = values [2];
pNum = int.Parse (values [3]);
if (values[1] == "1")
{
Scripts.GetComponent<loadLevel>().reLoad();
}
else if (values[0] == "2")
{
Scripts.GetComponent<InstantiateTrack>().numberOfNodes = int.Parse(values[1]);
Scripts.GetComponent<InstantiateTrack>().minX = int.Parse(values[2]);
Scripts.GetComponent<InstantiateTrack>().maxX = int.Parse(values[3]);
Scripts.GetComponent<InstantiateTrack>().minY = int.Parse(values[4]);
Scripts.GetComponent<InstantiateTrack>().maxY = int.Parse(values[5]);
Scripts.GetComponent<InstantiateTrack>().PlaceNodes();
}
else if (values[1] == "3")
{
rollercoaster.GetComponent<RollerCoasterPlanes>().enabled = true;
}
//Instantiate (Sphere1);
//nPlanetOffset = nPlanetOffset + 1;
This is the code I adapted from the Max/Unity interoperability toolkit.
Any help would be greatly appreciated :)
Answer by Icyteck01 · May 09, 2017 at 08:31 AM
LMAO dont use Update () with while do a Coroutine because while loop blocks update function
cheers, that's from code I didn't write so I guess I should tell the person who wrote it. Not sure that would solve this problem though right?
If that were the problem then the app would be freezing while waiting for the messages to come through which it doesn't do.
Think of it this way. Unity is calling update function each frame (lets say you have 60 frames/second) so that means your while loop is called 60 times a second and it probably doesn't have time to read the bytes available in network because each time you do the wile loop the byte[] tmpbuf = new byte[1024]; resets. So msg will be prob nothing.
Answer by bitdigger · May 09, 2017 at 08:43 AM
ahh right, so over localhost it's fast enough to read it, just not with network latency. I'll try that now
I suggest you use UNET NetworkClient and NetworkServer they do good things together.
Answer by WazzaM · May 10, 2017 at 09:17 PM
First, if you're doing this in gameplay it won't be any fun at all. TCP is not good for data that ages because it guarantees delivery and re-sends until it gets there. UDP is much better for data that is NOW. It will lose packets but the ones you get represent a current event, not one that was sent some time before now. Also Unity will block on your Update() method due to the while loop.
@LegalSin would be correct in an environment using multi-threading or, potentially if you convert the code to Coroutines, but the answer isn't quite right as the code stands because Unity is cooperative tasking. Update() doesn't return for ages due its while loop so there's nothing stealing the CPU away from it while you're reading the TCP socket. It's not forced to be 60 updates per second. It's only 60 updates per second if your code lets it achieve that by returning control back to Unity really quickly (hint: no while loops) which is @LegalSin's first point to use a Coroutine... much better but you'll need to restructure this a lot.
Also, TCP packets are assembled in the OS kernel (Linux is the kernel on Android) and the kernel presents them as a stream of data to a readable file descriptor so your app just needs to read it when it gets around to it. Messages are not getting lost if your app is distracted - that is doing other things. If on the other hand, you did a read and then threw the buffer away before using it, the data would be lost. That doesn't appear to be happening here.
One thing that might be happening in your code is when there is no data available (other side hasn't sent yet) the while loop will exit. You should break out the session handling away from waiting for more data. If you use an ethernet packet listening tool (Ethereal / tcpdump on linux) you might find that you're creating and dropping sessions a lot.
I've haven't checked in recent 5.6 builds but in earlier 5.x releases (say 5.1 ish) the TCP/IP .NET implementation seemed to be lacking from Android builds. Remember, the .NET code gets converted to Java in a regular SDK build and I'm reasonably certain this translation doesn't cover the full spectrum of the .NET framework. Might be a bit different in an IL2CPP build where Unity is pushing to update framework support.... yeah maybe.
I suspect that Unity had to deliver more of the System.Net.Sockets support to non-PC platforms to for the newer UNET part of the Unity API because it and other classes in UnityEngine.Networking need the ability to use UDP and TCP.
I recommend trying the simplest System.Net.Socket classes such as the Socket
class to bind, listen, send, receive etc but you might find yourself in a similar situation. You might strike gold by finding the parts of the framework Unity translated to Java for Android builds.
Good luck.
PS: the code fragment will need some severe refactoring to make it more performant but I hope it let's you break a barrier and progress further.