/// <summary> /// /// </summary> public void ThreadProc() { bool bCont = true; int iSockBytes = 0; int iMsgBytes = 0; int iArrayIndex = 0; int iVmcIndex = -1; byte[] baMsg; // Buffer received from socket. AMSockData amsData; // Message extraced from socket buffer. byte[] baAudio; // Audio extracted from socket msg. ISMessaging.ISAppEndpoint mSrc; // Indicates where msg came from. ISMessaging.ISMVMC mVMC; // VMC string sSessionId = ""; string sMsgType = ""; m_Logger.Init("", "", Thread.CurrentThread.Name, "", "", ""); Log(Level.Verbose, "[TI:" + m_iThreadIndex.ToString() + "]" + "ARMsgListenerThread started."); // Initialize session-id collection - Should be the same size as the number of VMCs. // FIX - This may be more than this particular system handles. for (int ii = 0; ii < m_aqAudioIn.Length; ii++) { m_asSessionIds.Add(""); } m_SockConn.m_sockARRead = m_SockConn.m_listenARRead.AcceptSocket(); if (m_SockConn.m_sockARRead != null) { baMsg = new Byte[AMSockData.SIZEOPPACKET]; // Make sure that baAudio size is such as to hold an integer number of audio data packets that collectively are at least 250 ms worth of audio. baAudio = new Byte[AMSockData.SIZE025SECAUDIO + (AMSockData.SIZE025SECAUDIO % AMSockData.SIZEAUDIODATA)]; amsData = new AMSockData(ref m_Logger); iArrayIndex = 0; ISMessaging.Audio.ISMRawData.MuClear(baAudio); while (bCont) // FIX - use failure counter (to prevent infinite looping) { try { // Receive data from socket and extract the message. iSockBytes = m_SockConn.m_sockARRead.Receive(baMsg, 0, baMsg.Length, SocketFlags.None); if (iSockBytes > 0) { amsData.Extract(baMsg); if (baMsg == null) { Log(Level.Exception, "[TI:" + m_iThreadIndex.ToString() + "] ARMLT Extract returned null message!"); } else { // If this is a new session coming in, make sure to grab a new VMC, otherwise reuse the one already open. sMsgType = amsData.m_Msg.GetType().ToString(); if (sMsgType == "ISMessaging.Session.ISMSessionBegin") { iVmcIndex = m_RM.AddSession(m_iThreadIndex, amsData.m_sMsgSrc); //iVmcIndex = m_RM.AddSession(m_iThreadIndex, amsData.m_sMsgSrc, 4, 250); if (iVmcIndex == -1) { sSessionId = ""; // The error will get logged after the check below } else { sSessionId = m_RM.GetSessionId(iVmcIndex); } } else { sSessionId = m_RM.GetSessionId(iVmcIndex); if ((sSessionId == null) || (sSessionId.Length == 0)) { iVmcIndex = -1; } else { mVMC = m_RM.GetVMCBySessionid(sSessionId); iVmcIndex = mVMC.m_iKey; } } if (iVmcIndex == -1) { if (sMsgType != "ISMessaging.Audio.ISMRawData") { Log(Level.Exception, string.Format("[TI:{0}] ARMLT got invalid VMC index: {1} on msg: '{2}'.", iVmcIndex.ToString(), iVmcIndex.ToString(), sMsgType)); } } else { m_SockConn.m_VMC = m_RM.GetVMCByKey(iVmcIndex); if (m_SockConn.m_VMC == null) { Log(Level.Exception, "[TI:" + m_iThreadIndex.ToString() + "] ARMLT.ThreadProc() returned VMC was null on VMC index: " + iVmcIndex.ToString() + "on msg: " + amsData.m_Msg.GetType().ToString()); } else if (m_SockConn.m_VMC.m_iKey != iVmcIndex) { Log(Level.Exception, "[TI:" + m_iThreadIndex.ToString() + "] ARMLT.ThreadProc() returned VMC key: " + m_SockConn.m_VMC.m_iKey + " didn't match VMC index: " + iVmcIndex.ToString() + "on msg: " + amsData.m_Msg.GetType().ToString()); } else { switch (sMsgType) { case "ISMessaging.Audio.ISMRawData": { // Grab the session id amsData.m_Msg.m_sSessionId = m_asSessionIds[m_iThreadIndex]; // Should also be the same as sSessionId // Copy audio data from message, if the 1sec buffer is full, forward it on. iMsgBytes = amsData.m_abData.Length; // Do we have enough audio data to pass it to AudioInThread? If not then just save it until we do. if ((iArrayIndex + iMsgBytes) <= baAudio.Length) { Array.Copy(amsData.m_abData, 0, baAudio, iArrayIndex, iMsgBytes); iArrayIndex += iMsgBytes; } else { // Forward audio data mSrc = new ISAppEndpoint(); mVMC = new ISMVMC(); mVMC.Init(iVmcIndex, m_SockConn.m_VMC.m_sDescription, m_SockConn.m_VMC.m_sSessionId); mSrc.Init(mVMC, amsData.m_sMsgSrc, EApplication.eAudioMgr, "Raw data"); ((ISMessaging.Audio.ISMRawData)(amsData.m_Msg)).Init(mSrc, null, ISMessaging.Audio.SOUND_FORMAT.ULAW_8KHZ, baAudio, iArrayIndex); // NOTE - i_Dest is currently null, we're assuming that it isn't used. m_aqAudioIn[iVmcIndex].Push(amsData.m_Msg); // FIX - get array index from message. // Reset index, clear data iArrayIndex = 0; ISMessaging.Audio.ISMRawData.MuClear(baAudio); // Save the audio data that triggered the sending of audio data to AudioInThread but wasn't included in the actual message sent. Array.Copy(amsData.m_abData, 0, baAudio, iArrayIndex, iMsgBytes); iArrayIndex += iMsgBytes; } } break; case "ISMessaging.Audio.ISMDtmf": { // Grab the session id amsData.m_Msg.m_sSessionId = m_asSessionIds[m_iThreadIndex]; // Should also be the same as sSessionId m_aqAudioIn[iVmcIndex].Push(amsData.m_Msg); } break; case "ISMessaging.Session.ISMSessionBegin": { // Store the session-id in the local array and the message m_asSessionIds[m_iThreadIndex] = sSessionId; amsData.m_Msg.m_sSessionId = sSessionId; // Set IpAddress, SessionId in Logger so all subsequent calls to Log() will be identified. (Need-to/should do this in every thread once the ISMSessionBegin comes in.) m_Logger.UpdateValue(Thread.CurrentThread.Name, LoggerCore.eRequiredParams.IpAddress.ToString(), Dns.GetHostEntry(Dns.GetHostName()).AddressList.FirstOrDefault(oAddr => oAddr.AddressFamily == AddressFamily.InterNetwork).ToString() ?? ""); m_Logger.UpdateValue(Thread.CurrentThread.Name, LoggerCore.eRequiredParams.SessionId.ToString(), sSessionId); m_Logger.Log(Level.Info, "[TI:" + m_iThreadIndex.ToString() + "][VMC:" + iVmcIndex + "] <<<ARMLT ISMessaging.Session.ISMSessionBegin(F:'" + ((ISMessaging.Session.ISMSessionBegin)amsData.m_Msg).m_sFromAddr + "', T:'" + ((ISMessaging.Session.ISMSessionBegin)amsData.m_Msg).m_sToAddr + "')>>>"); // m_aqAudioOut[iVmcIndex].Clear(); // FIX: Is this clear causing messages we want to receive to be deleted? Or is it helping us by clearing out residual messages that are meaningless to the new session? m_aqAudioOut[iVmcIndex].Push(amsData.m_Msg); // Push to qAudioOut first, so that socket handling gets done first //m_aqAudioIn[iVmcIndex].Clear(); // Moved this to Begin handler in AudioOut to ensure proper delivery order. //m_aqAudioIn[iVmcIndex].Push((ISMessaging.ISMsg)((ISMessaging.Session.ISMSessionBegin)(amsData.m_Msg)).Clone()); // Clone, just in case Mono is not properly ref-counting. If it isn't, will this cause a leak? } break; case "ISMessaging.Session.ISMSessionEnd": { // Grab the session id amsData.m_Msg.m_sSessionId = m_asSessionIds[m_iThreadIndex]; // Should also be the same as sSessionId // FIX - What happens if there is a socket exception caught in AOT? It will forward an ISMSessionEnd to AIT, but we'll never get here and clear baAudio. m_Logger.Log(Level.Info, "[TI:" + m_iThreadIndex.ToString() + "][VMC:" + iVmcIndex + "] <<<ARMLT ISMessaging.Session.ISMSessionEnd>>>"); m_aqAudioOut[iVmcIndex].Push(amsData.m_Msg); // Moved the Push to audio-in thread below to AudioOut's End to ensure no overlap //m_aqAudioIn[iVmcIndex].Push((ISMessaging.ISMsg)((ISMessaging.Session.ISMSessionEnd)(amsData.m_Msg)).Clone()); // Pass 'end' on to AudioOut so it can reset socket. // FIX - get array index from message. SEE CLONE COMMENT ABOVE // Reset index, clear data iArrayIndex = 0; ISMessaging.Audio.ISMRawData.MuClear(baAudio); } break; default: { //Console.Error.WriteLine("AudioMgr.AudioEngine_srv.ARMsgListenerThread() Got unknown message from AudioRouter: '{0}'.", amsData.m_Msg.GetType().ToString()); Log(Level.Exception, "[TI:" + m_iThreadIndex.ToString() + "][VMC:" + iVmcIndex + "] ARMLT Got unknown message from AudioRouter: " + amsData.m_Msg.GetType().ToString()); } break; } // switch } // if(!iVmcIndex) //Console.Write("*"); } // else vmcindex != -1 } // else msg != null } // if else { // Receive returned 0 bytes, socket has probably been closed down by other side. // Close socket and wait for new connection. m_SockConn.m_VMC = null; //m_RM.ReleaseSession(iVmcIndex); // Fix - Here or ProcessSessionEnd()? try { if (m_SockConn.m_sockARRead.Connected) { m_SockConn.m_sockARRead.Shutdown(SocketShutdown.Both); } } catch (SocketException exc2) { Log(Level.Exception, "[TI:" + m_iThreadIndex.ToString() + "][VMC:" + iVmcIndex + "] ARMLT caught exception when calling Shutdown: " + exc2.ErrorCode.ToString()); } try { m_SockConn.m_sockARRead.Close(); } catch (SocketException exc2) { Log(Level.Exception, "[TI:" + m_iThreadIndex.ToString() + "][VMC:" + iVmcIndex + "] ARMLT caught exception when calling Close: " + exc2.ErrorCode.ToString()); } m_SockConn.m_sockARRead = m_SockConn.m_listenARRead.AcceptSocket(); } } catch (SocketException exc) { // Most likely, the AudioRtr dropped out. // Close socket and wait for new connection. m_SockConn.m_VMC = null; //m_RM.ReleaseSession(iVmcIndex); // Fix - Here or ProcessSessionEnd()? try { // WSAENOTSOCK == 10038, WSAEINTR == 10004 if (exc.ErrorCode == 10038) { // Do nothing here, the socket will get nulled below. Log(Level.Exception, "[TI:" + m_iThreadIndex.ToString() + "][VMC:" + iVmcIndex + "] ARMLT: socket NOTSOCK."); } else if (exc.ErrorCode == 10004) { // Do nothing here, the socket will get nulled below. Log(Level.Exception, "[TI:" + m_iThreadIndex.ToString() + "][VMC:" + iVmcIndex + "] ARMLT: socket INTR."); } else { Log(Level.Exception, "[TI:" + m_iThreadIndex.ToString() + "][VMC:" + iVmcIndex + "] ARMLT: " + exc.ToString()); if (m_SockConn == null) { Log(Level.Exception, "[TI:" + m_iThreadIndex.ToString() + "][VMC:" + iVmcIndex + "] ARMLT: m_SockConn was null!!! 1"); } else if (m_SockConn.m_sockARRead == null) { Log(Level.Exception, "[TI:" + m_iThreadIndex.ToString() + "][VMC:" + iVmcIndex + "] ARMLT: m_SockConn.m_sockARRead was null."); } else if (!m_SockConn.m_sockARRead.Connected) { Log(Level.Exception, "[TI:" + m_iThreadIndex.ToString() + "][VMC:" + iVmcIndex + "] ARMLT: Socket was not connected."); } else { if (m_SockConn.m_sockARRead.Connected) { Log(Level.Exception, "[TI:" + m_iThreadIndex.ToString() + "][VMC:" + iVmcIndex + "] ARMLT: socket.Shutdown..."); m_SockConn.m_sockARRead.Shutdown(SocketShutdown.Both); } Log(Level.Exception, "[TI:" + m_iThreadIndex.ToString() + "][VMC:" + iVmcIndex + "] ARMLT: socket.Close..."); m_SockConn.m_sockARRead.Close(); Log(Level.Exception, "[TI:" + m_iThreadIndex.ToString() + "][VMC:" + iVmcIndex + "] ARMLT: socket.Closed."); } } } catch (SocketException exc2) { Log(Level.Exception, "[TI:" + m_iThreadIndex.ToString() + "] ARMLT socket error in cleanup: " + exc2.ErrorCode.ToString()); } catch (Exception exc2) { Log(Level.Exception, "[TI:" + m_iThreadIndex.ToString() + "] ARMLT Exception in cleanup: " + exc2.ToString()); } if (m_SockConn == null) { Log(Level.Exception, "[TI:" + m_iThreadIndex.ToString() + "][VMC:" + iVmcIndex + "] ARMLT: m_SockConn was null!!! Nothing more we can do, exiting..."); break; } else { Log(Level.Exception, "[TI:" + m_iThreadIndex.ToString() + "][VMC:" + iVmcIndex + "] ARMLT: socket.AcceptSocket..."); m_SockConn.m_sockARRead = m_SockConn.m_listenARRead.AcceptSocket(); } Log(Level.Exception, "[TI:" + m_iThreadIndex.ToString() + "][VMC:" + iVmcIndex + "] ARMLT: Socket exception handling complete."); } catch (Exception exc) { Log(Level.Exception, "[TI:" + m_iThreadIndex.ToString() + "][VMC:" + iVmcIndex + "] ARMLT: " + exc.ToString()); } } // while } if (m_SockConn == null) { Log(Level.Exception, "[TI:" + m_iThreadIndex.ToString() + "][VMC:" + iVmcIndex + "] ARMLT: m_SockConn was null!!! 3"); } else { if (m_SockConn.m_sockARRead != null) { m_SockConn.m_sockARRead.Shutdown(SocketShutdown.Both); m_SockConn.m_sockARRead.Close(); } if (m_SockConn.m_listenARRead != null) { m_SockConn.m_listenARRead.Stop(); } } Log(Level.Exception, "[TI:" + m_iThreadIndex.ToString() + "][VMC:" + iVmcIndex + "] ARMLT: Exiting thread now."); } // ThreadProc()