/// <summary> /// This implementation of the ReleaseSession method is /// invoked to simultaneously release any locks that are /// currently being held on a session and update the info /// for the session in the database. /// </summary> /// <param name="sessionId"> /// The session id of the session being released. /// </param> /// <param name="record"> /// The updated record for the session being released. /// </param> public void ReleaseSession(string sessionId, VfxFixDatabaseRecord record) { string sessionPath = Path.Combine(_rootPath, sessionId); string sessionFile = Path.Combine(sessionPath, "Session.xml"); // REC: Write the updated session details out to // the session record file: XmlFixDatabaseRecord xmlRecord = new XmlFixDatabaseRecord(); xmlRecord.TxSequence = record.TxSequence; xmlRecord.RxSequence = record.RxSequence; FileStream fsSession = new FileStream(sessionFile, FileMode.Create, FileAccess.Write, FileShare.None); XmlSerializer xsSession = new XmlSerializer(typeof(XmlFixDatabaseRecord)); xsSession.Serialize(fsSession, xmlRecord); fsSession.Close(); // REC: Close the index and message database files since // they are no longer needed: SessionDetails sessionDetails = _mapSessions[sessionId]; sessionDetails.IdxWriter.Close(); sessionDetails.MsgWriter.Close(); // REC: Delete the session's lock file: string lockPath = Path.Combine(_rootPath, sessionId); string lockFile = Path.Combine(lockPath, "Locked.txt"); if (File.Exists(lockFile)) { File.Delete(lockFile); } // REC: Remove the session details from the map: _mapSessions.Remove(sessionId); }
/// <summary> /// The HandleSession_Logon method is invoked to handle /// a logon message that has been received from the peer /// session that this session is communicating with. /// </summary> /// <param name="msg"> /// The FIX logon message received from the peer. /// </param> private void HandleSession_Logon(FixMessage msg) { // REC: Ensure that we're in the appropriate state // to handle a logon message: if (_currentState == SessionStates.Session_Pending || _currentState == SessionStates.Session_Closed) { // REC: Retrieve the FIX SenderCompID of the peer // from the logon message: FixField fldSenderCompID = msg.Header.GetField(49); if (fldSenderCompID != null) { // REC: The SenderCompID from the peer becomes the // session's TargetCompID for outgoing messages: _fixTargetCompID = fldSenderCompID.Content; // REC: Now that the FIX SenderCompID of the peer // session is known, it is used to construct the // identifier for the session - used to retrieve // the session's details from the database: _sessionId = string.Format("{0}-{1}", _fixSenderCompID, _fixTargetCompID); // REC: Attempt to retrieve the session details // from the session database: if (_fixDatabase != null) { // REC: Note that once a session record is acquired // from the database, it must be released when it is // no longer needed by the session instance... _sessionRecord = _fixDatabase.AcquireSession(_sessionId); if (this._resetSequence == true) { _sessionRecord.RxSequence = 1; _sessionRecord.TxSequence = 1; } } // REC: Register the peer session's SenderCompID as // the TargetCompID for outgoing messages: _fixAssembler.SetField(new FixField(56, _fixTargetCompID)); // REC: Assemble the response message: FixMessage response = _fixAssembler.CreateMessage(_sxVersion, _axVersion, "A"); // REC: Handle the sequence number reset flag // if it is present in the logon message: FixField fieldReset = msg.GetField(141); if (fieldReset != null) { response.AddField(new FixField(141, "Y")); } // REC: Transition to the opened state: _currentState = SessionStates.Session_Opened; // REC: Notify the session's owner that the peer // session has sent the logon request: _handler.OnSessionLogon(this, msg); // REC: Dispatch the administrative message out // to the session's owner: Dispatch_AdmMessage(response); } else { // REC: Throw an exception since the session has not // yet been established and there will be no handler // at the application level that can deal with this: throw new ArgumentException("Logon message missing required field - SenderCompID."); } } else { // REC: Notify the session handler that an // administrative message has been received // from the peer session: _handler.OnSessionRxAdmMessage(this, msg); // REC: Throw an exception that indicates the // session is not in the appropriate state to // handle the received message: //throw new InvalidOperationException("Session state invalid for received message."); } }
/// <summary> /// The HandleConnect_Entrypoint method is the asynchronous /// entrypoint for the session's connect handling logic. /// </summary> /// <param name="state"> /// Ignored parameter; required for the method to meet the /// required signature for use with the sequencer. /// </param> private void HandleConnect_Entrypoint(object state) { // REC: Transition the session to the active state so // that it can begin handling messages: _currentState = SessionStates.Session_Pending; // REC: Reset the receive timestamp: this._lastRxTicks = DateTime.Now.Ticks; // REC: Reset the transmit timestamp: this._lastTxTicks = DateTime.Now.Ticks; // REC: Acquire the session's persisted session // state from the database: if (_fixDatabase != null) { _sessionRecord = _fixDatabase.AcquireSession(_sessionId); } // REC: The session's timer is started in order // to drive the heartbeat and timeout logic: this._fixTimer.Start(); _handler.OnSessionOpened(this); // REC: The client session needs to dispatch a logon // message to the peer as soon as it connects: FixMessage msgLogon = _fixAssembler.CreateMessage(_sxVersion, _axVersion, "A"); if (msgLogon != null) { Dispatch_AdmMessage(msgLogon); } }
/// <summary> /// The AcquireSession method is invoked to request exclusive /// access to a session's information in the database. /// </summary> /// <param name="sessionId"> /// The session's unique identifier. /// </param> /// <returns> /// The persisted information that is maintained for /// the specified session identifier. /// </returns> public VfxFixDatabaseRecord AcquireSession(string sessionId) { VfxFixDatabaseRecord result = new VfxFixDatabaseRecord(); // REC: Determine if the session's file path exists: string sessionPath = Path.Combine(_rootPath, sessionId); if (!Directory.Exists(sessionPath)) { Directory.CreateDirectory(sessionPath); } // REC: Attempt to open and load the database entry // for the specified session: string sessionFile = Path.Combine(sessionPath, "Session.xml"); if (!File.Exists(sessionFile)) { ResetSession(sessionId); } // REC: Attempt to read the session's details from // the persisted information in the session file: FileStream fsSession = new FileStream(sessionFile, FileMode.Open, FileAccess.Read, FileShare.None); XmlSerializer xsSession = new XmlSerializer(typeof(XmlFixDatabaseRecord)); XmlFixDatabaseRecord sessionRecord = xsSession.Deserialize(fsSession) as XmlFixDatabaseRecord; result.TxSequence = sessionRecord.TxSequence; result.RxSequence = sessionRecord.RxSequence; fsSession.Close(); // REC: Create the session's lock file: string lockFile = Path.Combine(sessionPath, "Locked.txt"); FileStream fs = new FileStream(lockFile, FileMode.Create, FileAccess.ReadWrite, FileShare.None); fs.Close(); // REC: Create a new instance of the SessionDetails class // that will be used to maintain the session's information: SessionDetails sessionDetails = new SessionDetails(); // REC: Attempt to open the index file for the session: string idxFile = Path.Combine(sessionPath, "Index.xml"); FileStream idxStream = new FileStream(idxFile, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); // REC: Maintain a reference to the index file stream // in the session's details record: sessionDetails.IdxWriter = new StreamWriter(idxStream); // REC: Read all of the index entries from the index file // and store them in the session's details structure: StreamReader idxReader = new StreamReader(idxStream); while (idxReader.EndOfStream == false) { string idxLine = idxReader.ReadLine(); string[] idxTokens = idxLine.Split(new char[] { ':' }); IndexEntry idxEntry = new IndexEntry(); idxEntry.msgOffset = int.Parse(idxTokens[1]); idxEntry.msgLength = int.Parse(idxTokens[2]); idxEntry.msgSequence = int.Parse(idxTokens[0]); sessionDetails.Index.Add(idxEntry); } // REC: Attempt to open the message file for the session: string msgFile = Path.Combine(sessionPath, "Messages.txt"); FileStream msgStream = new FileStream(msgFile, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); // REC: Maintain a reference to the message file stream // in the session's details record: sessionDetails.MsgWriter = new StreamWriter(msgStream); // REC: Add the session details record to the internal // map, keyed by the session identifier: _mapSessions.Add(sessionId, sessionDetails); // REC: Return the session's specifics to the caller: return(result); }