c# thread safe logging issue not using singleton - c#

I'm creating a logging class in C# and I need it to be thread safe. I've implemented TextWriter.Synchronized and locks but I'm getting a very strange issue with the locks where they seem to not work.
I don't want to use a singleton or a static class because I want to be able to have multiple instances of this logging class at any given time and I want to synchronize the threads based on the log's filename. So if I have 30 threads with 3 different instances of the Log class all using the same log file, it will synchronize properly and not have any issues. Below is what I've come up with so far. I've left out some of the code that is irrelevant, like the constructor and close/dispose.
public class Log : IDisposable
{
public enum LogType
{
Information,
Warning,
Error
}
private FileStream m_File;
private TextWriter m_Writer;
private string m_Filename;
//this is used to hold sync objects per registered log file
private static SortedList<string, object> s_SyncObjects = new SortedList<string, object>();
//this is used to lock and modify the above variable
private static readonly object s_SyncRoot = new object();
public void WriteLine(Log.LogType MsgType, string Text)
{
//this is the problem i think, the lock isn't functioning correctly
//see below this code for an example log file with issues
lock (Log.s_SyncObjects[this.m_Filename])
{
this.m_Writer.WriteLine(DateTime.Now.ToString("MM/dd/yyyy HH:mm:ss:fffffff") + " " + MsgType.ToString() + ": " + Text);
}
return;
}
public void Open(string Filename)
{
//make filename lowercase to ensure it's always the same
this.m_Filename = Filename.ToLower();
this.m_File = new FileStream(Filename, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
this.m_Writer = TextWriter.Synchronized(new StreamWriter(this.m_File) { AutoFlush = true });
//lock the syncroot and modify the collection of sync objects
//this should make it so that every instance of this class no matter
//what thread it's running in will have a unique sync object per log file
lock (Log.s_SyncRoot)
{
if (!Log.s_SyncObjects.ContainsKey(this.m_Filename))
Log.s_SyncObjects.Add(this.m_Filename, new object());
}
}
}
To test this I'm creating 3 instances of the logger pointing to the same log file, creating 30 threads and assigning each thread one of the loggers (in order 1,2,3,1,2,3), then I run all 30 threads until I press q.
This works great for writing line by line in a log file and keeping the times the writes happen in the correct order but here is what I get in the log file. It seems that the thread overwrites a portion of the log file and it seems to happen with different instances of the logger on different threads, never the same instance of the logger on different threads. The log file below has the time the entry was created, the logger ID (1 based), the thread ID (0 based) and the message "test".
08/27/2012 11:47:34:3469116 Information: LOGID: 1, THREADID: 9, MSG: test
08/27/2012 11:47:34:3469116 Information: LOGID: 1, THREADID: 9, MSG: test
08/27/2012 11:47:34:3469116 Information: LOGID: 1, THREADID: 9, MSG: test
08/27/2012 11:47:34:3469116 Information: LOGID: 1, THREADID: 9, MSG: test
08/27/2012 11:47:34:3469116 Information: LOGID: 1, THREADID: 9, MSG: test
08/27/2012 11:47:34:3469116 Information: LOGID: 1, THREADID: 9, MSG: test
08/27/2012 11:47:34:3479116 Information: LOGID: 1, THREADID: 9, MSG: test
08/27/2012 11:47:34:3479116 Information: LOGID: 1, THREADID: 9, MSG: test
08/27/2012 11:47:34:3479116 Information: LOGID: 1, THREADID: 9, MSG: test
08/27/2012 11:47:34:3479116 Information: LOGID08/27/2012 11:47:34:3479116 Information: LOGID: 3, THREADID: 23, MSG: test
08/27/2012 11:47:34:3479116 08/27/2012 11:47:34:3509118 Information: LOGID: 1, THREADID: 0, MSG: test
08/27/2012 11:47:34:3509118 Information: LOGID: 1, THREADID: 0, MSG: test
08/27/2012 11:47:34:3509118 Information: LOGID: 1, THREADID: 0, MSG: test
08/27/2012 11:47:34:3509118 Information: LOGID: 1, THREADID: 0, MSG: test
08/27/2012 11:47:34:3509118 Information: LOGID: 1, THREADID: 0, MSG: test
Notice that 2 of the lines have been mangled. I'm guessing this is due to the locks not working properly, or my misuse of the locks. I would also prefer not to use queuing or any kind of singleton. This behavior doesn't seem to happen if I change the lock within WriteLine to the m_SyncRoot variable and make it non-static. I have no idea why that works but to me it seems like that is not what I want to do. I also don't want to lock on a static m_SyncRoot alone because then if I have 3 instances of the logger pointing to 3 different log files then each one will block the other for no reason.
I'm so lost on this, am I completely screwing this up?
In case anyone needs it, here is the test class for generating the threads
public class LogTest
{
private Log m_Log1;
private Log m_Log2;
private Log m_Log3;
private Thread[] m_Threads;
private const int THREAD_COUNT = 30;
private bool m_Done;
public LogTest()
{
this.m_Log1 = new Log();
this.m_Log2 = new Log();
this.m_Log3 = new Log();
this.m_Log1.Open("test.txt");
this.m_Log2.Open("test.txt");
this.m_Log3.Open("test.txt");
this.m_Threads = new Thread[THREAD_COUNT];
this.m_Done = false;
}
public void run()
{
for (int i = 0; i < THREAD_COUNT; i++)
{
Thread th = new Thread(new ParameterizedThreadStart(this.LogThread));
this.m_Threads[i] = th;
}
for (int i = 0; i < THREAD_COUNT; i++)
{
int logId = 1;
Log temp = this.m_Log1;
if ((i % 3) == 1)
{
temp = this.m_Log2;
logId = 2;
}
else if ((i % 3) == 2)
{
temp = this.m_Log3;
logId = 3;
}
this.m_Threads[i].Start(new object[] { logId, i, temp });
}
ConsoleKeyInfo key = new ConsoleKeyInfo();
while ((key = Console.ReadKey()).KeyChar != 'q')
;
this.m_Done = true;
}
private void LogThread(object state)
{
int loggerId = (int)((object[])state)[0];
int threadId = (int)((object[])state)[1];
Log l = (Log)((object[])state)[2];
while (!this.m_Done)
{
l.WriteLine(Log.LogType.Information, String.Format("LOGID: {0}, THREADID: {1}, MSG: {2}", loggerId, threadId, "test"));
}
}
}
EDIT: edited to change static m_ to s_ as suggested and added the AutoFlush property to the StreamWriter; setting it to true... still does not work.

I figured out the problem!
The thread synchronization works as it should and so does the TextWriter.Synchronized() so the problem isn't really the threads at all. Take this into account:
I create 3 instances of the Log class and point them all to "test.txt"
Log log1 = new Log();
Log log2 = new Log();
Log log3 = new Log();
log1.Open("test.txt"); //new file handle as instance member
log2.Open("test.txt"); //new file handle as instance member
log3.Open("test.txt"); //new file handle as instance member
In each call to Open() I am opening a new file handle to the same file, so I have 3 unique and separate file handles. Each file handle or Stream has it's own file pointer which seeks along the stream as I read or write.
So, if we have the following:
log1.WriteLine("this is some text"); //handled on thread 1
log2.WriteLine("testing"); //handled on thread 2
If Thread 1 starts to write to the file and completes the file contents will be
this is some text
When Thread 2 starts to write, because the file handles and streams are unique the current location of log1's file pointer is at 16 and log2's is still at 0 so after log2 is done writing, the resulting log file will read:
testing some text
So, all I need to do is make sure I open only 1 unique FileStream per log file and do the synchronization like I have been. Works great now!

I think your lock is working fine, but according to the documentation, TextWriter.Flush doesn't actually do anything, so it isn't actually flushing the buffer before you release the lock. Here's the [link]. 1
It looks like you can fix the problem by using AutoFlush on the streamwriter in the Open method.
this.m_Writer = TextWriter.Synchronized(new StreamWriter(this.m_File){AutoFlush=true})

Related

c# Message Message Queue Count. - Sharing violation resulted from queue being open already for exclusive receive

So I am busy writing an Watchdog for message queue installed on one of my servers. I have a application ( 3rd party ) that is listening on the queue and processing the messages. I now want to do a count on it and if message reach example 1500 I send a email. So all my code works except that I need to close the 3rd party app to use the message queue. What I do is I get all the queue names that exist. Work fine.
public void GetPrivateQueues()
{
MessageQueue[] QueueList =
MessageQueue.GetPrivateQueuesByMachine(".");
foreach (MessageQueue queueItem in QueueList)
{
i++;
myPrivateQueues.Add(queueItem.Path);
Count(queueItem.Path);
}
return;
}
So when I do the count of the queue like this
public void Count(String path)
{
MessageQueue queue = new MessageQueue(path);
MessageEnumerator messageEnumerator = queue.GetMessageEnumerator2();
int iii = 0;
while (messageEnumerator.MoveNext())
{
iii++;
}
myPrivateQueuesCount.Add(iii);
return;//i;
}
I get the error.
System.Messaging.MessageQueueException (0x80004005): Sharing violation resulted from queue being open already for exclusive receive.
How can I go about reading the queue to do a count without trying to get exclusive access on it. I just need to count it.
Thank you
Regards
I used performance Counter to read the queue.
Working like a dream now!
Added the catch exception. This is for when the queue is blank. I write a 0. Performance counter gives error on blank queue.
public void Count(String path)
{
path = path.Remove(0, 21);
try
{
PerformanceCounter queueCounter = new PerformanceCounter(
"MSMQ Queue",
"Messages in Queue",
#path);
Console.WriteLine("Queue contains {0} messages",
queueCounter.NextValue().ToString());
myPrivateQueuesCount.Add((int)queueCounter.NextValue());
}
catch (Exception exc)
{
myPrivateQueuesCount.Add(0);
}
return;
}

Azure service bus queue Message deadlettered after Message.Abandon

I am trying out Azure Service Bus queue. I have the below code:
Queue send:
string strConnectionString = ConfigurationManager.AppSettings["Microsoft.ServiceBus.ConnectionString"];
var namespaceManager = NamespaceManager.CreateFromConnectionString(strConnectionString);
if (!namespaceManager.QueueExists("Test"))
{
QueueDescription qD = new QueueDescription("Test");
qD.DefaultMessageTimeToLive = new TimeSpan(05, 00, 00);
qD.LockDuration = new TimeSpan(00, 02, 30);
qD.MaxSizeInMegabytes = 5120;
namespaceManager.CreateQueue(qD);
}
if (namespaceManager.QueueExists("Test"))
{
QueueClient client = QueueClient.CreateFromConnectionString(strConnectionString, "Test", ReceiveMode.PeekLock);
var qMessage = Console.ReadLine();
using (MemoryStream strm = new MemoryStream(Encoding.UTF8.GetBytes(qMessage)))
{
BrokeredMessage bMsg = new BrokeredMessage(strm);
bMsg.MessageId = Guid.NewGuid().ToString();
bMsg.TimeToLive = new TimeSpan(05, 00, 00);
client.Send(bMsg);
Console.WriteLine("Message sent");
}
}
Console.ReadLine();
The receive code:
string strConnectionString = ConfigurationManager.AppSettings["Microsoft.ServiceBus.ConnectionString"];
var namespaceManager = NamespaceManager.CreateFromConnectionString(strConnectionString);
if (namespaceManager.QueueExists("Test"))
{
QueueClient client = QueueClient.CreateFromConnectionString(strConnectionString, "Test",ReceiveMode.PeekLock);
if (client != null)
{
OnMessageOptions options = new OnMessageOptions();
options.AutoComplete = false;
options.AutoRenewTimeout = TimeSpan.FromSeconds(31);
client.OnMessage((message) =>
{
Console.WriteLine(message.State.ToString());
Console.WriteLine("Message Id: " + message.MessageId);
Stream stream = message.GetBody<Stream>();
StreamReader reader = new StreamReader(stream);
Console.WriteLine("Message: " + reader.ReadToEnd());
Console.WriteLine("***************");
message.Abandon();
});
Console.ReadLine();
}
}
I see that whenever I call Abandon, the message is getting DeadLettered. My assumption was that it should get Active and can be picked up by another client.
Your understanding of BrokeredMessage.Abandon Api is correct. It is intended to abandon the peek-lock acquired on the message (but NOT abandon the message itself) and hence, makes it available for other receivers to pick the Message up.
Here's how we envisioned different states of a peek-lock'ed message:
Basics first
The 'Why': If Customers need Competing-Consumer (Job-Queue) semantics - where they need multiple workers to simultaneously process different messages from a Queue with Exactly-Once guarantee - then they use the ReceiveMode.PeekLock. In this model, each worker (the queue receiver) needs a way to communicate the Progress of its Current message (Job) to other workers. Hence, brokeredMessage provides 4 functions to express the states.
The 'What':
if a message is successfully processed by the current Worker - call BrokeredMessage.Complete()
if the BrokeredMessage cannot be processed by the current worker, and want the processing to be retried on another Worker - then, Abandon the message. But, the catch here is: lets say, there are 2 workers and each of them thinks that the other one can process this message and calls Abandon - soon they will end up in an Infinite loop of retry'ing to process just that message! So, to avoid this situation, we provided a Configuration called MaxDeliveryCount on QueueDescription. This setting guards the limit on the number of times the message is delivered from the Queue to receiver. In the above example, Each time you received (and abandoned) the message, the 'deliveryCount' on the ServiceBus service is incremented. When it reaches 10 - the message has hit max no. of deliveries and hence, will be deadlettered.
if the current receiver (worker) knows for sure, that, this message cannot be processed, BrokeredMessage.DeadLetter(). The goal here is to let the consuming application Audit the dead-lettered messages regularly.
if the current receiver (worker) cannot process this message, but, knows that this message can be processed at a later point of time BrokeredMessage.Defer().
HTH!
Sree

IOException every time when calling SerialPort.BaseStream.EndRead()

I try to get data asynchronously from a serial port. Mainly because DataReceived event seems not to be reliable enough and we end up with RXOver errors.
Target is .NET 4.0, so we need to use the older Begin/End methods.
I also learned that it is needed to call BeginRead() within a thread from the ThreadPool, otherwise the initiating thread would have terminated already when processing the passed callback.
However, even with a pooled thread I always get an IOException "The I/O operation has been aborted because of either a thread exit or an application request".
The underlying com port is open.
Any advise welcome.
#region DataReceiving
private readonly byte[] buffer = new byte[MAX_BUFFER_SIZE];
private void StartDataReceiving()
{
ThreadPool.QueueUserWorkItem( state => this.AsyncDataReceiving() );
}
private void AsyncDataReceiving()
{
this.serialPort.BaseStream.BeginRead(
this.buffer, 0, this.buffer.Length,
asyncResult =>
{
try
{
int actualLength = this.serialPort.BaseStream.EndRead( asyncResult );
byte[] received = new byte[actualLength];
Buffer.BlockCopy( this.buffer, 0, received, 0, actualLength );
this.dataConsumer.Add( received );
}
catch (IOException ex)
{
this.HandleSerialError( ex );
}
// Setup receiving action again
this.AsyncDataReceiving();
}
, null );
}
#endregion

C# TCP Async BeginSend Callback Never Happens

I'm working on a game in Unity with C#, and using TCP to exchange data between the server and the client.
I'm using async calls to connect and that seems to work fine. Then a handshake/auth happens as the server prompts the client for client version, client replies, server asks for player's name, player replies, and if all good, server notifies client has been accepted, otherwise connection is closed.
That works most of the time. However, in about 1 in 20, on the very first msg (server->client request for client version, and within a few seconds of game start), the async callback from the first BeginSend never gets called, thus halting the auth process. The client does receive the message based on the logging I have in place, and verified via debugging.
The server's call is:
m_isSending = true;
m_socket.BeginSend(byteArray.buffer, 0, byteArray.arrayLen, SocketFlags.None, new AsyncCallback(EndAsyncWrite), byteArray);
I've added m_isSending to help more easily debug/track whether a message is being sent to confirm if the callback has been called.
with EndAsyncWrite being:
protected void EndAsyncWrite(IAsyncResult iar)
{
m_isSending = false;
m_socket.EndSend(iar);
ByteArray byteArray = (iar.AsyncState as ByteArray);
//Add prev msg's ByteArray to await recycling
lock (m_byteArraysAwaitingRecycle)
{
m_byteArraysAwaitingRecycle.Add(byteArray);
}
}
In those 1 in 20 situations, m_isSending will still be true even after the client has received and processed the message. I've poked around in the debugger, but due to, what I assume is Unity's utilizing of mono, can't peer too far in. I have however found the message in the socket's writeQ (which I assume is the queue for writing). Normally it is empty, so I'm wondering if it might be able to shed some light into why the callback isn't being called.
Watch Snippet. The only entry present is the message that was sent and the client received.
Other info: nagle's is disable, blocking is set to true. The other 19/20 tries seem to work alright, with no changes. I'm currently testing with localhost as destination.
So I'm pretty confused as I'm doing everything properly as far as I can tell. Why wouldn't the callback get called? Any ideas? Any suggestions? Is there any way to work around this?
After doing some more testing, I've come to the conclusion it's a bug/effect on threads while running in Unity Editor. Here's how I came to that conclusion:
I noticed the problem occurred more often the first time opening the Unity project and starting the game. Stopping, and then starting, usually would work. It'd always work within 2-3 tries and continue to work for at least another dozen or so attempts. That could of course indicate some kind of race condition or threading concurrency issue in my own code, so I did the following: 1) Made a very stripped down tcplistener/tcpclient project in Unity, eliminating any caching/recycling of byte[] arrays or other things that may inadvertently affect the async or overall performance. 2) I tested this new project both in editor, and as a standalone build to check for outcome.
This of course requires unity, though it could probably just as easily be adopted to a .net console application for further evaluation. The project consisted of one scene, with a game camera the following three scripts attached to the camera. You need to drag/drop the TCPClient and TCPServer references onto ConnectGUI once attached to the gameobject. The code:
ConnectGUI.cs
using UnityEngine;
using System.Collections;
public class ConnectGUI : MonoBehaviour {
public enum ConnectionState
{
NotConnected,
AttemptingConnect,
Connected
}
public TCPClient client;
public TCPServer server;
// Use this for initialization
void Start ()
{
client.connectState = ConnectionState.NotConnected;
}
// Update is called once per frame
void Update () {
}
void OnGUI()
{
GUI.Label(new Rect(10, 10, Screen.width - 20, 20), client.connectState.ToString());
if (client.connectState == ConnectionState.NotConnected)
{
if (GUI.Button(new Rect(Screen.width * 0.5f - 200, Screen.height * 0.5f - 40, 400, 80), "Connect"))
{
server.StartServer();
System.Threading.Thread.Sleep(10);
client.StartConnect();
}
}
}
}
TCPClient.cs
using UnityEngine;
using System.Collections;
using System.Net;
using System.Net.Sockets;
public class TCPClient : MonoBehaviour {
public ConnectGUI.ConnectionState connectState;
Socket m_clientSocket;
byte[] m_readBuffer;
void Start()
{
connectState = ConnectGUI.ConnectionState.NotConnected;
m_readBuffer = new byte[1024];
}
public void StartConnect()
{
m_clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
System.IAsyncResult result = m_clientSocket.BeginConnect("127.0.0.1", 10000, EndConnect, null);
bool connectSuccess = result.AsyncWaitHandle.WaitOne(System.TimeSpan.FromSeconds(10));
if (!connectSuccess)
{
m_clientSocket.Close();
Debug.LogError(string.Format("Client unable to connect. Failed"));
}
}
catch (System.Exception ex)
{
Debug.LogError(string.Format("Client exception on beginconnect: {0}", ex.Message));
}
connectState = ConnectGUI.ConnectionState.AttemptingConnect;
}
void EndConnect(System.IAsyncResult iar)
{
m_clientSocket.EndConnect(iar);
m_clientSocket.NoDelay = true;
connectState = ConnectGUI.ConnectionState.Connected;
BeginReceiveData();
Debug.Log("Client connected");
}
void OnDestroy()
{
if (m_clientSocket != null)
{
m_clientSocket.Close();
m_clientSocket = null;
}
}
void BeginReceiveData()
{
m_clientSocket.BeginReceive(m_readBuffer, 0, m_readBuffer.Length, SocketFlags.None, EndReceiveData, null);
}
void EndReceiveData(System.IAsyncResult iar)
{
int numBytesReceived = m_clientSocket.EndReceive(iar);
ProcessData(numBytesReceived);
BeginReceiveData();
}
void ProcessData(int numBytesRecv)
{
string temp = TCPServer.CompileBytesIntoString(m_readBuffer, numBytesRecv);
Debug.Log(string.Format("Client recv: '{0}'", temp));
byte[] replyMsg = new byte[m_readBuffer.Length];
System.Buffer.BlockCopy(m_readBuffer, 0, replyMsg, 0, numBytesRecv);
//Increment first byte and send it back
replyMsg[0] = (byte)((int)replyMsg[0] + 1);
SendReply(replyMsg, numBytesRecv);
}
void SendReply(byte[] msgArray, int len)
{
string temp = TCPServer.CompileBytesIntoString(msgArray, len);
Debug.Log(string.Format("Client sending: len: {1} '{0}'", temp, len));
m_clientSocket.BeginSend(msgArray, 0, len, SocketFlags.None, EndSend, msgArray);
}
void EndSend(System.IAsyncResult iar)
{
m_clientSocket.EndSend(iar);
byte[] msg = (iar.AsyncState as byte[]);
string temp = TCPServer.CompileBytesIntoString(msg, msg.Length);
Debug.Log(string.Format("Client sent: '{0}'", temp));
System.Array.Clear(msg, 0, msg.Length);
msg = null;
}
}
TCPServer.cs
using UnityEngine;
using System.Collections;
using System.Net;
using System.Net.Sockets;
public class TCPServer : MonoBehaviour
{
public enum TestMessageOrder
{
NotConnected,
Connected,
SendFirstMessage,
ReceiveFirstMessageReply,
SendSecondMessage,
ReceiveSecondMessageReply,
SendThirdMessage,
ReceiveThirdMessageReply,
Error,
Done
}
protected TcpListener m_tcpListener;
protected Socket m_testClientSocket;
protected byte[] m_readBuffer;
[SerializeField]
protected TestMessageOrder m_testClientState;
public void StartServer()
{
m_tcpListener = new TcpListener(IPAddress.Any, 10000);
m_tcpListener.Start();
StartListeningForConnections();
}
void StartListeningForConnections()
{
m_tcpListener.BeginAcceptSocket(AcceptNewSocket, m_tcpListener);
Debug.Log("SERVER ACCEPTING NEW CLIENTS");
}
void AcceptNewSocket(System.IAsyncResult iar)
{
m_testClientSocket = null;
m_testClientState = TestMessageOrder.NotConnected;
m_readBuffer = new byte[1024];
try
{
m_testClientSocket = m_tcpListener.EndAcceptSocket(iar);
}
catch (System.Exception ex)
{
//Debug.LogError(string.Format("Exception on new socket: {0}", ex.Message));
}
m_testClientSocket.NoDelay = true;
m_testClientState = TestMessageOrder.Connected;
BeginReceiveData();
SendTestData();
StartListeningForConnections();
}
void SendTestData()
{
Debug.Log(string.Format("Server: Client state: {0}", m_testClientState));
switch (m_testClientState)
{
case TestMessageOrder.Connected:
SendMessageOne();
break;
//case TestMessageOrder.SendFirstMessage:
//break;
case TestMessageOrder.ReceiveFirstMessageReply:
SendMessageTwo();
break;
//case TestMessageOrder.SendSecondMessage:
//break;
case TestMessageOrder.ReceiveSecondMessageReply:
SendMessageTwo();
break;
case TestMessageOrder.SendThirdMessage:
break;
case TestMessageOrder.ReceiveThirdMessageReply:
m_testClientState = TestMessageOrder.Done;
Debug.Log("ALL DONE");
break;
case TestMessageOrder.Done:
break;
default:
Debug.LogError("Server shouldn't be here");
break;
}
}
void SendMessageOne()
{
m_testClientState = TestMessageOrder.SendFirstMessage;
byte[] newMsg = new byte[] { 1, 100, 101, 102, 103, 104 };
SendMessage(newMsg);
}
void SendMessageTwo()
{
m_testClientState = TestMessageOrder.SendSecondMessage;
byte[] newMsg = new byte[] { 3, 100, 101, 102, 103, 104, 105, 106 };
SendMessage(newMsg);
}
void SendMessageThree()
{
m_testClientState = TestMessageOrder.SendThirdMessage;
byte[] newMsg = new byte[] { 5, 100, 101, 102, 103, 104, 105, 106, 107, 108 };
SendMessage(newMsg);
}
void SendMessage(byte[] msg)
{
string temp = TCPServer.CompileBytesIntoString(msg);
Debug.Log(string.Format("Server sending: '{0}'", temp));
m_testClientSocket.BeginSend(msg, 0, msg.Length, SocketFlags.None, EndSend, msg);
}
void EndSend(System.IAsyncResult iar)
{
m_testClientSocket.EndSend(iar);
byte[] msgSent = (iar.AsyncState as byte[]);
string temp = CompileBytesIntoString(msgSent);
Debug.Log(string.Format("Server sent: '{0}'", temp));
}
void BeginReceiveData()
{
m_testClientSocket.BeginReceive(m_readBuffer, 0, m_readBuffer.Length, SocketFlags.None, EndReceiveData, null);
}
void EndReceiveData(System.IAsyncResult iar)
{
int numBytesReceived = m_testClientSocket.EndReceive(iar);
ProcessData(numBytesReceived);
BeginReceiveData();
}
void ProcessData(int numBytesRecv)
{
string temp = TCPServer.CompileBytesIntoString(m_readBuffer, numBytesRecv);
Debug.Log(string.Format("Server recv: '{0}'", temp));
byte firstByte = m_readBuffer[0];
switch (firstByte)
{
case 1:
Debug.LogError(string.Format("Server should not receive first byte of 1"));
m_testClientState = TestMessageOrder.Error;
break;
case 2:
m_testClientState = TestMessageOrder.ReceiveSecondMessageReply;
break;
case 3:
Debug.LogError(string.Format("Server should not receive first byte of 3"));
m_testClientState = TestMessageOrder.Error;
break;
case 4:
m_testClientState = TestMessageOrder.ReceiveThirdMessageReply;
break;
case 5:
Debug.LogError(string.Format("Server should not receive first byte of 5"));
m_testClientState = TestMessageOrder.Error;
break;
default:
Debug.LogError(string.Format("Server should not receive first byte of {0}", firstByte));
m_testClientState = TestMessageOrder.Error;
break;
}
SendTestData();
}
void OnDestroy()
{
if (m_testClientSocket != null)
{
m_testClientSocket.Close();
m_testClientSocket = null;
}
if (m_tcpListener != null)
{
m_tcpListener.Stop();
m_tcpListener = null;
}
}
public static string CompileBytesIntoString(byte[] msg, int len = -1)
{
string temp = "";
int count = len;
if (count < 1)
{
count = msg.Length;
}
for (int i = 0; i < count; i++)
{
temp += string.Format("{0} ", msg[i]);
}
return temp;
}
}
What this does is start's a TcpListener, and begins an async connection socket accept. Then a client socket is created and connects as a tcp socket (on port 10000, of 127.0.0.1). It turns of nagle's algorithm and the server sends a first message. The client receives the message, increments the first byte from 1->2 and returns the original message. Server then receives that message, and sends another message starting with 3. Client receives, increments 3->4 and echos back the rest of the message. Server then receives that, and sends a 3rd and last message starting with 5. Client turns 5->6 and sends back message. Once that occurs, the server prints "ALL DONE". Both server and client should print to log the various message contents (not always in the same order due to the nature of threading).
If for some reason "ALL DONE" is not printed, then the experiment has failed.
Running this in Unity Editor, it failed 10/10 on the first run, when immediately run after opening the editor. Subsequent attempts to run it resulted in mixed success for the 2nd and 3rd attempts. By the 4th attempt, I have no recorded failures.
I then compiled the project as a standalone program, and repeated the same number of attempts. Since it was reliant on the "ALL DONE" in the log, output.log was checked for "ALL DONE" and was found each time.
So, unless I'm misinterpretating the results, there is a problem either in Unity Editor's or its underlying mono version that is mucking about with threads which causes tcp async read/writes to fail in some capacity. However in the standalone builds, whatever that something is, thankfully, does not seem to appear to be a problem at least as far as testing on Windows allows.
I fully admit the testing was limited with only about 40 runs each but the results were significantly different, though I am too lazy to calculate actual significance. I am puzzled and a still bit concerned that it may be my own flawed implementation, since something like this is not more widespread; however Unity's own networking relies mainly on RPC calls, and most middleware fully embrace an exclusive UDP based network option.
If there is some fundamental flaw present, please let me know, otherwise I hope this may help some lost soul (as I was for almost two weeks) in the future as there is little to no searchable results on this topic. This was all done in Unity 4.6.1f1, but also tested by a friend in the present Unity 5 Beta (unsure of current beta version number).
Personally, while this is extremely annoying, I feel I can ignore this as an editor-only problem with little-to-no potential to impact actual players playing a compiled version. It will be something to heavily test once builds are regularly happening.

Issues with Thread in C#

I have written the code for Server/Client program in C#. Without using the Thread, it is working fine. If I use Thread, I am getting following error message.
The thread '' (0x9a8) has exited with code 0 (0x0).
Sample code
public class MyServer{
public MyServer (){
...
...
System.Threading.Thread socThread = new System.Threading.Thread(new System.Threading.ThreadStart(receiveSockets));
socThread.Start();
}
private void receiveSockets()
{
try
{
while(true){
IPAddress ipadd = IPAddress.Parse(systemIPAddress);
TcpListener tcp = new TcpListener(ipadd, 8001);
tcp.Start();
Console.WriteLine("Waiting for client in 8001 port no");
Socket socket = tcp.AcceptSocket();
Console.WriteLine("Client address : " + socket.RemoteEndPoint);
System.Threading.Thread socThread = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(receiveData));
socThread.Start(socket);
}
}
catch (Exception e)
{
Console.WriteLine("Error in receive Sockets : " + e.Message);
}
}
private void receiveData(object obj)
{
try
{
while(true){
Socket socket = (Socket)obj;
byte[] data = new byte[1000];
int status = socket.Receive(data);
Console.WriteLine("Received 1.");
string content = Encoding.UTF8.GetString(data, 0, 1000);
Console.WriteLine("Received data 1 : " + content);
}
}
catch (Exception e)
{
Console.WriteLine("Error in receive Data : " + e.Message);
}
}
static void Main(string[] args){
MyServer server = new MyServer();
}
Client Program
static void Main(string[] args)
{
TcpClient tcp = new TcpClient();
tcp.Connect("192.168.1.11", 8001);
Stream stream = tcp.GetStream();
String msg = "Testing...";
byte[] content = new byte[msg.Length * sizeof(char)];
System.Buffer.BlockCopy(msg.ToCharArray(), 0, content, 0, content.Length);
stream.Write(content, 0, content.Length);
}
I am getting the following output.
IP Addrress : 192.168.1.11
Waiting for client in 8001 port no
Client address : 192.168.1.11:50140
The thread '' (0x9a8) has exited with code 0 (0x0).
A first chance exception of type 'System.Net.Sockets.SocketException' occurred in System.dll
The thread '' (0x1760) has exited with code 0 (0x0).
Error in receive Data : An existing connection was forcibly closed by the remote host
The program '[1396] Window_Server.vshost.exe: Managed (v4.0.30319)' has exited with code 0 (0x0).
Please help me to fix this issue.
You need to figure out why you are throwing a socket exception. If you read the documentation for Socket.Receive You would see this section:
Note
If you receive a SocketException, use the SocketException.ErrorCode property to obtain the specific error code.
After you have obtained this code, refer to the Windows Sockets
version 2 API error code documentation in the MSDN library for a
detailed description of the error.
Follwing that link shows you how to read the error code:
The ErrorCode property contains the error code that is associated with
the error that caused the exception.
The default constructor for SocketException sets the ErrorCode
property to the last operating system error that occurred. For more
information about socket error codes, see the Windows Sockets version
2 API error code documentation in MSDN.
Which should bring you to the error codes page.
Now depending on your error code, which you have not provided, you can diagnose the network issue.
"The thread '' (0x9a8) has exited with code 0 (0x0)." is not an error. It is simply telling you that a background thread has exited. Zero means the thread ran and exited successfully.
The exception is in receiveData(object obj) as you should be able to tell, given the exception , "Error in receive Data : An existing connection was forcibly closed by the remote host".
If you post the client program you are working with I might be able to help.
The problem is that Main() does not wait for the sockets to be done with their jobs. As soon as it has created the threads, it exists... And the threads are destroyed.
You need to wait for the socket-handling threads by using events of some sort, or by sleeping, from Main() - or from MyServer(), as long as the program exists only when the whole job is done.

Categories

Resources