/// <summary> /// Called by the router whenever it receives a message with a non-empty /// _SessionID. This method dispatches the message to the associated session /// (if any). /// </summary> /// <param name="msg">The message.</param> /// <param name="sessionInfo">The session information associated with the handler.</param> public void OnMsg(Msg msg, SessionHandlerInfo sessionInfo) { ISession session; Assertion.Test(msg._SessionID != Guid.Empty); using (TimedLock.Lock(router.SyncRoot)) { if ((msg._Flags & MsgFlag.ServerSession) != 0) { serverSessions.TryGetValue(msg._SessionID, out session); } else { clientSessions.TryGetValue(msg._SessionID, out session); } } if (session != null) { msg._Trace(router, 2, "SessionManager: Dispached", null); session.OnMsg(msg, sessionInfo); } else { msg._Trace(router, 2, "SessionManager: No session", null); } }
/// <summary> /// Handles message packets received on the socket. /// </summary> /// <param name="ar">The async result.</param> private void OnSocketReceive(IAsyncResult ar) { int cb; Msg msg = null; byte[] msgBuf; int cbMsg; IPEndPoint fromEP = NetworkBinding.Any; using (TimedLock.Lock(router.SyncRoot)) { if (!isOpen) { return; } try { cb = sock.EndReceiveFrom(ar, ref recvEP); if (cb > 0) { msgBuf = router.DecryptFrame(recvBuf, cb, out cbMsg); msg = Msg.Load(new EnhancedMemoryStream(msgBuf)); fromEP = (IPEndPoint)recvEP; msg._SetFromChannel(new ChannelEP(transport, fromEP)); } } catch (MsgException) { // Ignore messages that can't be parsed } catch (Exception e) { SysLog.LogException(e); } // Initiate the receive of the next message if (sock.IsOpen) { try { sock.BeginReceiveFrom(recvBuf, 0, recvBuf.Length, SocketFlags.None, ref recvEP, onSocketReceive, null); } catch (Exception e) { SysLog.LogException(e, "LillTek UDP message channel is no longer able to receive messages."); } } } if (msg != null) { msg._Trace(router, 2, "UDP: Recv", string.Format("From: {0}", fromEP)); msg._Trace(router, 0, "Receive", string.Empty); router.OnReceive(this, msg); } }
/// <summary> /// Transmits the message via the UDP broadcast client. /// </summary> /// <param name="toEP">The target endpoint.</param> /// <param name="msg">The message.</param> private void TransmitViaUdpBroadcast(ChannelEP toEP, Msg msg) { byte[] sendBuf; int cbSend; int cbMsg; msg._SetToChannel(toEP); msg._SetFromChannel(localEP); msg._Trace(router, 2, "UDP: Send", null); using (TimedLock.Lock(router.SyncRoot)) { // Initiate transmission of the message cbMsg = Msg.Save(new EnhancedMemoryStream(msgBuf), msg); sendBuf = router.EncryptFrame(msgBuf, cbMsg); cbSend = sendBuf.Length; Assertion.Validate(cbSend <= TcpConst.MTU - UdpBroadcastClient.MessageEnvelopeSize, "Message larger than UDP MTU."); try { broadcastClient.Broadcast(sendBuf); } catch { } } }
/// <summary> /// Completes the execution of an asynchronous query operation. /// </summary> /// <param name="ar">The async result returned by <see cref="BeginQuery" />.</param> /// <returns>The query response message.</returns> /// <remarks> /// <note> /// Each call to <see cref="BeginQuery" /> must be matched with /// a call to EndQuery. /// </note> /// </remarks> public Msg EndQuery(IAsyncResult ar) { var arQuery = (AsyncResult)ar; arQuery.Wait(); try { if (arQuery.Exception != null) { throw arQuery.Exception; } #if TRACE if (error != null) { query._Trace(base.Router, 2, "Q/R Finish", null, "Exception: {0}", error.ToString()); } else { var sb = new StringBuilder(512); sb.Append("\r\nResponse:\r\n\r\n"); response._TraceDetails(base.Router, sb); query._Trace(base.Router, 2, "Q/R Finish", "", sb.ToString()); } #endif Assertion.Test(error != null || response != null, "Either an error or a response should be present."); if (error != null) { Helper.Rethrow(error); throw null; } else { return(response); } } finally { arQuery.Dispose(); } }
/// <summary> /// Handles messages received from the UdpBroadcast client. /// </summary> /// <param name="sender">The UDP broadcast client.</param> /// <param name="args">Event arguments.</param> private void OnBroadcastReceive(object sender, UdpBroadcastEventArgs args) { Msg msg = null; byte[] msgBuf; int cbMsg; int cb; using (TimedLock.Lock(router.SyncRoot)) { if (!isOpen) { return; } try { cb = args.Payload.Length; if (cb > 0) { msgBuf = router.DecryptFrame(args.Payload, cb, out cbMsg); msg = Msg.Load(new EnhancedMemoryStream(msgBuf)); msg._SetFromChannel(new ChannelEP(transport, new IPEndPoint(args.SourceAddress, 0))); } } catch (MsgException) { // Ignore messages that can't be parsed } catch (Exception e) { SysLog.LogException(e); } } if (msg != null) { msg._Trace(router, 2, "UDP: Broadcast Recv", string.Format("From: {0}", args.SourceAddress)); msg._Trace(router, 0, "Receive", string.Empty); router.OnReceive(this, msg); } }
/// <summary> /// Initiates a network connection to the message router at the /// specified network endpoint and then initiates the transmission /// of the message once the connection is established. /// </summary> /// <param name="ep">The remote router's endpoint.</param> /// <param name="msg">The message to be sent (or <c>null</c>).</param> public void Connect(IPEndPoint ep, Msg msg) { using (TimedLock.Lock(router.SyncRoot)) { Assertion.Test(sock == null); sock = new EnhancedSocket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); localEP = new ChannelEP(Transport.Tcp, router.NormalizeEP(router.TcpEP)); sock.NoDelay = !router.TcpDelay; sock.SendBufferSize = router.TcpSockConfig.SendBufferSize; sock.ReceiveBufferSize = router.TcpSockConfig.ReceiveBufferSize; if (router.FragmentTcp) { sock.SendMax = 1; sock.ReceiveMax = 1; } // Queue the channel initialization message and the message passed Msg initMsg; initMsg = new TcpInitMsg(router.RouterEP, new MsgRouterInfo(router), isUplink, router.TcpEP.Port); initMsg._TTL = 1; Serialize(initMsg); Enqueue(initMsg); try { SetLastAccess(); remoteEP = new ChannelEP(Transport.Tcp, router.NormalizeEP(ep)); if (msg != null) { msg._SetToChannel(remoteEP); msg._SetFromChannel(localEP); msg._Trace(router, 2, "TCP: Queue", null); Serialize(msg); Enqueue(msg); } router.Trace(2, "TCP: Outbound", "LocalEP=" + localEP.NetEP.ToString() + " remoteEP=" + remoteEP.NetEP.ToString(), null); sock.BeginConnect(remoteEP.NetEP, new AsyncCallback(OnConnect), null); } catch (Exception e) { router.Trace(string.Format(null, "TCP: Connect Failed [{0}]", ep), e); router.OnTcpClose(this); Close(); } } }
/// <summary> /// Private implementation of the Transmit() method that implements an /// option that disables queuing for the message passed. /// </summary> /// <param name="toEP">The target endpoint.</param> /// <param name="msg">The message.</param> /// <param name="queue">Indicates whether queuing should be enabled for this message.</param> private void Transmit(ChannelEP toEP, Msg msg, bool queue) { msg._SetToChannel(toEP); msg._SetFromChannel(localEP); msg._Trace(router, 2, "TCP: Send", null); // Serialize the message here rather than within the lock // below for better multiprocessor performance. Serialize(msg); // Initiate transmission of the message or queue it if other // messages are awaiting transmission (if queuing is enabled). try { using (TimedLock.Lock(router.SyncRoot)) { if (!connected || sending) { Enqueue(msg); return; } // If there are already messages in the queue then add this // message to the end of the queue and then dequeue a message // from the front of the queue and send it. if (queue && sendQueue.Count > 0) { Enqueue(msg); msg = Dequeue(); } // Initiate message transmission sendBuf = msg._MsgFrame; sending = true; sendPos = 0; cbSend = sendBuf.Length; sock.BeginSend(sendBuf, sendPos, cbSend, SocketFlags.None, onSend, null); } } catch (Exception e) { TraceException(e); router.OnTcpClose(this); Close(); } }
/// <summary> /// Queues the message passed rather than initiating an /// immediate transmission. /// </summary> /// <param name="toEP">The target endpoint.</param> /// <param name="msg">The message to queue.</param> internal void QueueTo(ChannelEP toEP, Msg msg) { msg._SetToChannel(toEP); msg._SetFromChannel(localEP); msg._Trace(router, 2, "TCP: Queue", null); Serialize(msg); using (TimedLock.Lock(router.SyncRoot)) { msg._SetToChannel(toEP); Enqueue(msg); } }
/// <summary> /// Handles server side messages. /// </summary> /// <param name="msg">The message.</param> private void OnServerMsg(Msg msg) { // If we haved a cached reply for this session then // resend it, otherwise ignore this message. if (base.Reply != null) { base.Reply._Trace(base.Router, 2, "Q/R Cached Reply", null); base.Router.Send(base.Reply.Clone()); } else { msg._Trace(base.Router, 2, "Q/R Msg Ignored", null); } }
private AsyncResult arQuery; // The query async result /// <summary> /// Initiates an asynchronous query operation by sending the message /// passed to the target endpoint. /// </summary> /// <param name="toEP">The target endpoint.</param> /// <param name="query">The query message.</param> /// <param name="callback">The delegate to be called when the operation completes (or <c>null</c>).</param> /// <param name="state">Application specific state (or <c>null</c>).</param> /// <returns>The async result used to track the operation.</returns> /// <remarks> /// <note> /// Each call to <see cref="BeginQuery" /> must be matched with a call to <see cref="EndQuery" />. /// </note> /// </remarks> public IAsyncResult BeginQuery(MsgEP toEP, Msg query, AsyncCallback callback, object state) { Assertion.Test(arQuery == null, "Cannot reuse a query session."); this.query = query; query._ToEP = toEP.Clone(true); query._FromEP = base.Router.RouterEP.Clone(true); query._SessionID = base.SessionID; query._Flags |= MsgFlag.OpenSession | MsgFlag.ServerSession; if (base.Router.DeadRouterDetection) { query._Flags |= MsgFlag.ReceiptRequest; } retry = 0; retryTime = base.StartTime + TimeSpan.FromTicks(base.Router.SessionTimeout.Ticks); arQuery = new AsyncResult(base.SessionManager, callback, state); response = null; error = null; base.TTD = DateTime.MaxValue; // Client side QuerySessions handle their own lifespan base.IsRunning = true; try { query._Trace(base.Router, 2, "Q/R Query", null); base.SessionManager.ClientStart(this); base.Router.Send(query); } catch (Exception e) { AsyncResult arTemp; arQuery.Notify(e); arTemp = arQuery; arQuery = null; base.IsRunning = false; return(arTemp); } arQuery.Started(); return(arQuery); }
/// <summary> /// Handles client side messages. /// </summary> /// <param name="msg">The message.</param> private void OnClientMsg(Msg msg) { msg._Trace(base.Router, 2, "Q/R Client Recv", null); Assertion.Test((msg._Flags & MsgFlag.OpenSession) == 0); using (TimedLock.Lock(base.Router.SyncRoot)) { if (arQuery == null) { return; } // If the message is a SessionKeepAliveMsg then reset // the time-to-retry timer. var keepAliveMsg = msg as SessionKeepAliveMsg; if (keepAliveMsg != null) { retryTime = SysTime.Now + keepAliveMsg.SessionTTL; return; } // I'm going to assume that any other message received that is // associated with this session is the response so // set the response field and signal that the session // is finished. response = msg; arQuery.Notify(); arQuery = null; base.IsRunning = false; // Tell the session manager base.SessionManager.OnFinished(this); } }
/// <summary> /// Transmits the message via the socket. /// </summary> /// <param name="toEP">The target endpoint.</param> /// <param name="msg">The message.</param> private void TransmitViaSocket(ChannelEP toEP, Msg msg) { int cbMsg; if (cloudEP != null && !multicastInit) { // Retry adding the socket to the multicast group if this didn't // work earlier. try { this.sock.MulticastGroup = cloudEP.Address; this.multicastInit = true; } catch { this.multicastInit = false; } } msg._SetToChannel(toEP); msg._SetFromChannel(localEP); msg._Trace(router, 2, "UDP: Send", null); using (TimedLock.Lock(router.SyncRoot)) { if (sendMsg != null) { // We're already in the process of transmitting // a message so queue this one. Enqueue(msg); return; } // If there are already messages in the queue then queue // this one and then setup to transmit the first message // waiting in the queue. if (sendQueue.Count > 0) { Enqueue(msg); msg = sendQueue.Dequeue(); } // Initiate transmission of the message sendMsg = msg; cbMsg = Msg.Save(new EnhancedMemoryStream(msgBuf), sendMsg); sendBuf = router.EncryptFrame(msgBuf, cbMsg); cbSend = sendBuf.Length; Assertion.Validate(cbSend <= TcpConst.MTU, "Message larger than UDP MTU."); try { sock.BeginSendTo(sendBuf, 0, cbSend, SocketFlags.None, router.NormalizeEP(toEP.NetEP), onSend, null); } catch { // Ignoring } } }