/// <summary> /// This is the heart of the CII. This takes data blobs from the backend manager /// and interprets it according to the spec. /// </summary> /// <remarks> /// This is called by the backend connected to us. It needs to know about /// the CiiClient. This always runs on a worker thread internal to this /// library. A user should never call this. /// </remarks> /// <param name="buffer">The byte array where our data lives.</param> /// <param name="dataLength">Length valid data in the array. (dataLength != buffer.Length)</param> public void RouteReceivedMessage(byte[] buffer, int dataLength) { uint sequenceNumber; uint statusCode; uint subcommand; CiiMessageTracker messageTracker; uint substatus; CiiMessageType type = (CiiMessageType)BitConverter.ToUInt32(buffer, 0); switch (type) { case CiiMessageType.MtAccept: logger.Log("ACCEPT", buffer, dataLength); grantedAccess = (CiiAccessLevel)BitConverter.ToInt32(buffer, 4); loginAcceptEvent.Set(); break; case CiiMessageType.MtAck: logger.Log("ACK", buffer, dataLength); sequenceNumber = BitConverter.ToUInt32(buffer, 4); messageTracker = messagesInFlight.Retrieve(sequenceNumber); if (messageTracker == null) { SendAsyncError("Protocol Failure - Unexpected ACK"); break; } if (messageTracker.AckReceived) { // // Error! Double ACK! // messagesInFlight.Delete(sequenceNumber); SendAsyncError("Protocol Failure - Double ACK"); break; } else { messageTracker.AckReceived = true; } if ((messageTracker.Completion != null) && (messageTracker.Completion.AckHandler != null)) { messageTracker.Completion.AckHandler( messageTracker.Completion.UserData, sequenceNumber); } else { Debug.WriteLine("Discarding ACK for Sequence # " + sequenceNumber); } break; case CiiMessageType.MtNak: logger.Log("NAK", buffer, dataLength); sequenceNumber = BitConverter.ToUInt32(buffer, 4); statusCode = BitConverter.ToUInt32(buffer, 8); messageTracker = messagesInFlight.Retrieve(sequenceNumber); if (messageTracker == null) { SendAsyncError("Protocol Failure - Unexpected NAK"); break; } messagesInFlight.Delete(sequenceNumber); if (messageTracker.AckReceived) { // // Error! ACK / NAK! // SendAsyncError("Protocol Failure - ACK - NAK"); break; } if ((messageTracker.Completion != null) && (messageTracker.Completion.NakHandler != null)) { messageTracker.Completion.NakHandler( messageTracker.Completion.UserData, sequenceNumber, statusCode); } else { Debug.WriteLine("Discarding NAK for Sequence # " + sequenceNumber); } messageTracker = null; break; case CiiMessageType.MtResponse: logger.Log("RSP", buffer, dataLength); sequenceNumber = BitConverter.ToUInt32(buffer, 4); subcommand = BitConverter.ToUInt32(buffer, 8); statusCode = BitConverter.ToUInt32(buffer, 12); messageTracker = messagesInFlight.Retrieve(sequenceNumber); if (messageTracker == null) { SendAsyncError("Protocol Failure - Unexpected RSP"); break; } messagesInFlight.Delete(sequenceNumber); if (!messageTracker.AckReceived) { // // Error! No ACK! // SendAsyncError("Protocol Failure - Missing ACK"); break; } if ((messageTracker.Completion != null) && (messageTracker.Completion.ResponseHandler != null)) { messageTracker.Completion.ResponseHandler( messageTracker.Completion.UserData, sequenceNumber, subcommand, statusCode, buffer, 16, dataLength - 16); } else { Debug.WriteLine("Discarding RSP for Sequence # " + sequenceNumber); } break; case CiiMessageType.MtStatus: logger.Log("STAT", buffer, dataLength); substatus = BitConverter.ToUInt32(buffer, 4); if (connectionState != ConnectionState.Connected) { Debug.WriteLine("Throwing away early status message"); break; } lock (statusCallbacksLock) { ReceiveStatusHandler callback; StatusCallbacks.TryGetValue(substatus, out callback); if ((callback != null) && (dataLength >= 8)) { callback(substatus, buffer, 8, dataLength - 8); } else if ((UnhandledStatusCallback != null) && (dataLength >= 8)) { UnhandledStatusCallback(substatus, buffer, 8, dataLength - 8); } } break; // // We should never see another type of message here. // This is an asymetric protocol between client and server. // default: logger.Log("UNKNOWN", buffer, dataLength); SendAsyncError("Unknown MessageType! " + type.ToString()); break; } }
private bool Login(CiiAccessLevel requestedAccess) { byte[] LoginBuffer; byte[] MyAddress = BackEndManager.GetLocalAddress(); byte[] Access = BitConverter.GetBytes((uint)requestedAccess); int CopyLength; #if WindowsCE byte[] Username = System.Text.Encoding.UTF8.GetBytes("Display"); byte[] MachineName = System.Text.Encoding.UTF8.GetBytes("Cortex"); #else byte[] Username = System.Text.Encoding.UTF8.GetBytes(Environment.UserName); byte[] MachineName = System.Text.Encoding.UTF8.GetBytes(Environment.MachineName); #endif LoginBuffer = new byte[BytesLogin.Length + Access.Length + MyAddress.Length + 64 + 64]; Array.Copy(BytesLogin, 0, LoginBuffer, 0, BytesLogin.Length); Array.Copy(Access, 0, LoginBuffer, 4, Access.Length); Array.Copy(MyAddress, 0, LoginBuffer, 8, MyAddress.Length); CopyLength = Username.Length > 64 ? 64 : Username.Length; Array.Copy(Username, 0, LoginBuffer, 12, CopyLength); CopyLength = MachineName.Length > 64 ? 64 : MachineName.Length; Array.Copy(MachineName, 0, LoginBuffer, 76, CopyLength); loginAcceptEvent.Reset(); logger.Log("LOGIN", LoginBuffer, LoginBuffer.Length); bool Success = BackEndManager.SendMessage(LoginBuffer); if (!Success) { SendAsyncError("Failed Login!"); } else { Success = loginAcceptEvent.WaitOne(LoginTimeout, false); if (!Success) { SendAsyncError("Login Accept timed out! " + LoginTimeout + " ms"); } } return Success; }
/// <summary> /// Client invoked code. /// </summary> /// <param name="requestedAccess"></param> /// <returns></returns> public bool Connect(CiiAccessLevel requestedAccess) { if (connectionState != ConnectionState.NotConnected) { return false; } bool success = BackEndManager.Connect(); if (success) { connectionState = ConnectionState.WaitingForLogin; success = Login(requestedAccess); if (success) { connectionState = ConnectionState.Connected; ConnectEventHandler callbacks = ConnectEvent; if (callbacks != null) { callbacks(this, new EventArgs()); } } else { BackEndManager.Disconnect(); connectionState = ConnectionState.NotConnected; } } return success; }