I'm not sure if this is the right subsite of stackexchange because it's not only about code review but a bit about design eihter.
I'm currently developing a network application. I need to connect a (java) server with a tcp socket. I implemented two different variants of my client. One uses a blocking socket with threads, the other one uses a non blocking socket and Unitys Update method. Personally I like the one without threads better, but my concern is that if I use Unitys Update method writing to the socket could take longer, especially if Unitys framerate is low, since Update would be called less frequently than with a seperate thread. I know there are many other questions about blocking and non-blocking sockets, but nothing about Unity3d.
The approach with a blocking TCPClient and Threads:
using UnityEngine;
using UnityEngine.Events;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Threading;
public class ThreadedTestClient : MonoBehaviour
{
public string Server = "127.0.0.1";
public int Port = 5001;
public int ReadBufferSize = 1024, IncomingBufferSize=1024*1024*4;
private byte[] ReadBuffer;
private TcpClient Connection;
private NetworkStream Stream;
private Queue<OutgoingMessage> WriteQueue;
private MessageBuffer IncomingBuffer;
private Thread Writer,Reader;
private bool Running;
void Start ()
{
try {
IncomingBuffer=new MessageBuffer(IncomingBufferSize);
ReadBuffer = new byte[ReadBufferSize];
WriteQueue = new Queue<OutgoingMessage> ();
Connection = new TcpClient();
Connection.Connect(Server,Port);
Stream=Connection.GetStream();
Running=true;
Writer=new Thread(Writing);
Writer.Start();
Reader=new Thread(Reading);
Reader.Start();
#if UNITY_EDITOR
UnityEditor.EditorApplication.playmodeStateChanged = delegate () {
if (!UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode &&
UnityEditor.EditorApplication.isPlaying) {
Debug.Log (string.Format("[{0}] Exiting playmode.",GetType().Name));
Running=false;
Writer.Abort();
Reader.Abort();
}
};
#endif
} catch (System.ArgumentNullException e) {
Debug.LogWarning (string.Format ("ArgumentNullException: {0}", e));
} catch (SocketException e) {
Debug.LogWarning (string.Format ("SocketException: {0}", e));
}
}
public void SendHelloWorld(){
string prefix="SechzehnSechzehn";
//c# implementation of javas ByteBuffer
ByteBuffer b = ByteBuffer.allocate (1024);
byte[] content = System.Text.Encoding.UTF8.GetBytes (prefix+"HelloWorld");
byte[] data = new byte[b.put (MessageBuffer.intToByteArray (content.Length)).put (content).flip ().limit ()];
b.get (data);
Write (data,()=>Debug.Log("Sended Hello World!"));
}
public void SendRandomData(int size){
byte[] content = new byte[size+4];
new System.Random ().NextBytes (content);
byte[] bs=MessageBuffer.intToByteArray(size);
for (int i = 0; i < bs.Length; i++) {
content [i] = bs [i];
}
Write (content,()=>Debug.Log("Sended Random data!"));
}
//onDone doesnt guarantee any data to have reached to server, just asures that data where passed to the underlying socket
public void Write (byte[] b,UnityAction onDone)
{
WriteQueue.Enqueue (new OutgoingMessage(b,onDone));
}
private void CallWorker(Queue<MessageBuffer.EncryptedMessage> messages){
//TODO
}
private void Reading ()
{
while (Running & Connection.Connected) {
int readed = Stream.Read (ReadBuffer, 0, ReadBufferSize);
//addFrom returns the amount of complete messages;
if (IncomingBuffer.addFrom (ReadBuffer, 0, readed) > 0) {
//gets the queue of complete messages that arrived
CallWorker (IncomingBuffer.getAndResetQueue ());
}
}
}
private void Writing(){
while (Running & Connection.Connected) {
if (WriteQueue.Count > 0) {
OutgoingMessage packet = WriteQueue.Dequeue();
Stream.Write (packet.Data, 0, packet.Data.Length);
packet.Done ();
}
}
}
private class OutgoingMessage{
private readonly UnityAction OnDone;
public readonly byte[] Data;
public OutgoingMessage(byte[] data, UnityAction onDone){
Data=data;
OnDone=onDone;
}
public void Done(){
if (OnDone != null) {
OnDone ();
}
}
}
}
The approach with a non-blocking TCPClient and Unitys Update:
using UnityEngine;
using UnityEngine.Events;
using System.Collections.Generic;
using System.Net.Sockets;
public class TestClient : MonoBehaviour
{
public string Server = "127.0.0.1";
public int Port = 5001;
public int ReadBufferSize = 1024, IncomingBufferSize=1024*1024*4;
private byte[] ReadBuffer;
private TcpClient Connection;
private Queue<OutgoingMessage> WriteQueue;
private int WriteOffset;
private MessageBuffer IncomingBuffer;
void Start ()
{
try {
IncomingBuffer=new MessageBuffer(IncomingBufferSize);
ReadBuffer = new byte[ReadBufferSize];
WriteQueue = new Queue<OutgoingMessage> ();
Connection = new TcpClient();
Connection.Connect(Server,Port);
Connection.Client.Blocking = false;
} catch (System.ArgumentNullException e) {
Debug.LogWarning (string.Format ("ArgumentNullException: {0}", e));
} catch (SocketException e) {
Debug.LogWarning (string.Format ("SocketException: {0}", e));
}
}
public void SendHelloWorld(){
string prefix="SechzehnSechzehn";
ByteBuffer b = ByteBuffer.allocate (1024);
byte[] content = System.Text.Encoding.UTF8.GetBytes (prefix+"HelloWorld");
byte[] data = new byte[b.put (MessageBuffer.intToByteArray (content.Length)).put (content).flip ().limit ()];
b.get (data);
Write (data,()=>Debug.Log("Sended Hello World!"));
}
public void SendRandomData(int size){
byte[] content = new byte[size+4];
new System.Random ().NextBytes (content);
byte[] bs=MessageBuffer.intToByteArray(size);
for (int i = 0; i < bs.Length; i++) {
content [i] = bs [i];
}
Write (content,()=>Debug.Log("Sended Random data!"));
}
public void Write (byte[] b,UnityAction onDone)
{
WriteQueue.Enqueue (new OutgoingMessage(b,onDone));
}
private void CallWorker(Queue<MessageBuffer.EncryptedMessage> messages){
//TODO
}
void Update ()
{
if (Connection.Connected) {
if (Connection.Client.Available > 0) {
int readed=Connection.Client.Receive (ReadBuffer);
if (IncomingBuffer.addFrom (ReadBuffer, 0, readed) > 0) {
CallWorker (IncomingBuffer.getAndResetQueue ());
}
}
if (WriteQueue.Count > 0) {
OutgoingMessage packet = WriteQueue.Peek ();
WriteOffset += Connection.Client.Send (packet.Data, WriteOffset, packet.Data.Length - WriteOffset, SocketFlags.None);
if (WriteOffset >= packet.Data.Length) {
WriteQueue.Dequeue ();
WriteOffset = 0;
packet.Done ();
}
}
}
}
private class OutgoingMessage{
private readonly UnityAction OnDone;
public readonly byte[] Data;
public OutgoingMessage(byte[] data, UnityAction onDone){
Data=data;
OnDone=onDone;
}
public void Done(){
if (OnDone != null) {
OnDone ();
}
}
}
}
IEnumerator
orFixedUpdate
instead of theUpdate()
you are using right now. Considering 30fps, you would execute that code 30times / second. Let's not even consider playing at 120+ fps – MX D Oct 7 '16 at 11:31