Example #1
0
        public void Broadcast(ADMMessage message)
        {
            if (message == null)
            {
                return;
            }

            MessageTags.Release(message.Tag);
            message.Target = null; //clear the target as it may have been used internally but now this is a broadcast so it's not intended for any specific target

            if (_listener != null && message.CanBroadcast)
            {
                switch (message.Type)
                {
                case Chetch.Messaging.MessageType.ERROR:
                    _listener(message, this);
                    break;

                default:
                    if (State == ADMState.DEVICE_READY || State == ADMState.DEVICE_CONNECTED)
                    {
                        _listener(message, this);
                    }
                    break;
                }
            }
        }
        /// <summary>
        /// Since the message objects get modified during processing, we do the chunking synchronously here.
        /// Otherwise we would end up sending a bunch of extra stuff to the bridge.
        /// </summary>
        /// <param name="message"></param>
        public void SendToBridge(Message message)
        {
            MessageTags tags = new MessageTags();

            tags.BridgePriority = GetBridgePriority(message);

            if (tags.BridgePriority == MessagePriority.Lowest)
            {
                //These are DeckSlideContentMessages which are big and are unused in the current implementation of the streaming/archiving system.
                Trace.WriteLine("Client U2M Bridge dropping message:" + message.ToString());
                return;
            }

            // Serialize the message and split it into chunks with the chunk encoder.
            // The encoder updates the current message- and chunk sequence numbers.
            Chunk[] chunks;
            using (Synchronizer.Lock(this)) {
                chunks = this.m_Encoder.MakeChunks(message, ref this.m_ChunkSequence);
                Trace.WriteLine("Client U2M Bridge MakeChunks returned " + chunks.Length.ToString() + " chunks for message " + message.ToString());
            }

            lock (m_SendQueue) {
                m_SendQueue.Enqueue(new ChunksAndPriority(chunks, tags));
            }
            m_SendQueueWait.Set();
        }
Example #3
0
        public byte RequestStatus(byte boardID = 0)
        {
            var message = new ADMMessage(MessageTags.CreateTag());

            message.Type     = Messaging.MessageType.STATUS_REQUEST;
            message.TargetID = boardID;
            SendMessage(message);
            return(message.Tag);
        }
Example #4
0
        public byte Ping()
        {
            var message = new ADMMessage(MessageTags.CreateTag());

            message.Type     = Messaging.MessageType.PING;
            message.TargetID = 0;
            SendMessage(message);
            return(message.Tag);
        }
Example #5
0
        public byte Initialise()
        {
            ADMMessage message = new ADMMessage(MessageTags.CreateTag());

            message.TargetID = 0;
            message.Type     = Messaging.MessageType.INITIALISE;
            SendMessage(message);
            return(message.Tag);
        }
