Beispiel #1
0
        private BroadcastMessage deserialize(byte[] buf)
        {
            MemoryStream     ms      = new MemoryStream(buf);
            BroadcastMessage message = null;

#if GENERIC_SERIALIZATION
            try {
                SerializedPacket p = new SerializedPacket(ms);
                message = (BroadcastMessage)PacketTypes.DecodeMessage(null, p);
            } catch (Exception e) {
                Trace.WriteLine(e.ToString(), this.GetType().ToString());
                return(null);
            }
#else
            BinaryFormatter bf = new BinaryFormatter();
            try {
                message = (BroadcastMessage)bf.Deserialize(ms);
            } catch (SerializationException se) {
                Trace.WriteLine("Failed to deserialize a BroadcastMessage: " + se.Message, this.GetType().ToString());
                return(null);
            } catch (Exception e) {
                Trace.WriteLine(e.ToString(), this.GetType().ToString());
                return(null);
            }
#endif
            return(message);
        }
Beispiel #2
0
 /// <summary>
 /// Construct the message
 /// </summary>
 /// <param name="p">The packet</param>
 public Message(Message parent, SerializedPacket p)
 {
     SerializedPacket.VerifyPacket(p, this.GetClassId());
     this.Group = (!SerializedPacket.IsNullPacket(p.PeekNextPart())) ?
                  new Group(p.PeekNextPart()) : null; p.GetNextPart();
     if (SerializedPacket.IsNullPacket(p.PeekNextPart()))
     {
         this.TargetId = null;
     }
     else if (p.PeekNextPart().Type == PacketTypes.GuidId)
     {
         this.TargetId = (ValueType)SerializedPacket.DeserializeGuid(p.PeekNextPart());
     }
     else if (p.PeekNextPart().Type == PacketTypes.ByteArrayClassId)
     {
         this.TargetId = (ValueType) new Model.Presentation.ByteArray(SerializedPacket.DeserializeGuid(p.PeekNextPart()));
     }
     else
     {
         throw new Exception("Unknown ValueType");
     }
     p.GetNextPart();
     this.Parent = parent;
     this.Child  = (!SerializedPacket.IsNullPacket(p.PeekNextPart())) ?
                   (Message)PacketTypes.DecodeMessage(this, p.PeekNextPart()): null; p.GetNextPart();
     this.Predecessor = (!SerializedPacket.IsNullPacket(p.PeekNextPart())) ?
                        (Message)PacketTypes.DecodeMessage(this, p.PeekNextPart()): null; p.GetNextPart();
     this.m_MessageIdentifier = SerializedPacket.DeserializeGuid(p.GetNextPart());
 }
Beispiel #3
0
            /// <summary>
            /// Once a message is complete, deserializes the message from its chunks.
            /// </summary>
            /// <returns>The deserialized message</returns>
            public object DeserializeMessage()
            {
                if (!this.IsComplete)
                {
                    throw new InvalidOperationException("Cannot deserialize the chunked message until all chunks have been received.");
                }

                // First count the total size of the concatenated data.
                long total = 0;

                foreach (byte[] chunk in this.m_Data)
                {
                    total += chunk.LongLength;
                }

                // Concatenate all of the data into a single array.
                // TODO: Make a new "MemoryStream" class which can read directly from the jagged 2D array.
                byte[] serialized = new byte[total];
                total = 0;
                foreach (byte[] chunk in this.m_Data)
                {
                    chunk.CopyTo(serialized, total);
                    total += chunk.LongLength;
                }

                using (MemoryStream ms = new MemoryStream(serialized)) {
#if GENERIC_SERIALIZATION
                    return(PacketTypes.DecodeMessage(null, new SerializedPacket(ms)));
#else
                    return(this.m_Formatter.Deserialize(ms));
#endif
                }
            }
