Unity: C# TCP Client Server TcpListener NetworkStream UDP

from: https://gist.github.com/anselm/c92efb677d8cbbe23d4e


/*

Some scratch code for testing a variety of patterns of client server connection.

I needed to send data from an android phone to a macbook via USB and I happened to be using C# under Unity 3D. Some of the problems I ran into included:

 - co-routines under unity were not fully exhausting the tcp buffers fast enough i think; even when I aggressively polled until they were empty...
 
 - kept running into a curious bug where when the android device overheated that there would be a huge latency in traffic from the android device to the desktop.
 
 - ran into a problem where the fake TCP port forwarding accomplished by "sdk/platform-tools/adb forward tcp:1234 tcp:1234" was not letting the android device be anything other than a host... and wouldn't let me ship UDP packets either.
 
  - there's a lot of bad advice out there; for example the android emulator does offer an IP for the host but a real device does not. And it's a hassle to setup a real route back to the host - even with netcat and the like...  for example usb0 doesn't even appear as a device and I don't feel like adding it...

It was easiest to ask for each packet one by one... to throttle the traffic rate... this was "good enough" for me. Once I was comfortable with that I then let the android end go ahead and just send at a fixed rate... although the overheating problem was still present in all cases. In my mind many of these issues must have something to do with the fake TCP over USB bridge.

*/

-------------------------------------------------------------------------------------------------------------------------------
myserver.cs
-------------------------------------------------------------------------------------------------------------------------------


using UnityEngine;
using System.Collections;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System;

public class MyServer : MonoBehaviour {

	static float x=0,y=0,z=0,qx=0,qy=0,qz=0,qw=1;
	
	void Start () {
		Application.targetFrameRate = 30;
		Screen.sleepTimeout = 0;
		TCPStart(0);
	}
	
	void OnDisable() {
		stop = true;
		TCPStop();
	}
	
	void Update () {

		x = transform.position.x;
		y = transform.position.y;
		z = transform.position.z;
		qx = transform.rotation.x;
		qy = transform.rotation.y;
		qz = transform.rotation.z;
		qw = transform.rotation.w;
		tick++;
		
		if(style == -1) TCPSpeak();
	}
	
	void OnGUI () {
		var style = new GUIStyle("label");
		style.fontSize = 50;
		GUI.color = Color.yellow;
		GUI.Label (new Rect(10,400,1200,50), "t="+ms, style);
		GUI.Label (new Rect (10, 500, 1200, 50), "x=" + x, style);
		GUI.Label (new Rect (10, 550, 1200, 50), "y=" + y, style);
		GUI.Label (new Rect (10, 600, 1200, 50), "z=" + z, style);
	}

	// --------------------------------------------------------------------------------------------
	