Example #6
0
        /// <summary>
        /// Serialize the chunk then enqueue a send operation for each client socket for which there is a
        /// participant membership in the receivers group.
        /// </summary>
        /// <param name="chunk"></param>
        /// <param name="receivers"></param>
        public void Send(Chunk chunk, Group receivers, MessageTags tags)
        {
            using (MemoryStream stream = new MemoryStream((int)(this.m_Encoder.MaximumChunkSize * 2))) {
                stream.Position = 0;
                this.m_Encoder.EncodeChunk(stream, chunk);
                byte[] buffer = stream.GetBuffer();
                int    length = (int)stream.Length;

                //Unicast to multicast bridge
#if RTP_BUILD
                if ((this.m_U2MBridge != null) && !(receivers is SingletonGroup))
                {
                    this.m_U2MBridge.Send(buffer, length, tags);
                }
#endif
                lock (m_AllClients) {
                    foreach (ClientData client in m_AllClients.Values)
                    {
                        if (client.Participant != null)
                        {
                            if (!client.Participant.Groups.Contains(receivers))
                            {
                                if (receivers == Group.AllParticipant)
                                {
                                    //Should never happen
                                    Trace.WriteLine("!!!Participant has lost membership in 'All Participants' (sending anyway). Client= " + client.Id.ToString(), this.GetType().ToString());
                                    if (client.Socket != null)
                                    {
                                        Trace.WriteLine("    Socket endpoint=" + client.Socket.RemoteEndPoint.ToString());
                                    }
                                }
                                else
                                {
                                    //Ignoring participant (no group match)
                                    if (receivers is SingletonGroup)
                                    {
                                        //Trace.WriteLine("Ignoring participant; no group match for singleton group; participant= " + client.Id.ToString(), this.GetType().ToString());
                                    }
                                    else
                                    {
                                        Trace.WriteLine("Ignoring participant; no group match for group: " + receivers.FriendlyName + "; participant= " + client.Id.ToString(), this.GetType().ToString());
                                    }
                                    continue;
                                }
                            }
                        }
                        else
                        {
                            //This probably shouldn't ever happen
                            Trace.WriteLine("Failed to find a participant for a connected socket.", this.GetType().ToString());
                            continue;
                        }
                        m_ServerSender.Send(buffer, length, client, tags, chunk.MessageSequence, chunk.ChunkSequenceInMessage, false);
                    }
                }
            }
        }
Example #7
0
        /// <summary>
        /// If connected, send a message, otherwise do nothing.
        /// </summary>
        /// <param name="chunk"></param>
        /// <param name="group"></param>
        public void Send(Chunk chunk, Group group, MessageTags tags)
        {
            AsyncCallback ac = new AsyncCallback(SendCallback);

            using (MemoryStream stream = new MemoryStream((int)(this.m_Encoder.MaximumChunkSize * 2))) {
                stream.Position = 0;
                this.m_Encoder.EncodeChunk(stream, chunk);

                Trace.WriteLine(string.Format("TCPClient sending message #{0}, chunk #{1} of {2}, {3} bytes.",
                                              chunk.MessageSequence,
                                              chunk.ChunkSequenceInMessage + 1,
                                              chunk.NumberOfChunksInMessage,
                                              chunk.Data.Length),
                                this.GetType().ToString());

                byte[] buffer = stream.GetBuffer();
                int    length = (int)stream.Length;

                if ((m_Socket.Connected) && (m_Connected))
                {
                    while (true)
                    {
                        try {
                            m_Socket.BeginSend(buffer, 0, length, SocketFlags.None, ac, new SendState(m_Socket, length)); //BeginSend since it won't block.
                            break;
                        }
                        catch (SocketException se) {
                            if (se.ErrorCode == 10055)
                            {
                                Trace.WriteLine("Client Send queue is full.  Sleeping 50 mS", this.GetType().ToString());
                                Thread.Sleep(50);
                            }
                            else
                            {
                                Trace.WriteLine("SendThread socket exception: " + se.ToString() + " Error code: " + se.ErrorCode.ToString(), this.GetType().ToString());
                                break;
                            }
                        }
                        catch (ObjectDisposedException ode) {
                            Trace.WriteLine("SendThread ObjectDisposedException: " + ode.Message, this.GetType().ToString());
                            break;
                        }
                        catch (Exception e) {
                            Trace.WriteLine("SendThread Exception: " + e.ToString(), this.GetType().ToString());
                            break;
                        }
                    }
                }
                else
                {
                    Trace.WriteLine("SendThread found a socket disconnected. Send request ignored.", this.GetType().ToString());
                }
            }
        }
Example #8
0
        /// <summary>
        /// The main Send method.  Called both by the unicast code and as a result of calls from the BeaconService thread.
        /// </summary>
        /// <param name="buf"></param>
        public void Send(byte[] buf, int length, MessageTags tags)
        {
            BridgeMessage bmsg = new BridgeMessage(new BufferChunk(buf, 0, length), tags.BridgePriority);

            if (m_SendQueue != null)
            {
                lock (m_SendQueue) {
                    m_SendQueue.Enqueue(bmsg);
                    m_SendQueueWait.Set();
                }
            }
        }
