- Home /
How to do a TCP Server?
Hi guys, I think my last question was misinterpreted, so here is a more simple one.
How to create a TCP server and Client in unity? I don't want to use unity network layer, this game will connect to a C++ server so I need it to talk to this server someway.
I'm completely lost, all my tries resulted in failure.
Edit:
This the code that is problematic, whenever i call "Socket soc = listener.AcceptSocket();"Unity crashes, I've tried putting it inside a coroutine without sucess.
using System;
using System.Collections;
using System.IO;
using System.Net;
using System.Net.Sockets;
public class ServerClass : MonoBehaviour
{
TcpListener listener;
public void Start()
{
listener = new TcpListener(2033);
listener.Start();
}
public void Update()
{
if(!listener.Pending())
{
Debug.Log("Nothing to do here");
}
else
{
Socket soc = listener.AcceptSocket();
[...]
}
}
}
I already did it, every implementation I did in unity resulted in failure.
"Failure" is not a problem description I can work with. If you have really tried anything (have you?) you should be able to post some code and ask a more concrete question.
Answer by whydoidoit · Sep 18, 2012 at 05:35 PM
Ok so you can use my Loom class to interact with other thread (per the comments to my answer above - this is too long to be a comment) which lets you do this:
void Update() {
if(somethingHappened)
{
Loom.RunAsync(()=>{
//Your other thread code goes here
//e.g. SendStuff();
//If you need to then call back to the game
Loom.QueueOnMainThread(()=>{
//Code triggered from the other thread that
//accesses Unity object goes here
});
});
}
}
Loom.cs
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
using System.Threading;
using System.Linq;
//Loom handles threading
public class Loom : MonoBehaviour
{
private static Loom _current;
public static Loom Current
{
get
{
if (_current == null && Application.isPlaying)
{
var g = GameObject.Find("Loom");
if(g==null)
{
g = new GameObject("Loom");
g.hideFlags = HideFlags.HideAndDontSave;
}
_current = g.GetComponent<Loom>() ?? g.AddComponent<Loom>();
}
return _current;
}
}
void Awake()
{
if(_current != null && _current != this)
{
DestroyImmediate(gameObject);
}
else
{
_current = this;
}
}
private List<Action> _actions = new List<Action>();
public class DelayedQueueItem
{
public float time;
public Action action;
public string name;
}
private List<DelayedQueueItem> _delayed = new List<DelayedQueueItem>();
public static void QueueOnMainThread(Action action, float time, string name)
{
lock(Current._delayed)
{
if(Current._delayed.Any(d=>d.name==name))
return;
QueueOnMainThread(action, time);
}
}
public static void QueueOnMainThread(Action action, string name)
{
QueueOnMainThread(action, 0, name);
}
public static void QueueOnMainThread(Action action, float time)
{
if (time != 0)
{
lock (Current._delayed)
{
Current._delayed.Add(new DelayedQueueItem { time = Time.time + time, action = action});
}
}
else
{
lock (Current._actions)
{
Current._actions.Add(action);
}
}
}
public static void QueueOnMainThread(Action action)
{
lock (Current._actions)
{
Current._actions.Add(action);
}
}
public static void RunAsync(Action a)
{
var t = new Thread(RunAction);
t.Priority = System.Threading.ThreadPriority.Normal;
t.Start(a);
}
private static void RunAction(object action)
{
((Action)action)();
}
Action[] toBeRun = new Action[1000];
DelayedQueueItem[] toBeDelayed = new DelayedQueueItem[1000];
void Update()
{
try
{
var actions = 0;
var delayedCount = 0;
//Process the non-delayed actions
lock (_actions)
{
for(var i = 0; i < _actions.Count; i++)
{
toBeRun[actions++] = _actions[i];
if(actions==999)
break;
}
_actions.Clear();
}
for(var i = 0; i < actions; i++)
{
var a = toBeRun[i];
try
{
a();
}
catch (Exception e)
{
Debug.LogError("Queued Exception: " + e.ToString());
}
}
lock (_delayed)
{
for(var i = 0; i <_delayed.Count; i++)
{
var d = _delayed[i];
if(d.time < Time.time)
{
toBeDelayed[delayedCount++]= d;
if(delayedCount==999)
break;
}
}
}
for(var i = 0; i < delayedCount; i++)
{
var delayed = toBeDelayed[i];
lock (_delayed)
{
_delayed.Remove(delayed);
}
try
{
delayed.action();
}
catch (Exception e)
{
Debug.LogError("Delayed Exception:" + e.ToString());
}
}
}
catch(Exception e)
{
Debug.LogError("Loom Error " + e.ToString());
}
}
}
Nice!!! But i didn't get somethings. How to call this lambda code? Can i put it inside a function like this?
void AsyncSendStuff()
{
Loom.RunAsync(()=>{
[...]
//If you need to then call back to the game
Loom.QueueOn$$anonymous$$ainThread(()=>{ [...]});
});
}
And about this function "Loom.QueueOn$$anonymous$$ainThread(()=>{});"I didn't get it, can you please post and example?
Thanks for everything, you have done so much to help!
So you can put any code you like where you have [...] in your example - it can access local variables, class variables etc etc and call anything BUT nothing must touch a Unity object (read or update it in any way) or it will fail. So you can call your code to send to the server etc. Let's imagine that the server returns am X,Y,Z coordinate that you are supposed to update the game object with - you can't do that in a second thread - hence the QueueOn$$anonymous$$ainThread.
Loom.RunAsync(()=>{
var newPosition = SomehowGetVector3FromServer();
Loom.QueueOn$$anonymous$$ainThread(()=>{
transform.position = newPosition;
});
});
I thought it would be something like that, sorry, never used lambda before.
I'll implement right now.
Very powerful - especially the local variable access stuff. Closures are cool :)
No threading is easy and it is damn easy to call something, that calls something that accesses a Unity object and bang. But your case is ideal for this approach - write all the blocking code you like in the lambda and it will run alongside the game - just use the QueueOn$$anonymous$$ainThread to actually change something in your game due to the server.
The code in QueueOn$$anonymous$$ainThread will run during the current or next frame depending on where it is during your async queue call. You can also pass a parameter to delay it for a period of time - but I doubt that you will need that.
Answer by whydoidoit · Sep 18, 2012 at 04:28 PM
AcceptSocket is a blocking call - you don't want to be calling that unless you know that there is a request pending - you can tell that using this. Otherwise Unity will hang until a request appears.
Coroutines are not threads - a coroutine that blocks will lock up Unity.
It's not crashing anymore! At least in idle state, but when I try to send a message using the following code unity crashes just like before. Also I will update the code in my question with your hint.
void Sendstuff()
{
TcpClient client = new TcpClient("127.0.0.1", 2055);
try
{
Stream s = client.GetStream();
StreamReader sr = new StreamReader(s);
StreamWriter sw = new StreamWriter(s);
sw.AutoFlush = true;
string name = "$$anonymous$$SG";
sw.WriteLine(name);
s.Close();
}
finally
{
client.Close();
}
}
That code blocks too... You need to by using Asynchronous IO or have this stuff happening on another thread. Take a look at the Async methods here - it might be enough just to call FlushAsync() but I don't know what that would do with your close. I really think you should consider spinning up a thread for this.
There is no FlushAsync() method on my StreamWriter but thats no problem. I'll create a thread, but I don't know how exactly, can I follow this example link? But I will only use the Start call and let the thread finishes by itself, right?
Answer by salat · Jul 30, 2013 at 08:29 PM
Check this brilliant presentation. There is a chapter with full example based on .net sockets, server and client side. Very clean and easy code. You will realize why async socket operations like BeginConnect, BeginReceive and so are needed to make it running with Unity.
http://video.unity3d.com/video/6947115/unite-2012-unity-network
UPDATE: somehow original ^^^ video not available anymore, so mirror:
Thank you for the interest in the topic, I've bookmarked your links :)
Answer by MadDave · Sep 18, 2012 at 04:14 PM
If you are going to connect to a C++ Server, why do you want to create a TCP server in Unity? And since you don't want to use the Unity network layer, this is not a Unity question any more.
Anyway, the answer is "Use a socket API you like." Let me google this for you... like this one: http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.aspx
Hope it helps.
.Net Sockets support is a Unity3D feature exposed in the licenses page. I'm having problems implementing it. Also, by server I meant just a TCPListener, so i can catch messages.
1) one might not want to use Unity Network. many reasons. 2) using .net sockets in Unity application have some specifics. For example right now I am trying to figure out how to prevent .net socket asyncronious callbacks sometime being called from Unity working thread. Avoid blocking of Unity thread is a main concern.
Your answer
Follow this Question
Related Questions
Error: The requested address is not valid in its context. 1 Answer
TcpListener with Network Manager 1 Answer
Network Connections in Editor 1 Answer
Is there any difference calling Network.Disconnect or Network.CloseConnection from client? 1 Answer
May I control the game of a client through pressing some GuiButtons in the server application? 1 Answer