/// <summary> /// Contructs a msg from the SSH stream /// </summary> /// <param name="msg">RSProtoBuffSSHMsg (ref)</param> /// <param name="timeOut">timeout in ms ( 0 = default)</param> /// <param name="GetHeader">read header or skip to ProtoBufMsg</param> /// <returns>success?</returns> private bool ReadMsgFromStream(ref RSProtoBuffSSHMsg msg, uint timeOut = 0, bool GetHeader = true) { if (timeOut == 0) { timeOut = _timeOut; } byte[] input = new byte[16], buffer; uint magicCode; if (GetHeader) { // get Header if (!ReadFromStream(timeOut, 16, out buffer)) { return(false); } buffer.CopyTo(input, 0); // read header Array.Reverse(input); msg.BodySize = BitConverter.ToUInt32(input, 0); msg.ReqID = BitConverter.ToUInt32(input, 4); msg.MsgID = BitConverter.ToUInt32(input, 8); magicCode = BitConverter.ToUInt32(input, 12); System.Diagnostics.Debug.WriteLineIf(DEBUG, "rec: ReqID: " + msg.ReqID + " - MsgID: " + msg.MsgID + " - body size: " + msg.BodySize + " byte"); if (magicCode != msg.MagicCode) { System.Diagnostics.Debug.WriteLineIf(DEBUG, "rec: MagicCode mismatch"); _findMagicCode = true; _lastSize = msg.BodySize; _parent.Error(new Exception("MagicCode mismatch"), RSRPC.ErrorFrom.ProtoBuf); return(false); } if (!IsRpcMsgIdResponse(msg.MsgID)) { System.Diagnostics.Debug.WriteLineIf(DEBUG, "rec: msg is no response"); } } else { System.Diagnostics.Debug.WriteLineIf(DEBUG, "rec: ReqID: " + msg.ReqID + " - MsgID: " + msg.MsgID + " - body size: " + msg.BodySize + " byte"); } // get ProtoBufMsg byte[] PbMsg = new byte[msg.BodySize]; if (!ReadFromStream(timeOut, msg.BodySize, out buffer)) { return(false); } buffer.CopyTo(PbMsg, 0); msg.ProtoBuffMsg = new MemoryStream(); msg.ProtoBuffMsg.Write(PbMsg, 0, (int)msg.BodySize); msg.ProtoBuffMsg.Position = 0; return(true); }
/// <summary> /// wrapper for ReadMsgFromStream(..) /// </summary> /// <param name="msg">RSProtoBuffSSHMsg (out)</param> /// <param name="timeOut">timeout in ms ( 0 = default)</param> /// <returns>success?</returns> public bool Receive(out RSProtoBuffSSHMsg msg, uint timeOut = 0) { msg = new RSProtoBuffSSHMsg(); if (!_stream.CanRead) { System.Diagnostics.Debug.WriteLine("rec: cannot read stream!"); _parent.Error(new IOException("send: can't read stream"), RSRPC.ErrorFrom.ProtoBuf); return(false); } if (!ReadMsgFromStream(ref msg, timeOut)) { System.Diagnostics.Debug.WriteLine("rec: Error receiving data from stream"); return(false); } System.Diagnostics.Debug.WriteLineIf(DEBUG, "rec: " + msg.ReqID + " body Length: " + msg.ProtoBuffMsg.Length); return(true); }
/// <summary> /// Converts a msg into a a byte array in network order (ready to send) /// </summary> /// <param name="msg">input RSProtoBuffSSHMsg to convert</param> /// <returns>msg as a byte array</returns> private byte[] CreatePacketFromMsg(RSProtoBuffSSHMsg msg) { byte[] a = new byte[4]; byte[] pbMsg = new byte[msg.BodySize]; byte[] output = new byte[16 + msg.BodySize]; a = UintToByteNetwortOrder(msg.MagicCode); Array.Copy(a, 0, output, 0, 4); a = UintToByteNetwortOrder(msg.MsgID); Array.Copy(a, 0, output, 4, 4); a = UintToByteNetwortOrder(msg.ReqID); Array.Copy(a, 0, output, 8, 4); a = UintToByteNetwortOrder(msg.BodySize); Array.Copy(a, 0, output, 12, 4); msg.ProtoBuffMsg.Read(pbMsg, 0, (int)msg.BodySize); msg.ProtoBuffMsg.Position = 0; Array.Copy(pbMsg, 0, output, 16, (int)msg.BodySize); return(output); }
// ---------- send ---------- // ---------- generic send<T> ---------- public uint Send <T>(T pbMsg, uint inMsgID) { if (!_connected) { Error(new Exception("not connected"), ErrorFrom.RPC); return(0); } RSProtoBuffSSHMsg msg = new RSProtoBuffSSHMsg(); msg.MsgID = inMsgID; msg.ReqID = _rsProtoBuf.GetReqID(); msg.ProtoBuffMsg = new MemoryStream(); Serializer.Serialize <T>(msg.ProtoBuffMsg, pbMsg); msg.ProtoBuffMsg.Position = 0; msg.BodySize = (uint)msg.ProtoBuffMsg.Length; lock (_sendQueue) _sendQueue.Enqueue(msg); return(msg.ReqID); }
/// <summary> /// Wrtites a msg to the SSH stream /// </summary> /// <param name="msg">msg to send</param> /// <returns></returns> public uint Send(RSProtoBuffSSHMsg msg) { System.Diagnostics.Debug.WriteLineIf(DEBUG, "send: sending packet " + msg.ReqID + " MsgID: " + msg.MsgID + " body size: " + msg.BodySize); if (_stream.CanWrite) { try { _stream.Write(CreatePacketFromMsg(msg), 0, 16 + (int)msg.BodySize); _stream.Flush(); } catch (Exception e) { _parent.Error(e, RSRPC.ErrorFrom.ProtoBuf); } } else { System.Diagnostics.Debug.WriteLine("send: can't write stream"); _parent.Error(new IOException("send: can't write stream"), RSRPC.ErrorFrom.ProtoBuf); } return(msg.ReqID); }
/// <summary> /// Main loop to send and receive data from the SSH stream. /// /// Takes msgs from _sendQueue and sends them. /// Reads msgs and put them in _receiveQueue for further processing. /// Also reconnects when "MagicCode missmatch" occurs. /// /// Is designed to run in an own thread. /// Use _run = false to stop the loop. /// Use _finishQueue to send al pending msgs ( won't receive data anymore ) /// </summary> private void mainLoop() { bool foundWork; //byte[] header1 = new byte[4], header234 = new byte[12], buffer = new byte[1024 * 25]; RSProtoBuffSSHMsg msg = new RSProtoBuffSSHMsg(), newMsg = new RSProtoBuffSSHMsg(); while (_run) { foundWork = false; if (_stream.DataAvailable && _run && !_finishQueue && !_findMagicCode) { System.Diagnostics.Debug.WriteLineIf(DEBUG, "#######################################"); System.Diagnostics.Debug.WriteLineIf(DEBUG, "loop: receiving"); if (Receive(out newMsg)) { lock (_receiveQueue) _receiveQueue.Enqueue(newMsg); } foundWork = true; } if (_stream.DataAvailable && _findMagicCode) { if (!_parent.Reconnect(out _stream)) { _parent.Error(new Exception("Error while reconnecting"), RSRPC.ErrorFrom.ProtoBuf); _run = false; } else { _findMagicCode = false; } /* * this doesn't work yet * the idea is to search for the next MagicCode assuming it's the begin of a new msg * but this produce some strange behavior, is very slow and unreliable */ //System.Diagnostics.Debug.WriteLineIf(DEBUG, "#######################################"); //System.Diagnostics.Debug.WriteLineIf(DEBUG, "loop: trying to find Magic Code ..."); //for (short i = 0; i < 1024 * 25 / 4; i++) // 25k too much? //{ // if (!_stream.DataAvailable) // break; // if (!ReadFromStream(_timeOut, 4, out header1)) // break; // //buffer.CopyTo(header1, 0); // Array.Reverse(header1); // if (BitConverter.ToUInt32(header1, 0) == msg.MagicCode) // { // System.Diagnostics.Debug.WriteLineIf(DEBUG, "loop: found Magic Code"); // // found magic code -> try to receive whole msg // if (!ReadFromStream(_timeOut, 12, out header234)) // break; // //buffer.CopyTo(header234, 0); // Array.Reverse(header234); // newMsg.BodySize = BitConverter.ToUInt32(header234, 0); // newMsg.ReqID = BitConverter.ToUInt32(header234, 4); // newMsg.MsgID = BitConverter.ToUInt32(header234, 8); // System.Diagnostics.Debug.WriteLineIf(DEBUG, "rec: ReqID: " + msg.ReqID + " - MsgID: " + msg.MsgID + " - body size: " + msg.BodySize + " byte"); // if (!IsRpcMsgIdResponse(msg.MsgID)) // { // // is no response ( the magic code was a wrong one - or the response was somehow broken ) // System.Diagnostics.Debug.WriteLineIf(DEBUG, "loop: no response"); // continue; // } // if (ReadMsgFromStream(msg: ref newMsg, GetHeader: false)) // { // // there is nothing more we can do here ... just put the msg in the queue and hope the best :D // System.Diagnostics.Debug.WriteLineIf(DEBUG, "loop: received msg"); // lock (_receiveQueue) // _receiveQueue.Enqueue(newMsg); // _findMagicCode = false; // break; // } // else // System.Diagnostics.Debug.WriteLineIf(DEBUG, "loop: couldn't get msg"); // } //} // for //foundWork = true; } if (_sendQueue.Count > 0) { System.Diagnostics.Debug.WriteLineIf(DEBUG, "#######################################"); System.Diagnostics.Debug.WriteLineIf(DEBUG, "loop: sending req"); lock (_sendQueue) msg = _sendQueue.Dequeue(); Send(msg); foundWork = true; } if (!foundWork && _run && !_finishQueue) { System.Threading.Thread.Sleep(250); } if (_finishQueue && _sendQueue.Count == 0) { _run = false; } } }