Example #9
0
 public SendParameters(byte[] buffer, int length, Guid id, MessageTags tags,
                       ulong messageSequence, ulong chunkSequence, bool isHeartbeat)
 {
     this.Buffer             = buffer;
     this.Length             = length;
     this.Id                 = id;
     this.Tags               = tags;
     this.SequenceNumber     = ulong.MaxValue;
     this.MessageSequence    = messageSequence;
     this.ChunkSequence      = chunkSequence;
     IsPublicNode            = false;
     this.IsHeartbeatMessage = isHeartbeat;
 }
Example #10
0
        /// <summary>
        /// Enqueue the send to occur when network bandwidth is available.
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="length"></param>
        /// <param name="socket"></param>
        /// <param name="participant"></param>
        internal void Send(byte[] buffer, int length, ClientData client, MessageTags tags,
                           ulong messageSequence, ulong chunkSequence, bool isHeartbeat)
        {
            using (Synchronizer.Lock(m_SocketMap)) {
                Debug.Assert(m_SocketMap.ContainsKey(client.Id), "TCPServerSender.Send found a missing Socket Map entry.");
            }

            //Trace.WriteLine("TCPServerSender.Send seq=" + messageSequence.ToString());

            //Enqueue the message
            using (Synchronizer.Cookie cookie = Synchronizer.Lock(m_SendQueue)) {
                m_SendQueue.Enqueue(new SendParameters(buffer, length, client.Id, tags, messageSequence, chunkSequence, isHeartbeat),
                                    client.Participant);

                // Notify the sender thread that the queue's status has changed.
                cookie.PulseAll();
            }
        }
Example #11
0
        public byte SendCommand(byte targetID, ArduinoCommand command, List <Object> extraArgs = null, byte tag = 0)
        {
            var message = new ADMMessage();

            message.LittleEndian = LittleEndian;
            message.Type         = Messaging.MessageType.COMMAND;
            message.TargetID     = targetID;
            message.Tag          = tag == 0 ? MessageTags.CreateTag() : tag;
            message.CommandID    = command.ID;

            List <Object> allArgs = command.Arguments;

            if (extraArgs != null && extraArgs.Count > 0)
            {
                allArgs.AddRange(extraArgs);
            }

            foreach (Object arg in allArgs)
            {
                byte[] b;
                if (arg is String)
                {
                    b = Chetch.Utilities.Convert.ToBytes((String)arg);
                }
                else if (arg.GetType().IsValueType)
                {
                    b = Chetch.Utilities.Convert.ToBytes((ValueType)arg, LittleEndian);
                }
                else
                {
                    throw new Exception("Unable to process type " + arg.GetType());
                }
                message.AddArgument(b);
            }

            return(SendMessage(message));
        }
        /// <summary>
        /// Since the message objects get modified during processing, we do the chunking synchronously here.  
        /// Otherwise we would end up sending a bunch of extra stuff to the bridge.
        /// </summary>
        /// <param name="message"></param>
        public void SendToBridge(Message message)
        {
            MessageTags tags = new MessageTags();
            tags.BridgePriority = GetBridgePriority(message);

            if (tags.BridgePriority == MessagePriority.Lowest) {
                //These are DeckSlideContentMessages which are big and are unused in the current implementation of the streaming/archiving system.
                Trace.WriteLine("Client U2M Bridge dropping message:" + message.ToString());
                return;
            }

            // Serialize the message and split it into chunks with the chunk encoder.
            // The encoder updates the current message- and chunk sequence numbers.
            Chunk[] chunks;
            using (Synchronizer.Lock(this)) {
                chunks = this.m_Encoder.MakeChunks(message, ref this.m_ChunkSequence);
                Trace.WriteLine("Client U2M Bridge MakeChunks returned " + chunks.Length.ToString() + " chunks for message " + message.ToString());
            }

            lock (m_SendQueue) {
                m_SendQueue.Enqueue(new ChunksAndPriority(chunks, tags));
            }
            m_SendQueueWait.Set();
        }