	static IPEndPoint p = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 1234);
	static UdpClient u = null;
	static TcpClient c = null;
	static NetworkStream s;
	static TcpListener l = null;
	static long msr=0,ms=0,ms2=0;
	static byte[] buf = new byte[1024];
	static bool stop = false;
	static int tick=0,lasttick=0;
	static int style = -1; // -1 = send on update, 0 = send update requests every 33ms, 1 or 2 = send update requests when asked
	static bool pending = false;
	static bool host = true;
	static bool isandroiddevice = true;

	static void TCPStop() {
		if(s!=null) s.Close(); s = null;
		if(c!=null) c.Close(); c = null;
		if(u!=null) u.Close(); u = null;
		if(l!=null) l.Stop(); l = null;
	}
	
	static void TCPStart(int delay = 1000) {
		if(stop) return;
		TCPStop();
		if(delay>0) System.Threading.Thread.Sleep(delay);
		Debug.Log("(re)starting");
		TCPHost(); // else TCPClient();
	}
	
	static void TCPHost() {
		try {
			l = new TcpListener(p);		
			l.Start();
			l.BeginAcceptTcpClient(TCPHostAccept,l);
		} catch (Exception e) {
			Debug.Log("Error..... " + e.StackTrace);
			TCPStart();
			return;
		}
		//u = new UdpClient(p);
		//u.BeginReceive(new System.AsyncCallback(UDPCallback),u);
		//Debug.Log ("UDP Started");
	}

	static void TCPHostAccept(IAsyncResult state) {
		if(stop)return;
		try {
			c = l.EndAcceptTcpClient(state);
		} catch(Exception e) {
			Debug.Log ("Error..." + e );
			TCPStart();
			return;
		}
		TCPAccept();
	}

	static void TCPClient() {
		if(stop)return;
		try {
			c = new TcpClient();
			c.Connect(p);
		} catch(Exception e) {
			Debug.Log ("Error..." + e );
			TCPStart();
			return;
		}
		TCPAccept();
	}

	static void TCPAccept() {
		if(stop)return;
		Debug.Log("Got a connection");
		c.NoDelay = true;
		c.SendBufferSize = 512;
		c.ReceiveBufferSize = 512;
		s = c.GetStream();
		//c.ReceiveTimeout = 1;
		//s.ReadTimeout = 100;
		tick = lasttick = 0;
		pending = false;
		TCPListen();
		if(style==0 && isandroiddevice)TCPSpeak(); 
	}
		
	// -----------------------------------------------------------------------------------

	static void TCPListen() {
		if(stop) return;
		try {
			s.BeginRead(buf,0,buf.Length,new AsyncCallback(TCPListened),s);
		} catch(Exception e) {
			Debug.Log("Error... " + e);
			TCPStart();
			return;
		}
	}
	
	static void TCPListened(IAsyncResult state) {
		if(stop) return;
		int len = 0;
		try {
			len = s.EndRead(state);
		}  catch(Exception e) {
			Debug.Log("Error... " + e);
			TCPStart();
			return;
		}
		TCPListen();
		if(style==1 || style==2) TCPSpeak(); // in a call response pattern we respond only when asked
	}

	// -----------------------------------------------------------------------------------
	
	static void TCPSpeak() {
		if(stop) { Debug.Log ("Asked to stop"); return; }
		if(s == null) { Debug.Log ("No connection"); return; }
		//if(pending) { Debug.Log ("Pending"); return; } // it is possible that there could be multiple calls outstanding - ignore overlapping calls
		try {
			long ms = DateTime.UtcNow.Ticks;
			string msg = x + " " + y + " " + z + " " + qx + " " + qy + " " + qz + " " + qw + " " + ms + " " + tick;
			byte[] bytes = System.Text.Encoding.UTF8.GetBytes(msg);
			s.BeginWrite(bytes,0,bytes.Length,new AsyncCallback(TCPSpoke),s);
			pending = true;
		} catch(Exception e) {
			Debug.Log ("Error..." + e );
			TCPStart();
			return;
		}
	}
	
	static void TCPSpoke(IAsyncResult state) {
		if(stop)return;
		try {
			s.EndWrite(state);
			pending = false;
		} catch(Exception e) {
			Debug.Log ("Error..." + e );
			TCPStart();
			return;
		}
		if(style==0) {
			System.Threading.Thread.Sleep(33);
			TCPSpeak();
		}
	}
	
	// ----------------------------------------------------------------------------------------------------

	static void UDPSend() {
		if(stop)return;
		try {
			string msg = x + " " + y + " " + z + " " + qx + " " + qy + " " + qz + " " + qw + " " + ms;
			byte[] bytes = System.Text.Encoding.UTF8.GetBytes(msg);
			u.BeginSend(bytes,bytes.Length,p,UDPDone,s);
			//u.Send(bytes,bytes.Length);
		} catch (Exception e ) {
			Debug.Log ("UDP Socket failed to send data " + e);
		}
	}
	
	static void UDPDone(System.IAsyncResult ar) {
		if(stop)return;
		try {
			u.EndSend(ar);
			// xxx could send again here right now...
		} catch(Exception e) {
			Debug.Log ("failed " + e);
		}
	}
	
	
}