Beispiel #4
0
        /// <summary>
        /// Receive and enqueue messages from the server endpoint.
        /// </summary>
        private void ReceiveThread()
        {
            BinaryFormatter binaryFormatter = new BinaryFormatter();

#if GENERIC_SERIALIZATION
            IGenericSerializable msg = null;
#else
            object msg = null;
#endif

            while (!m_Disposed)
            {
                if ((m_Socket != null) && (m_Socket.Connected) && (m_Connected))
                {
                    try {
#if GENERIC_SERIALIZATION
                        msg = PacketTypes.DecodeMessage(null, new SerializedPacket(m_NetworkStream));
#else
                        msg = binaryFormatter.Deserialize(m_NetworkStream);  //blocks
#endif
                    }
                    catch (Exception e) {
                        if (e.InnerException is SocketException)
                        {
                            if (((SocketException)e.InnerException).ErrorCode == 10053)
                            {
                                Trace.WriteLine("SocketException 10053: Read aborted by local host.");
                            }
                            else if (((SocketException)e.InnerException).ErrorCode == 10054)
                            {
                                Trace.WriteLine("SocketException 10054: Read aborted.  Remote connection lost.");
                            }
                            else
                            {
                                Trace.WriteLine("ReceiveThread SocketException while reading.  ErrorCode=" + ((SocketException)e.InnerException).ErrorCode.ToString());
                            }
                        }
                        else
                        {
                            Trace.WriteLine("ReceiveThread failed to read: " + e.ToString());
                        }

                        continue;
                    }

                    lock (m_ReceiveQueue) {
                        this.m_LastMsgReceived = DateTime.Now;
                        if (!(msg is TCPHeartbeatMessage))
                        {
                            m_ReceiveQueue.Enqueue(new ReceiveMessageAndParticipant(msg, m_ServerParticipant));
                            Chunk c = (msg as Chunk);
                            if (c != null)
                            {
                                m_LastMsgSequence   = c.MessageSequence;
                                m_LastChunkSequence = c.ChunkSequenceInMessage;
                            }
                        }
                        else
                        {
                            Trace.WriteLine("TCPClient received heartbeat message at " + DateTime.Now.ToString(), this.GetType().ToString());
                        }
                    }
                }
                else
                {
                    Thread.Sleep(100);
                }
            }
            Trace.WriteLine("Receive Thread is ending.", this.GetType().ToString());
        }
