Skip to content

Instantly share code, notes, and snippets.

@anselm
Created June 20, 2014 18:08
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save anselm/c92efb677d8cbbe23d4e to your computer and use it in GitHub Desktop.
Save anselm/c92efb677d8cbbe23d4e to your computer and use it in GitHub Desktop.
C# TCP Client Server TcpListener NetworkStream UDP
/*
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);
}
}
}
@anselm
Copy link
Author

anselm commented Jun 23, 2014

There's a carriage return missing on the publishing end btw.

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