Example #13
0
        /// <summary>
        /// The override of SendingQueue.Send used by the BeaconService.  Chunk, serialize and pass to the main send method.
        /// </summary>
        /// <param name="message"></param>
        /// <param name="priority"></param>
        public override void Send(Message message, UW.ClassroomPresenter.Model.MessagePriority priority)
        {
            Group receivers = message.Group != null ? message.Group : Group.AllParticipant;

            Chunk[] chunks;
            // Serialize the message and split it into chunks with the chunk encoder.
            // The encoder updates the current message- and chunk sequence numbers.
            using (Synchronizer.Lock(this)) {
                chunks = this.m_Encoder.MakeChunks(message, ref this.m_ChunkSequence);
            }

            foreach (Chunk c in chunks)
            {
                using (MemoryStream stream = new MemoryStream((int)(this.m_Encoder.MaximumChunkSize * 2))) {
                    stream.Position = 0;
                    this.m_Encoder.EncodeChunk(stream, c);
                    byte[]      buffer = stream.GetBuffer();
                    int         length = (int)stream.Length;
                    MessageTags tags   = new MessageTags();
                    tags.BridgePriority = MessagePriority.Higher;
                    this.Send(buffer, length, tags);
                }
            }
        }
Example #14
0
        /// <summary>
        /// Serialize the chunk then enqueue a send operation for each client socket for which there is a 
        /// participant membership in the receivers group.
        /// </summary>
        /// <param name="chunk"></param>
        /// <param name="receivers"></param>
        public void Send(Chunk chunk, Group receivers, MessageTags tags)
        {
            using (MemoryStream stream = new MemoryStream((int)(this.m_Encoder.MaximumChunkSize * 2))) {
                stream.Position = 0;
                this.m_Encoder.EncodeChunk(stream, chunk);
                byte[] buffer = stream.GetBuffer();
                int length = (int)stream.Length;

                //Unicast to multicast bridge
            #if RTP_BUILD
                if ((this.m_U2MBridge != null) && !(receivers is SingletonGroup)) {
                    this.m_U2MBridge.Send(buffer,length,tags);
                }
            #endif
                lock (m_AllClients) {
                    foreach (ClientData client in m_AllClients.Values) {
                        if (client.Participant != null) {
                            if (!client.Participant.Groups.Contains(receivers)) {
                                if (receivers == Group.AllParticipant) {
                                    //Should never happen
                                    Trace.WriteLine("!!!Participant has lost membership in 'All Participants' (sending anyway). Client= " + client.Id.ToString(), this.GetType().ToString());
                                    if (client.Socket != null)
                                        Trace.WriteLine("    Socket endpoint=" + client.Socket.RemoteEndPoint.ToString());
                                }
                                else {
                                    //Ignoring participant (no group match)
                                    if (receivers is SingletonGroup) {
                                        //Trace.WriteLine("Ignoring participant; no group match for singleton group; participant= " + client.Id.ToString(), this.GetType().ToString());
                                    }
                                    else
                                        Trace.WriteLine("Ignoring participant; no group match for group: " + receivers.FriendlyName + "; participant= " + client.Id.ToString(), this.GetType().ToString());
                                    continue;
                                }
                            }
                        }
                        else {
                            //This probably shouldn't ever happen
                            Trace.WriteLine("Failed to find a participant for a connected socket.", this.GetType().ToString());
                            continue;
                        }
                        m_ServerSender.Send(buffer, length, client, tags, chunk.MessageSequence, chunk.ChunkSequenceInMessage,false);
                    }
                }
            }
        }
 public SendParameters(byte[] buffer, int length, Guid id, MessageTags tags, 
     ulong messageSequence, ulong chunkSequence, bool isHeartbeat)
 {
     this.Buffer = buffer;
     this.Length = length;
     this.Id = id;
     this.Tags = tags;
     this.SequenceNumber = ulong.MaxValue;
     this.MessageSequence = messageSequence;
     this.ChunkSequence = chunkSequence;
     IsPublicNode = false;
     this.IsHeartbeatMessage = isHeartbeat;
 }
 public ChunksAndPriority(Chunk[] chunks, MessageTags tags)
 {
     this.Chunks = chunks;
     this.Tags   = tags;
 }