Beispiel #5
0
        /// <summary>
        /// Maintain the connection to the server endpoint.
        /// </summary>
        private void ConnectThread()
        {
            while (!m_Disposed)
            {
                if ((m_Socket == null) || (!m_Socket.Connected) || (!m_Connected))
                {
                    Socket s = (Socket)Interlocked.Exchange(ref m_Socket, null);
                    if (s != null)
                    {
                        s.Close();
                    }

                    try {
                        if (m_Disposed)
                        {
                            break;
                        }
                        using (Synchronizer.Lock(this.SyncRoot)) {
                            this.SetPublishedProperty("Connected", ref this.m_Connected, false);
                            NetworkStatus newStatus = m_NetworkStatus.Clone();
                            newStatus.ConnectionStatus = ConnectionStatus.TryingToConnect;
                            this.SetPublishedProperty("NetworkStatus", ref m_NetworkStatus, newStatus);
                        }
                        Trace.WriteLine("Attempting connection to server: " + this.m_RemoteEP.ToString(), this.GetType().ToString());
                        m_Socket = new Socket(this.m_RemoteEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
                        m_Socket.ReceiveTimeout = 20000; //Temporarily set to 20 seconds for the handshaking phase.
                        //In case the server endpoint changed:
                        UpdateRemoteEndpoint();
                        m_Socket.Connect(this.m_RemoteEP);
                        m_NetworkStream = new NetworkStream(m_Socket); //NetworkStream does not own the socket.

                        //Receive handshake
                        BinaryFormatter bf = new BinaryFormatter();
#if GENERIC_SERIALIZATION
                        IGenericSerializable o = PacketTypes.DecodeMessage(null, new SerializedPacket(m_NetworkStream));
#else
                        object o = bf.Deserialize(m_NetworkStream);
#endif
                        if (o is TCPHandshakeMessage)
                        {
                            Trace.WriteLine("Handshake received from " + ((TCPHandshakeMessage)o).ParticipantId.ToString() + " ep=" + ((TCPHandshakeMessage)o).EndPoint.ToString());
                            //send a handshake
                            TCPHandshakeMessage handshake = new TCPHandshakeMessage(m_Participant, (IPEndPoint)m_Socket.LocalEndPoint);
                            lock (this.m_ReceiveQueue) {
                                //If this is a reconnect, these values tell the server where we left off
                                handshake.LastMessageSequence = m_LastMsgSequence;
                                handshake.LastChunkSequence   = m_LastChunkSequence;
                            }
                            MemoryStream ms = new MemoryStream();
#if GENERIC_SERIALIZATION
                            handshake.Serialize().WriteToStream(ms);
#else
                            bf.Serialize(ms, handshake);
#endif
                            m_NetworkStream.Write(ms.GetBuffer(), 0, (int)ms.Length);
                            Trace.WriteLine("Handshake sent.", this.GetType().ToString());
                            //The first time we connect to a server we create a new ParticipantModel to represent the server.
                            if (m_ServerParticipant == null)
                            {
                                TCPHandshakeMessage h = (TCPHandshakeMessage)o;
                                m_ServerId          = h.ParticipantId;
                                m_ServerParticipant = new ParticipantModel(m_ServerId, h.HumanName);
                            }
                            else
                            {
                                //In reconnect scenarios we keep the same server ParticipantModel, but the Guid could
                                //change if the server was restarted.  In this case we just want to update the Guid.
                                //Notice that we can't create a new ParticipantModel here without breaking some things.
                                if (!m_ServerId.Equals(((TCPHandshakeMessage)o).ParticipantId))
                                {
                                    m_ServerId = ((TCPHandshakeMessage)o).ParticipantId;
                                    m_ServerParticipant.Guid = m_ServerId;
                                }
                            }
                        }
                        else
                        {
                            throw new ApplicationException("Invalid handshake received: " + o.GetType().ToString());
                        }

                        m_Socket.ReceiveTimeout = 0; //Reset socket to infinite timeout.
                        m_ClientTimeout         = SetClientTimeout();

                        using (Synchronizer.Lock(this.SyncRoot)) {
                            //Setting this property allows the ReceiveThread to begin:
                            this.SetPublishedProperty("Connected", ref this.m_Connected, true);
                            NetworkStatus newStatus = m_NetworkStatus.Clone();
                            newStatus.ConnectionStatus = ConnectionStatus.Connected;
                            this.SetPublishedProperty("NetworkStatus", ref m_NetworkStatus, newStatus);
                        }

                        lock (this.m_ReceiveQueue) {
                            //This enables the client timeout:
                            this.m_LastMsgReceived = DateTime.Now;
                        }

                        Trace.WriteLine("Connected.", this.GetType().ToString());
                    }
                    catch (SocketException se) {
                        if (se.ErrorCode == 10060)
                        {
                            Trace.WriteLine("ConnectThread SocketException 10060: remote host failed to respond.");
                        }
                        else if (se.ErrorCode == 10038)
                        {
                            Trace.WriteLine("ConnectThread SocketException 10038: operation attempted on non-socket.");
                        }
                        else
                        {
                            Trace.WriteLine("ConnectThread SocketException " + se.ErrorCode.ToString() + ": " + se.ToString());
                        }
                    }
                    catch (IOException ioe) {
                        Trace.WriteLine("ConnectThread IOException: " + ioe.Message);
                        if ((ioe.InnerException != null) && (ioe.InnerException is SocketException))
                        {
                            Trace.WriteLine("  InnerException: SocketException " + ((SocketException)ioe.InnerException).ErrorCode.ToString());
                        }
                    }
                    catch (Exception e) {
                        Trace.WriteLine("ConnectThread exception: " + e.ToString());
                    }
                }

                for (int i = 0; ((i < 10) && (!m_Disposed)); i++)
                {
                    Thread.Sleep(100);
                }
            }
            Trace.WriteLine("ConnectThread is ending.", this.GetType().ToString());
        }
Beispiel #6
0
        /// <summary>
        /// Attempts to assemble the chunk into a completed message.
        /// If this is the last chunk of a message, the completed message
        /// is returned, and any unsatisfied dependencies that are now
        /// satisfied by the completed message are recursively assembled.
        /// All such assembled message are also returned via the enumeration.
        /// </summary>
        /// <remarks>
        /// This method is called by <see cref="Add"/> and <see cref="FlushWaitingChunks"/>,
        /// after all chunk dependencies are satisfied.
        /// </remarks>
        /// <param name="chunk">The chunk to assemble.</param>
        /// <returns>
        /// The enumeration of decoded messages.
        /// The enumeration may be empty or may
        /// have arbitrarily many members if dependencies were
        /// (recursively) satisfied by this chunk.
        /// </returns>
        protected IEnumerable <object> Assemble(Chunk chunk)
        {
            // Process single-chunk messages immediately.
            if (chunk.NumberOfChunksInMessage <= 1)
            {
                // Don't create a MessageAssembler for singleton chunks.
                // Instead, just return the message immediately.
                using (MemoryStream ms = new MemoryStream(chunk.Data)) {
#if GENERIC_SERIALIZATION
                    yield return(PacketTypes.DecodeMessage(null, new SerializedPacket(ms)));
#else
                    yield return(this.m_Formatter.Deserialize(ms));
#endif

                    // The message has been completed, so flush any chunks which were waiting for it.
                    foreach (object dependent in this.FlushWaitingChunks(chunk.MessageSequence))
                    {
                        yield return(dependent);
                    }
                    yield break;
                }
            }

            // For multi-chunk messages, we first attempt to find an existing MessageAssembler
            // instance for the message to which the chunk belongs (based on the range of chunk
            // sequence numbers the message spans).
            MessageAssembler assembler = this.NewestMessageAssember, previous = null;
            object           message;
            for (; ;)
            {
                bool done, remove, complete;

                // If there does not exist any assembler for which IsInRange(chunk) returned true,
                // create one to hold the chunk.
                if (assembler == null)
                {
                    Debug.WriteLine(string.Format("Creating a new MessageAssembler to manage multipart message (message #{0}, chunks #{1}-{2})",
                                                  chunk.MessageSequence,
                                                  chunk.ChunkSequenceInMessage + 1,
                                                  chunk.NumberOfChunksInMessage),
                                    this.GetType().ToString());

                    assembler = new MessageAssembler(chunk.MessageSequence,
                                                     chunk.NumberOfChunksInMessage, this.m_Formatter);

                    // Insert the assembler as the first entry in our linked list,
                    // since it is most likely to be used by subsequent chunks.
                    assembler.NextOldestAssembler = this.NewestMessageAssember;
                    this.NewestMessageAssember    = assembler;
                }

                // See if the chunk belongs to the current assembler.
                if (assembler.MessageSequence == chunk.MessageSequence)
                {
                    // If so, add the chunk to it, and we can stop searching.
                    assembler.Add(chunk);
                    done = true;

                    // If the message has been fully assembled, process it
                    // and remove the no-longer-needed assembler.
                    complete = assembler.IsComplete;
                    if (complete)
                    {
                        message = assembler.DeserializeMessage();
                        remove  = true;
                    }
                    else
                    {
                        message = null;
                        remove  = false;
                    }
                }

                else if (assembler.MessageSequence < chunk.OldestRecoverableMessage)
                {
                    // For each message assembler that is waiting for more chunks (and to which the current
                    // chunk does not belong), make sure it will be possible to complete the message in
                    // the future.  If the sender reports that its OldestRecoverableFrame is greater than
                    // the sequence number of any frame yet needed to complete the message, then no
                    // NACK we send can ever satisfy our needs, so we discard the message completely
                    // (removing the assembler from the linked list).
                    Debug.WriteLine(string.Format("### Giving up on message #{0} (chunks #{0}-{1}): the oldest available chunk is {2}!",
                                                  chunk.MessageSequence,
                                                  chunk.ChunkSequenceInMessage + 1,
                                                  chunk.NumberOfChunksInMessage,
                                                  chunk.OldestRecoverableMessage), this.GetType().ToString());
                    remove   = true;
                    message  = null;
                    done     = false;
                    complete = false;
                }
                else
                {
                    remove   = false;
                    message  = null;
                    done     = false;
                    complete = false;
                }

                // If the assembler is no longer useful, remove it from the linked list.
                // (There are a couple of conditions, above, under which this might happen.)
                if (remove)
                {
                    if (previous == null)
                    {
                        this.NewestMessageAssember = assembler.NextOldestAssembler;
                    }
                    else
                    {
                        previous.NextOldestAssembler = assembler.NextOldestAssembler;
                    }
                }

                // If an assembler was found which accepted the chunk, we're done.
                // (There are a couple of conditions, above, under which this might happen.)
                if (done)
                {
                    if (complete)
                    {
                        yield return(message);

                        // The message has been completed, so flush any chunks which were waiting for it.
                        foreach (object dependent in this.FlushWaitingChunks(chunk.MessageSequence))
                        {
                            yield return(dependent);
                        }
                    }
                    yield break;
                }
                else
                {
                    // Get the next assembler.  Do not break from the loop if there
                    // is no "next" assembler, since one will be created.
                    previous  = assembler;
                    assembler = assembler.NextOldestAssembler;
                }
            }
        }
Beispiel #7
0
        /// <summary>
        /// When the ListenThread has a prospective new client on the line, here we attempt to establish the connection.
        /// </summary>
        /// <param name="ar"></param>
        private void AcceptSocketCallback(IAsyncResult ar)
        {
            Socket      s           = null;
            TcpListener tcpListener = (TcpListener)ar.AsyncState;

            try {
                s = tcpListener.EndAcceptSocket(ar);
            }
            catch (ObjectDisposedException ode) {
                Trace.WriteLine("AcceptSocketCallback ObjectDisposedException" + ode.Message, this.GetType().ToString());
                return;
            }
            catch (SocketException se) {
                Trace.WriteLine("AcceptSocketCallback: " + se.ToString(), this.GetType().ToString());
                return;
            }
            catch (Exception e) {
                Trace.WriteLine(e.ToString(), this.GetType().ToString());
                return;
            }
            finally {
                m_ClientConnected.Set();  //Let the ListenThread continue
            }

            if (s != null)
            {
                try {
                    //Send a handshake
                    NetworkStream       ns        = new NetworkStream(s); //Here the network stream does not "own" the socket, so we have to close it explicitly.
                    BinaryFormatter     bf        = new BinaryFormatter();
                    MemoryStream        ms        = new MemoryStream();
                    TCPHandshakeMessage handshake = new TCPHandshakeMessage(this.m_Participant, new IPEndPoint(0, 0));
#if GENERIC_SERIALIZATION
                    handshake.Serialize().WriteToStream(ms);
#else
                    bf.Serialize(ms, handshake);
#endif
                    ns.Write(ms.GetBuffer(), 0, (int)ms.Length);
                    Trace.WriteLine("Handshake sent.", this.GetType().ToString());

                    //Receive a handshake
#if GENERIC_SERIALIZATION
                    IGenericSerializable o = PacketTypes.DecodeMessage(null, new SerializedPacket(ns));
#else
                    object o = bf.Deserialize(ns);
#endif

                    if (o is TCPHandshakeMessage)
                    {
                        TCPHandshakeMessage h = (TCPHandshakeMessage)o;
                        Trace.WriteLine("Handshake received from " + h.ParticipantId.ToString() + " ep=" + h.EndPoint.ToString(), this.GetType().ToString());
                        ParticipantModel p;

                        //In case this client still has a socket open, force it to close
                        ClosePreviousSocket(h.ParticipantId);

                        //Notice that as soon as we add the entry to m_AllClients, it is eligible for sending of outbound messages:
                        bool newClient = false;
                        lock (m_AllClients) {
                            if (m_AllClients.ContainsKey(h.ParticipantId))
                            {
                                ((ClientData)m_AllClients[h.ParticipantId]).ConnectionState = ConnectionState.Connected;
                                ((ClientData)m_AllClients[h.ParticipantId]).Socket          = s;
                                p = ((ClientData)m_AllClients[h.ParticipantId]).Participant;
                                //Add the participant to the classroom model
                                using (Synchronizer.Lock(m_Classroom.SyncRoot)) {
                                    m_Classroom.Participants.Add(p);
                                }
                                ((ClientData)m_AllClients[h.ParticipantId]).Timeout = DateTime.MaxValue;
                                this.m_ServerSender.Reconnect(((ClientData)m_AllClients[h.ParticipantId]), h.LastMessageSequence, h.LastChunkSequence);
                            }
                            else
                            {
                                p = new ParticipantModel(h.ParticipantId, h.HumanName);
                                //Add the participant to the classroom model
                                using (Synchronizer.Lock(m_Classroom.SyncRoot)) {
                                    m_Classroom.Participants.Add(p);
                                }
                                ClientData client = new ClientData(s, h.ParticipantId, p);
                                this.m_ServerSender.AddClient(client);
                                m_AllClients.Add(h.ParticipantId, client);
                                newClient = true;
                            }
                        }

                        //Update connected client count for network status
                        using (Synchronizer.Lock(this.SyncRoot)) {
                            this.SetPublishedProperty("ClientCount", ref this.m_ClientCount, this.m_ClientCount + 1);
                            NetworkStatus newStatus = m_NetworkStatus.Clone();
                            newStatus.ClientCount = this.m_ClientCount;
                            this.SetPublishedProperty("NetworkStatus", ref m_NetworkStatus, newStatus);
                        }

                        //Start a receive thread for this socket.
                        Thread receiveThread = new Thread(ReceiveThread);
                        receiveThread.Start(new ReceiveThreadArgs(p, ns, s.RemoteEndPoint));

                        //Send the current presentation state if this is a new client
                        if (newClient)
                        {
                            m_Sender.ForceUpdate(new SingletonGroup(p));
                        }
                    }
                    else
                    {
                        Trace.WriteLine("AcceptSocketCallback invalid handshake from " + s.RemoteEndPoint.ToString(), this.GetType().ToString());
                    }
                }
                catch (Exception e) {
                    Trace.WriteLine("AcceptSocketCallback exception while handshaking with " + s.RemoteEndPoint.ToString() + ": " + e.ToString(), this.GetType().ToString());
                }
            }
        }
Beispiel #8
0
        /// <summary>
        /// We use one receive thread per socket
        /// </summary>
        /// <param name="o"></param>
        private void ReceiveThread(Object o)
        {
            ReceiveThreadArgs args        = (ReceiveThreadArgs)o;
            NetworkStream     ns          = args.NetStream;
            ParticipantModel  participant = args.Participant;
            Guid     participantId        = participant.Guid;
            EndPoint remoteEP             = args.RemoteEndPoint;

            IFormatter binaryFormatter = new BinaryFormatter();

#if GENERIC_SERIALIZATION
            IGenericSerializable msg = null;
#else
            object msg = null;
#endif

            while (!this.m_Disposed)
            {
                try {
#if GENERIC_SERIALIZATION
                    msg = PacketTypes.DecodeMessage(null, new SerializedPacket(ns));
#else
                    msg = binaryFormatter.Deserialize(ns);  //blocks
#endif
                }
                catch (SerializationException se) {
                    Trace.WriteLine(se.ToString(), this.GetType().ToString());
                    break;
                }
                catch (Exception e) {
                    if (e.InnerException is SocketException)
                    {
                        SocketException se = (SocketException)e.InnerException;
                        if (se.ErrorCode == 10054)
                        {
                            Trace.WriteLine("ReceiveThread detected a disconnected client during NetworkStream.Read: " + remoteEP.ToString(), this.GetType().ToString());
                            break;
                        }
                        else if (se.ErrorCode == 10053)
                        {
                            Trace.WriteLine("ReceiveThread: socket was closed by local host.", this.GetType().ToString());
                            break;
                        }
                        else if (se.ErrorCode == 10004)
                        {
                            Trace.WriteLine("ReceiveThread: a blocking operation was interrupted.", this.GetType().ToString());
                            break;
                        }
                        else
                        {
                            Trace.WriteLine("SocketException in ReceiveThread. remote Endpoint= " + remoteEP.ToString() + " " + se.ToString() + " error code: " + se.ErrorCode.ToString(), this.GetType().ToString());
                            break;
                        }
                    }
                    else
                    {
                        Trace.WriteLine("Exception while reading: " + e.ToString(), this.GetType().ToString());
                        break;
                    }
                }

                lock (m_ReceiveQueue) {
                    m_ReceiveQueue.Enqueue(new ReceiveMessageAndParticipant(msg, participant));
                }
            }

            //Remove the participant from the classroom here.  Notice that the socket for this participant may still be open.
            using (Synchronizer.Lock(m_Classroom.SyncRoot)) {
                m_Classroom.Participants.Remove(participant);
            }

            Trace.WriteLine("ReceiveThread is ending for remote endpoint=" + remoteEP.ToString(), this.GetType().ToString());
        }