I tried port .NET sockets code to the unity project.
First trouble - compiler show warnings at pulbic static event etc. Unity is not supported events and delegates?
Next question - I am need to take data from sockets. I see two ways:
create List<> to storage server messages, and in Update() check this storage
create graph of the variables, placed as “static”, and write data in this variables. In the Update game loop take data from this vars and change game states.
You should definitely work with a list of messages. Also remember that the System.Collections.Generic.List<> is not thread-safe, so its up to you to do some sort of thread locking.
Thanks, this is what I need. I think, I can take some standart algoritm to thread-lock.
Some other question - static members are one at all application, or available to one scene?
In App I had some science. At first scene user login, at second - view properties, at third - game world, and other… I had network class with basic sockets functions. At another class I had static exemplar of network class and methods for prepair/parse server messages. Is this network exemplar availabe for all game scenes, or at everyone scence I must recreate his?
I think you misunderstoud the reply NPSF3000, Unity does support events, you just have to spell it right.
When adding/removing items to lists from multiple threads you always need to consider threading. That means either traditional locks or some complex CAS (Compare And Swap) code. If you use locking I would recommend keep the operations inside the lock as short as possible. So do not obtain the lock and then iterate though all your messages and handle them.
Rather have your main thread obtain the lock and copy the data, leave an empty list, release the lock and then process the data. This means that other threads are free to receive more packets and put them into the list, rather than forcing your network thread to be waiting while you process all the incoming.
Use a Queue instead of a List, List has O(n) remove from anything but last element, Queue has O(1) for removing the one you need (the first one in the queue).
Also use locking to begin with, if you run into performance problems which are DIRECTLY related to locking, and it’s your last bottleneck, first then start looking into CAS operations. You can find them in the System.Threading.Interlocked class, the name of the CAS function in .NET is System.Threading.Interlocked.CompareExchange
You can also do what UnLogick said and switch between two Lists/Queues, to reduce the lock contention, it looks something like this:
using System.Collections.Generic;
namespace SlimNet.Collections
{
public class MultiplexingQueue<T>
{
object sync;
Queue<T> read;
Queue<T> write;
public int ReadCount
{
get { return read.Count; }
}
public int WriteCount
{
get { return write.Count; }
}
public MultiplexingQueue()
{
sync = new object();
read = new Queue<T>();
write = new Queue<T>();
}
public void Enqueue(T value)
{
lock (sync)
{
write.Enqueue(value);
}
}
public bool Dequeue(out T value)
{
if (read.Count > 0)
{
value = read.Dequeue();
return true;
}
value = default(T);
return false;
}
public bool Peek(out T item)
{
if (read.Count > 0)
{
item = read.Peek();
return true;
}
item = default(T);
return false;
}
public void Switch()
{
lock (sync)
{
Queue<T> temp = write;
write = read;
read = temp;
}
}
}
}
Or if you want something a bit simpler, I recommend you to wrap your data structure in a simple class which does the locking for you, so you don’t have to remember to lock it constantly, something like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SlimNet.Collections
{
public class SynchronizedQueue<T>
{
readonly System.Collections.Generic.Queue<T> queue;
public int Count
{
get
{
lock (queue)
{
return queue.Count;
}
}
}
public SynchronizedQueue()
{
queue = new System.Collections.Generic.Queue<T>();
}
public void Enqueue(T item)
{
lock (queue)
{
queue.Enqueue(item);
}
}
public bool TryPeek(out T item)
{
lock (queue)
{
if (queue.Count > 0)
{
item = queue.Peek();
return true;
}
}
item = default(T);
return false;
}
public bool TryDequeue(out T item)
{
lock (queue)
{
if (queue.Count > 0)
{
item = queue.Dequeue();
return true;
}
}
item = default(T);
return false;
}
}
}
(These two pieces of code is from my SlimNet networking library, normally the license is not open source but I will put these two pieces of code here for you under the very friendly MIT open source license)
Actually I would only agree with fholm when using CAS. When using locks I would recommend just replacing the list locking once from main thread. Such you extract the entire list locking once and its still an O(1) operation. Of course there is a chance that you receive new messages while processing, if you’re concerned about that you can loop and extract the entire list until you get an empty list and then proceed. Performance wise its the same, but the risk of getting lock collisions is lower.
These issues goes away with CAS, but as fholm says thats probably overkill until you want to go Massive.
I think I might not have been 100% clear, the case of one reader and one writer is pretty uncommon when doing networking on windows. It’s usually many writers and one reader due to how the async networking works on both mono and .NET, thus you end up needing something like the “MultiplexingQueue” that I posted. Because you need to lock on every write, or they will trample each-other, but you only want to lock once when you read to switch the read/write queue so no write can happen, under any circumstance when you’re processing input.
It’s also a matter of the fact that the simulation get’s moved a fixed steps, and when you’re starting one step you want to make sure to cut any input of, and data coming in while one step is processing should not be dealt with (this ofcourse depends on what type of data it is, taking in a general sense right now) untill the next step starts, or at least the current step is complete. Otherwise you can end up with really weird synchronization issues in the world state.
A general recommendation when doing multi-threaded programming is to not use a single static variable. It’s a little bit of extra work to pass around some Context object, but it’s worth it in the end.
public class NetExemplar
{
//this code is for sample, to shown where I call queue. End variant of code must be like you write
public Queue<byte[]> messagesBuffer;
public void WakeUp(string IP, int port)
{
}
private void recived(some object)
{
}
public void SendData(byte[] message)
{
}
}
And. We had network adapter
public class NetWorker
{
private static NetExemplar _net=new NetExemplar();
public static bool isConnected = false;
public static bool isAuth = false;
public static float userPositionX = 0.0f;
public static float userPositionZ = 0.0f;
public static void ConnectToServer()
{
_net.WakeUp("someIP", 4500);
void Update()
{
// at this place we are check buffer (messagesBuffer) at _net and parse data. then - write data to specific vars
}
}
Can I call ConnectToServer() at start scene, and then work with _net exemplar - get data and read his from specific vars? For example, at first scene I am auth user, then write isAuth = true;, and at next scene check are user auth?
I’ll just repeat, anything static is bad. It’s bad from a design and flexibility issue and it’s a nightmare when you mix multi-threading into it.
They are stable, without a doubt. If anyone can prove otherwise I would be more then interested. But I’ve been using the Mono sockets in Unity very very intensively for the past ~6 months have not found a single problem so far.
Well, I did mean public static fields, of course there is a need for static functions occasionally as it does not make sense to put everything on an instance.
Wich using Socket.ReceiveAsync and Socket.SendAsync Unity crush down after 1k calls.
Somebody can confirm this information? Or this is an especial case?