Example #17
0
        /// <summary>
        /// Close disconnected client sockets, send heartbeat messages and do other assorted cleanup.
        /// </summary>
        private void MaintenanceThread()
        {
            //Prepare the heartbeat message
            TCPHeartbeatMessage heartbeat = new TCPHeartbeatMessage();
            BinaryFormatter bf = new BinaryFormatter();
            MemoryStream hbms = new MemoryStream();
            bf.Serialize(hbms, heartbeat);
            byte[] hbbuf = hbms.GetBuffer();
            int hblen = (int)hbms.Length;
            MessageTags hbtags = new MessageTags();

            //Lists for socket maintenance
            List<Guid> toCleanup = new List<Guid>();
            List<Socket> toClose = new List<Socket>();
            int secondCounter = 0;
            int connectedClientCount = 0;

            while (!this.m_Disposed) {
                //iterate approximately once per second
                for (int i = 0; ((i < 10) && (!m_Disposed)); i++) {
                    Thread.Sleep(100);
                }
                if (this.m_Disposed) break;
                secondCounter++;

                lock (m_AllClients) {
                    //Close and remove disconnected sockets.
                    toCleanup.Clear();
                    toClose.Clear();
                    connectedClientCount = 0;
                    foreach (ClientData client in m_AllClients.Values) {
                        if (client.ConnectionState == ConnectionState.Disconnected) {
                            //Check for disconnected clients that have timed out
                            if (client.Timeout < DateTime.Now) {
                                this.m_ServerSender.RemoveQueue(client.Id);
                                toCleanup.Add(client.Id);
                            }
                        }
                        else if (client.ConnectionState == ConnectionState.Connected) {
                            //Find sockets to close for any new disconnects
                            if (!client.Socket.Connected) {
                                toClose.Add(client.Socket);
                                client.Socket = null;
                                client.ConnectionState = ConnectionState.Disconnected;
                                client.Timeout = DateTime.Now + TimeSpan.FromMinutes(m_QueueTimeout);
                            }
                            else {
                                connectedClientCount++;
                                //Send a heartbeat message to connected clients
                                if (secondCounter % HEARTBEAT_PERIOD == 0) {
                                    //Trace.WriteLine("TCPServer sending Heartbeat to client ID " + client.Id.ToString(), this.GetType().ToString());
                                    this.m_ServerSender.Send(hbbuf, hblen, client, hbtags,0,0,true);
                                }
                            }
                        }
                        else if (client.ConnectionState == ConnectionState.Reconnecting) {
                            //This is a temporary state used during handshaking
                            connectedClientCount++;
                        }
                    }

                    //Remove timed out clients from the master list
                    foreach (Guid goner in toCleanup) {
                        m_AllClients.Remove(goner);
                    }
                }

                //Update published client count
                if (m_ClientCount != connectedClientCount) {
                    using (Synchronizer.Lock(this.SyncRoot)) {
                        this.SetPublishedProperty("ClientCount", ref m_ClientCount, connectedClientCount);
                        NetworkStatus newStatus = m_NetworkStatus.Clone();
                        newStatus.ClientCount = m_ClientCount;
                        this.SetPublishedProperty("NetworkStatus", ref m_NetworkStatus, newStatus);
                    }
                }

                //Close sockets
                foreach (Socket s in toClose) {
                    try {
                        Trace.WriteLine("MaintenanceThread closing disconnected socket: " + s.RemoteEndPoint.ToString(), this.GetType().ToString());
                        s.Close();
                    }
                    catch (Exception e) {
                        Trace.WriteLine("MaintenanceThread exception while closing socket: " + e.ToString(), this.GetType().ToString());
                    }
                }

                if (toCleanup.Count > 0)
                    Trace.WriteLine("MaintenanceThread timed out " + toCleanup.Count.ToString() + " disconnected socket(s).", this.GetType().ToString());

                //Print a heartbeat to the log just to make it easier to parse by eye.
                if (secondCounter%10 == 0) {
                    Trace.WriteLine(DateTime.Now.ToString(), this.GetType().ToString());
                }
            }

            Trace.WriteLine("MaintenanceThread is ending.", this.GetType().ToString());
        }