-------------------------------------------------------------------------------------------------------------------------------
myclient.cs
-------------------------------------------------------------------------------------------------------------------------------


using UnityEngine;
using System.Collections;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Text;
using System;

public class MyClient : MonoBehaviour {

	public GameObject target;
	static float x=0,y=0,z=0,qx=0,qy=0,qz=0,qw=1;

	public void Start() {
		Application.targetFrameRate = 60;
		TCPStart(0);
	}

	public void OnDisable() {
		stop = true;
		TCPStop();
	}

	public void Update() {
		if(TCPUpdated()) {
			target.transform.position = new Vector3(x,y,z);
			target.transform.rotation = new Quaternion(qx,qy,qz,qw);
		}
	}

	// --------------------------------------------------------------------------
	static IPEndPoint p = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 1234);
	static UdpClient u = null;
	static TcpClient c = null;
	static NetworkStream s;
	static TcpListener l = null;
	static long msr=0,ms=0,ms2=0;
	static byte[] buf = new byte[1024];
	static bool stop = false;
	static int tick=0,lasttick=0;
	static int style = -1; // -1 / 0 = just listen for updates, 1 = ask for updates manually, 2 = ask for updates automatically
	static bool pending = false;
	static bool isandroiddevice = false;

	static bool TCPUpdated() {
		if(style == 1) TCPAsk(); // make a manual request; can do this as often as we wish - overlapping requests blocked in tcpask()
		if(tick > lasttick) {
			// for all modes we want to return true if we have new data: ticks > lasttick...
			lasttick = tick;
			return true;
		}
		return false;
	}

	static void TCPStop() {
		if(s!=null) s.Close(); s = null;
		if(c!=null) c.Close(); c = null;
		if(u!=null) u.Close(); u = null;
		if(l!=null) l.Stop(); l = null;
	}
	
	static void TCPStart(int delay = 1000) {
		if(stop) return;
		TCPStop();
		if(delay>0) System.Threading.Thread.Sleep(delay);
		Debug.Log("(re)starting");
		TCPClient();
	}
	
	static void TCPHost() {
		try {
			l = new TcpListener(p);		
			l.Start();
			l.BeginAcceptTcpClient(TCPHostAccept,l);
		} catch (Exception e) {
			Debug.Log("Error..... " + e.StackTrace);
			TCPStart();
			return;
		}
		//u = new UdpClient(p);
		//u.BeginReceive(new System.AsyncCallback(UDPCallback),u);
		//Debug.Log ("UDP Started");
	}
	
	static void TCPHostAccept(IAsyncResult state) {
		if(stop)return;
		try {
			c = l.EndAcceptTcpClient(state);
		} catch(Exception e) {
			Debug.Log ("Error..." + e );
			TCPStart();
			return;
		}
		TCPAccept();
	}
	
	static void TCPClient() {
		if(stop)return;
		try {
			c = new TcpClient();
			c.Connect(p);
		} catch(Exception e) {
			Debug.Log ("Error..." + e );
			TCPStart();
			return;
		}
		TCPAccept();
	}
	
	static void TCPAccept() {
		if(stop)return;
		Debug.Log("Got a connection");
		c.NoDelay = true;
		c.SendBufferSize = 512;
		c.ReceiveBufferSize = 512;
		s = c.GetStream();
		//c.ReceiveTimeout = 1;
		//s.ReadTimeout = 100;
		tick = lasttick = 0;
		pending = false;
		TCPListen();
		if(style==2 && !isandroiddevice)TCPAsk(); // on other devices it is our job to ask for data in some modes
	}

	// -----------------------------------------------------------------------------------
	
	static void TCPListen() {
		if(stop) return;
		try {
			s.BeginRead(buf,0,buf.Length,new AsyncCallback(TCPListened),s);
		} catch(Exception e) {
			Debug.Log("Error... " + e);
			TCPStart();
			return;
		}
	}
	
	static void TCPListened(IAsyncResult state) {
		if(stop) return;
		int len = 0;
		try {
			len = s.EndRead(state);
		}  catch(Exception e) {
			Debug.Log("Error... " + e);
			TCPStart();
			return;
		}

		string str = Encoding.UTF8.GetString(buf,0,len);
		string[] args = str.Split(' ');
		if(args.Length >= 8) {
			x = float.Parse(args[0]);
			y = float.Parse(args[1]);
			z = float.Parse(args[2]);
			qx = float.Parse(args[3]);
			qy = float.Parse(args[4]);
			qz = float.Parse(args[5]);
			qw = float.Parse(args[6]);
			msr = long.Parse (args[7]); // the time on REMOTE end
			ms2 = DateTime.UtcNow.Ticks;
			if((tick&31)==0) {
				Debug.Log ("Step="+tick+" msr="+msr+" ms="+ms+" delta="+(msr-ms)+" mydelta="+(ms2-ms)+" x="+x+" y="+y+" z="+z);
			}
		}
		tick++;
		TCPListen(); // listen again - this should be the only place that restarts listening other than bootup
	}

	// -----------------------------------------------------------------------------------
	
	static void TCPAsk() {
		if(stop) return;
		if(pending) return; // TODO technically this should be a synchronous block but I expect 33ms between these tests
		if(s==null) return;
		try {
			byte[] bytes = System.Text.Encoding.UTF8.GetBytes("" + DateTime.UtcNow.Ticks);
			s.BeginWrite(bytes,0,bytes.Length,new AsyncCallback(TCPAsked),s);
			pending = true;
		} catch(Exception e) {
			Debug.Log("Error... " + e);
			TCPStart();
			return;
		}
	}
	
	static void TCPAsked(IAsyncResult state) {
		if(stop) return;
		try {
			s.EndWrite(state);
			pending = false;
		} catch(Exception e) {
			Debug.Log("Error... " + e);
			TCPStart();
			return;
		}
		if(style == 2) {
			System.Threading.Thread.Sleep(33);
			TCPAsk(); // for automatical mode get a fresh update but not too often
		}
	}
	
	// ---------------------------------------------------------------------------------------
		
	public static void UDPCallback(System.IAsyncResult ar) {
		if(stop) return;
		try {
			byte[] receiveBytes = u.EndReceive(ar, ref p);
			string receiveString = Encoding.ASCII.GetString(receiveBytes);
			u.BeginReceive(new System.AsyncCallback(UDPCallback),ar);
			Debug.Log("Received: " +  receiveString);
		} catch (System.Exception send_exception ) {
			Debug.Log ("UDP Socket failed to receive");
		}
	}
	
}