Example #18
0
 public void AddMessageTag(MessageTag messageTag) => MessageTags.Add(messageTag);
Example #19
0
        public byte SendMessage(ADMMessage message)
        {
            if (message == null)
            {
                return(0);
            }

            if (Monitor.TryEnter(SendMessageLock, SEND_MESSAGE_LOCK_TIMEOUT))
            {
                try
                {
                    bool ready2send;
                    long msSinceLastSent = -1;

                    //loop waiting for a message response
                    do
                    {
                        msSinceLastSent = (DateTime.Now.Ticks - LastMessageSentOn.Ticks) / TimeSpan.TicksPerMillisecond;
                        if (LastMessageSent == null || MessageReceivedSuccess)
                        {
                            ready2send = true;
                        }
                        else
                        {
                            ready2send = msSinceLastSent > MESSAGE_RECEIVED_TIMEOUT;
                            if (!ready2send)
                            {
                                _sleep(10); //crappy idea to reduce load on cpu
                            }
                            else
                            {
                                //so here we have waited long enough for the previous sent message which has failed to arrive
                                //so we proceed with sending anyway
                                Tracing?.TraceEvent(TraceEventType.Warning, 0, "SendMessage {0}: Sending {1} timed out waiting to receive message {2} tag {3}", PortAndNodeID, message.Type, LastMessageSent.Type, LastMessageSent.Tag);
                            }
                        }
                    } while (!ready2send);

                    //store old values before sending (rollback if exception thrown)
                    //This unusual approach is because it appeared like messages were being received before the SendString could exit
                    ADMMessage lastMessageSent   = LastMessageSent;
                    DateTime   lastMessageSentOn = LastMessageSentOn;
                    try
                    {
                        lock (MessageStatsLock)
                        {
                            MessagesSent++;
                            LastMessageSent        = message;
                            LastMessageSentOn      = DateTime.Now;
                            MessageReceivedSuccess = false;
                        }
                        if (message.Tag == 0)
                        {
                            message.Tag = MessageTags.CreateTag();
                        }
                        message.SenderID = BoardID;
                        //Console.WriteLine("-------------> {0}: Sending message {1} tag {2} target {3}." , PortAndNodeID, message.Type, message.Tag, message.TargetID);
                        SendString(message.Serialize());
                    }
                    catch (Exception e)
                    {
                        lock (MessageStatsLock) //rollback
                        {
                            MessagesSent--;
                            LastMessageSent   = lastMessageSent;
                            LastMessageSentOn = lastMessageSentOn;
                        }
                        String errMsg = String.Format("{0} SendMessage: sending {1} (Tag={2}) produced exception {3} {4}", PortAndNodeID, message.Type, message.Tag, e.GetType(), e.Message);
                        Tracing?.TraceEvent(TraceEventType.Error, 0, errMsg);
                        throw e;
                    }
                }
                finally
                {
                    Monitor.Exit(SendMessageLock);
                }
            } //end attempt to try get lock
            else
            {
                //we waited too long to get the lock...
                throw new SendFailedException("ArduinoDeviceManager::SendMessage ... waited to long to obtain lock");
            }
            return(message.Tag);
        }
Example #20
0
 public void RemoveMessageTag(MessageTag messageTag) => MessageTags.Remove(messageTag);
 public ChunksAndPriority(Chunk[] chunks, MessageTags tags)
 {
     this.Chunks = chunks;
     this.Tags = tags;
 }
Example #22
0
        /// <summary>
        /// If connected, send a message, otherwise do nothing.
        /// </summary>
        /// <param name="chunk"></param>
        /// <param name="group"></param>
        public void Send(Chunk chunk, Group group, MessageTags tags)
        {
            AsyncCallback ac = new AsyncCallback(SendCallback);
            using (MemoryStream stream = new MemoryStream((int)(this.m_Encoder.MaximumChunkSize * 2))) {
                stream.Position = 0;
                this.m_Encoder.EncodeChunk(stream, chunk);

                Trace.WriteLine(string.Format("TCPClient sending message #{0}, chunk #{1} of {2}, {3} bytes.",
                    chunk.MessageSequence,
                    chunk.ChunkSequenceInMessage + 1,
                    chunk.NumberOfChunksInMessage,
                    chunk.Data.Length),
                    this.GetType().ToString());

                byte[] buffer = stream.GetBuffer();
                int length = (int)stream.Length;

                if ((m_Socket.Connected) && (m_Connected))
                    while (true) {
                        try {
                            m_Socket.BeginSend(buffer, 0, length, SocketFlags.None, ac, new SendState(m_Socket, length)); //BeginSend since it won't block.
                            break;
                        }
                        catch (SocketException se) {
                            if (se.ErrorCode == 10055) {
                                Trace.WriteLine("Client Send queue is full.  Sleeping 50 mS", this.GetType().ToString());
                                Thread.Sleep(50);
                            }
                            else {
                                Trace.WriteLine("SendThread socket exception: " + se.ToString() + " Error code: " + se.ErrorCode.ToString(), this.GetType().ToString());
                                break;
                            }
                        }
                        catch (ObjectDisposedException ode) {
                           Trace.WriteLine("SendThread ObjectDisposedException: " + ode.Message, this.GetType().ToString());
                           break;
                        }
                        catch (Exception e) {
                            Trace.WriteLine("SendThread Exception: " + e.ToString(), this.GetType().ToString());
                            break;
                        }
                    }
                else {
                    Trace.WriteLine("SendThread found a socket disconnected. Send request ignored.", this.GetType().ToString());
                }
            }
        }