--------------------------------------------------------------------------------------------------------
tcpclient.java - just another way to test
--------------------------------------------------------------------------------------------------------

import java.io.*;
import java.net.*;

class TCPClient {
 public static void main(String argv[]) throws Exception {
  Socket socket = new Socket("127.0.0.1", 1235);
  BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
  while(true) {
    String data = reader.readLine();
    if(data != null) System.out.println(data);
  }
 }
}

--------------------------------------------------------------------------------------------------------
tcpserver.java - just another way to test
--------------------------------------------------------------------------------------------------------

import java.io.*;
import java.net.*;

class TCPServer
{
   public static void main(String argv[]) throws Exception
      {
         String clientSentence;
         String capitalizedSentence;
         ServerSocket welcomeSocket = new ServerSocket(1235);

         while(true)
         {
            Socket connectionSocket = welcomeSocket.accept();
            BufferedReader inFromClient =
               new BufferedReader(new InputStreamReader(connectionSocket.getInputStream()));
            DataOutputStream outToClient = new DataOutputStream(connectionSocket.getOutputStream());
            clientSentence = inFromClient.readLine();
            System.out.println("Received: " + clientSentence);
            capitalizedSentence = clientSentence.toUpperCase() + '\n';
            outToClient.writeBytes(capitalizedSentence);
         }
      }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值