Example #23
0
        /// <summary>
        /// Close disconnected client sockets, send heartbeat messages and do other assorted cleanup.
        /// </summary>
        private void MaintenanceThread()
        {
            //Prepare the heartbeat message
            TCPHeartbeatMessage heartbeat = new TCPHeartbeatMessage();
            BinaryFormatter     bf        = new BinaryFormatter();
            MemoryStream        hbms      = new MemoryStream();

#if GENERIC_SERIALIZATION
            heartbeat.Serialize().WriteToStream(hbms);
#else
            bf.Serialize(hbms, heartbeat);
#endif
            byte[]      hbbuf  = hbms.GetBuffer();
            int         hblen  = (int)hbms.Length;
            MessageTags hbtags = new MessageTags();

            //Lists for socket maintenance
            List <Guid>   toCleanup            = new List <Guid>();
            List <Socket> toClose              = new List <Socket>();
            int           secondCounter        = 0;
            int           connectedClientCount = 0;

            while (!this.m_Disposed)
            {
                //iterate approximately once per second
                for (int i = 0; ((i < 10) && (!m_Disposed)); i++)
                {
                    Thread.Sleep(100);
                }
                if (this.m_Disposed)
                {
                    break;
                }
                secondCounter++;

                lock (m_AllClients) {
                    //Close and remove disconnected sockets.
                    toCleanup.Clear();
                    toClose.Clear();
                    connectedClientCount = 0;
                    foreach (ClientData client in m_AllClients.Values)
                    {
                        if (client.ConnectionState == ConnectionState.Disconnected)
                        {
                            //Check for disconnected clients that have timed out
                            if (client.Timeout < DateTime.Now)
                            {
                                this.m_ServerSender.RemoveQueue(client.Id);
                                toCleanup.Add(client.Id);
                            }
                        }
                        else if (client.ConnectionState == ConnectionState.Connected)
                        {
                            //Find sockets to close for any new disconnects
                            if (!client.Socket.Connected)
                            {
                                toClose.Add(client.Socket);
                                client.Socket          = null;
                                client.ConnectionState = ConnectionState.Disconnected;
                                client.Timeout         = DateTime.Now + TimeSpan.FromMinutes(m_QueueTimeout);
                            }
                            else
                            {
                                connectedClientCount++;
                                //Send a heartbeat message to connected clients
                                if (secondCounter % HEARTBEAT_PERIOD == 0)
                                {
                                    //Trace.WriteLine("TCPServer sending Heartbeat to client ID " + client.Id.ToString(), this.GetType().ToString());
                                    this.m_ServerSender.Send(hbbuf, hblen, client, hbtags, 0, 0, true);
                                }
                            }
                        }
                        else if (client.ConnectionState == ConnectionState.Reconnecting)
                        {
                            //This is a temporary state used during handshaking
                            connectedClientCount++;
                        }
                    }

                    //Remove timed out clients from the master list
                    foreach (Guid goner in toCleanup)
                    {
                        m_AllClients.Remove(goner);
                    }
                }

                //Update published client count
                if (m_ClientCount != connectedClientCount)
                {
                    using (Synchronizer.Lock(this.SyncRoot)) {
                        this.SetPublishedProperty("ClientCount", ref m_ClientCount, connectedClientCount);
                        NetworkStatus newStatus = m_NetworkStatus.Clone();
                        newStatus.ClientCount = m_ClientCount;
                        this.SetPublishedProperty("NetworkStatus", ref m_NetworkStatus, newStatus);
                    }
                }

                //Close sockets
                foreach (Socket s in toClose)
                {
                    try {
                        Trace.WriteLine("MaintenanceThread closing disconnected socket: " + s.RemoteEndPoint.ToString(), this.GetType().ToString());
                        s.Close();
                    }
                    catch (Exception e) {
                        Trace.WriteLine("MaintenanceThread exception while closing socket: " + e.ToString(), this.GetType().ToString());
                    }
                }

                if (toCleanup.Count > 0)
                {
                    Trace.WriteLine("MaintenanceThread timed out " + toCleanup.Count.ToString() + " disconnected socket(s).", this.GetType().ToString());
                }

                //Print a heartbeat to the log just to make it easier to parse by eye.
                if (secondCounter % 10 == 0)
                {
                    Trace.WriteLine(DateTime.Now.ToString(), this.GetType().ToString());
                }
            }

            Trace.WriteLine("MaintenanceThread is ending.", this.GetType().ToString());
        }
        /// <summary>
        /// Enqueue the send to occur when network bandwidth is available.
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="length"></param>
        /// <param name="socket"></param>
        /// <param name="participant"></param>
        internal void Send(byte[] buffer, int length, ClientData client, MessageTags tags,
            ulong messageSequence, ulong chunkSequence, bool isHeartbeat)
        {
            using (Synchronizer.Lock(m_SocketMap)) {
                Debug.Assert(m_SocketMap.ContainsKey(client.Id), "TCPServerSender.Send found a missing Socket Map entry.");
            }

            //Trace.WriteLine("TCPServerSender.Send seq=" + messageSequence.ToString());

            //Enqueue the message
            using (Synchronizer.Cookie cookie = Synchronizer.Lock(m_SendQueue)) {
                m_SendQueue.Enqueue(new SendParameters(buffer, length, client.Id, tags, messageSequence, chunkSequence, isHeartbeat),
                    client.Participant);

                // Notify the sender thread that the queue's status has changed.
                cookie.PulseAll();
            }
        }