예제 #1
0
        /// <summary>
        /// Broadcasts a single chunk and stores it in the frame buffer.
        /// </summary>
        /// <param name="chunk">The chunk to send</param>
        private void RtpSend(Chunk chunk, MessagePriority priority)
        {
            using(Synchronizer.Lock(this)) {
                using(MemoryStream stream = new MemoryStream((int) (this.m_Encoder.MaximumChunkSize * 2))) {
                    // Store the sequence number of non-real-time chunks so they can be NACKed.
                    // Use "++this.m_FrameSequence" so that non-real-time chunks always have a
                    // FrameSequence that is greater than 0.
                    if (priority != MessagePriority.RealTime)
                        chunk.FrameSequence = ++this.m_FrameSequence;

                    // If the chunk is not real time (or it is being resent a second time),
                    // store the chunk in the buffer so a NACK request can resend it.
                    if (chunk.FrameSequence > ulong.MinValue)
                        this.m_FrameBuffer.Insert(chunk);

                    // Buffering the chunk probably evicted other chunks from the buffer,
                    // so get the oldest frame in the buffer so receivers don't waste time
                    // sending NACKs for chunks that are no longer available.
                    chunk.OldestRecoverableFrame = this.m_FrameBuffer.OldestRecoverableFrame;
                    chunk.OldestRecoverableMessage = this.m_FrameBuffer.OldestRecoverableMessage;

                    // Serialize the chunk (which itself contains a serialized message).
                    // TODO: see whether the wire protocol can be made more efficient.
                    stream.Position = 0;
                    this.m_Encoder.EncodeChunk(stream, chunk);

                    // Prepare the frame to be sent over RTP.
                    BufferChunk buffer = new BufferChunk(stream.GetBuffer(), 0, (int) stream.Length);

                    // Send it (or drop some percentage of packets if the debug option is enabled).
                    if(DEBUG_DROP_RANDOM_FRAMES <= 0 || new Random().Next(100) >= DEBUG_DROP_RANDOM_FRAMES) {

                        Debug.WriteLine(string.Format("Sending frame #{0} ({1} bytes, message #{2}, chunk #{3} of {4}, depending on #{5}, priority {6})",
                            chunk.FrameSequence,
                            chunk.Data.Length,
                            chunk.MessageSequence,
                            chunk.ChunkSequenceInMessage + 1,
                            chunk.NumberOfChunksInMessage,
                            chunk.MessageDependency,
                            priority),
                            this.GetType().ToString());

                        try {
                            this.m_RtpSender.Send( buffer );
                        } catch( Exception ) {
                            this.Reset();
                        }
                    }

                    else {
                        Debug.WriteLine(string.Format("DEBUG: Dropping frame #{0} ({1} bytes, message #{2}, chunk #{3} of {4}, depending on #{5}, priority {6})",
                            chunk.FrameSequence,
                            chunk.Data.Length,
                            chunk.MessageSequence,
                            chunk.ChunkSequenceInMessage + 1,
                            chunk.NumberOfChunksInMessage,
                            chunk.MessageDependency,
                            priority),
                            this.GetType().ToString());
                    }
                }
            }
        }
예제 #2
0
 /// <summary>
 /// Writes a chunk to a data stream.
 /// </summary>
 public void EncodeChunk(Stream stream, Chunk chunk)
 {
     // FIXME: More efficient encoding of chunks might be possible here.
     //   Custom encoding would require a Decode method to be used by RTPMessageReceiver.
     this.m_Formatter.Serialize(stream, chunk);
 }
예제 #3
0
            /// <summary>
            /// Serializes the given message and constructs a number of
            /// chunks containing the serialized data.
            /// </summary>
            /// <param name="message">The message to serialize.</param>
            /// <param name="messageSequence">
            /// A reference to a counter which will be incremented.
            /// This becomes the value of each chunk's
            /// <see cref="Chunk.MessageSequence"/> field.
            /// </param>
            /// <returns>
            /// A non-empty array containing the created chunks.
            /// </returns>
            public Chunk[] MakeChunks(Message message, ref ulong messageSequence)
            {
                ++messageSequence;
                ulong currentChunkSequence = 0;
                ulong maxSize = this.m_MaximumChunkSize;

                using(MemoryStream stream = new MemoryStream()) {
                    // Serialize the message.
                    this.m_Formatter.Serialize(stream, message);

                    // Calculate the number of chunks needed to span the message and allocate an array to hold them.
                    ulong length = ((ulong) stream.Length);
                    ulong count = (length / maxSize) + (length % maxSize == 0ul ? 0ul : 1ul);
                    Chunk[] chunks = new Chunk[count];

                    stream.Position = 0;
                    int index = 0;
                    for(ulong amountLeft; (amountLeft = length - ((ulong) stream.Position)) > 0; index++) {
                        // Read the deserialized data into a small array.
                        byte[] chunk = new byte[amountLeft < maxSize ? amountLeft : maxSize];
                        int read = stream.Read(chunk, 0, chunk.Length);
                        if(read != chunk.Length)
                            throw new ApplicationException("Couldn't read enough data from MemoryStream.");

                        // Create a chunk, updating the sequence numbers.
                        chunks[index] = new Chunk(chunk, messageSequence, currentChunkSequence++, count);
                    }

                    return chunks;
                }
            }
예제 #4
0
        /// <summary>
        /// Updates <see cref="m_Buffer"/> with the specified chunk at the specified index,
        /// and also keeps the <see cref="OldestMessageSequence"/> value up-to-date.
        /// </summary>
        /// <param name="index">The index at which to place the chunk.</param>
        /// <param name="chunk">
        /// The chunk to insert, or <c>null</c> if the chunk is being removed from the buffer.
        /// </param>
        private void Set(ulong index, Chunk chunk)
        {
            ulong count;
            ulong message;

            // First decrement the count for the OLD chunk's message sequence number.
            Chunk old = this.m_Buffer[index];
            if (old != null) {
                message = old.MessageSequence;
                if (message > ulong.MinValue)
                    if (this.m_MessageSequences.TryGetValue(message, out count) && count > 0)
                        if (count <= 1)
                            this.m_MessageSequences.Remove(message);
                        else
                            this.m_MessageSequences[message] = count - 1;
                    else throw new ApplicationException("Unmatched insertions and removals into the FrameBuffer.");
            }

            // Now insert the chunk in the buffer.
            this.m_Buffer[index] = chunk;

            // Finally increment the count for the NEW chunk's message sequence number.
            if (chunk != null) {
                message = chunk.MessageSequence;
                if (message > ulong.MinValue)
                    if (this.m_MessageSequences.TryGetValue(message, out count))
                        this.m_MessageSequences[message] = count + 1;
                    else this.m_MessageSequences[message] = 1;
            }
        }
예제 #5
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());
                }
            }
        }
        /// <summary>
        /// Broadcasts a single chunk and stores it in the frame buffer.
        /// </summary>
        /// <param name="chunk">The chunk to send</param>
        private void CapabilitySend(Chunk chunk, MessagePriority priority)
        {
            using(Synchronizer.Lock(this)) {
                // Store the sequence number of non-real-time chunks so they can be NACKed.
                // Use "++this.m_FrameSequence" so that non-real-time chunks always have a
                // FrameSequence that is greater than 0.
                if (priority != MessagePriority.RealTime)
                    chunk.FrameSequence = ++this.m_FrameSequence;

                // If the chunk is not real time (or it is being resent a second time),
                // store the chunk in the buffer so a NACK request can resend it.
                if (chunk.FrameSequence > ulong.MinValue)
                    this.m_FrameBuffer.Insert(chunk);

                // Buffering the chunk probably evicted other chunks from the buffer,
                // so get the oldest frame in the buffer so receivers don't waste time
                // sending NACKs for chunks that are no longer available.
                chunk.OldestRecoverableFrame = this.m_FrameBuffer.OldestRecoverableFrame;
                chunk.OldestRecoverableMessage = this.m_FrameBuffer.OldestRecoverableMessage;

                Debug.WriteLine(string.Format("Sending frame #{0} ({1} bytes, message #{2}, chunk #{3} of {4}, depending on #{5}, priority {6})",
                    chunk.FrameSequence,
                    chunk.Data.Length,
                    chunk.MessageSequence,
                    chunk.ChunkSequenceInMessage + 1,
                    chunk.NumberOfChunksInMessage,
                    chunk.MessageDependency,
                    priority),
                    this.GetType().ToString());

                SendObject(new CapabilityMessageWrapper(chunk, m_LocalId, Guid.Empty, m_Capability.IsSender));
            }
        }
예제 #7
0
 /// <summary>
 /// Finds and removes the chunk with the given 
 /// <see cref="Chunk.FrameSequence">frame sequence number</see> from the buffer.
 /// <remarks>
 /// Note that this does NOT update <see cref="OldestRecoverableFrame"/>
 /// or <see cref="OldestRecoverableMessage"/>.  It is expected that the
 /// chunk will be reinserted into the buffer in the future.
 /// </remarks>
 /// </summary>
 /// <param name="frameSequence">
 /// The <see cref="Chunk.FrameSequence">frame sequence number</see> to extract
 /// from the buffer.
 /// </param>
 /// <param name="chunk">The chunk extracted from the buffer, if available.</param>
 /// <returns>
 /// <c>true</c> if the given <paramref name="frameSequence"/> was in the range
 /// <see cref="OldestRecoverableFrame"/> to <see cref="NewestRecoverableFrame"/>;
 /// <c>false</c> otherwise.  (A <c>true</c> return value does not imply that
 /// <paramref name="chunk"/> was set to non-null, since the chunk with the given
 /// <paramref name="frameSequence"/> might have been previously been removed from
 /// the buffer by <see cref="Take"/>, or might never have been inserted in the
 /// first place.)
 /// </returns>
 public bool Take(ulong frameSequence, out Chunk chunk)
 {
     if (this.m_Oldest <= frameSequence && frameSequence <= this.m_Newest) {
         ulong index = frameSequence % this.m_BufferSize;
         // FIXME: This shouldn't update OldestMessageSequence.
         // But if we don't, then Insert() will cause the count to get unbalanced.
         chunk = this.m_Buffer[index];
         this.Set(index, null);
         Debug.Assert(chunk == null || chunk.FrameSequence == frameSequence,
             "FrameBuffer table entry has mismatched frame sequence number.");
         return true;
     } else {
         chunk = null;
         return false;
     }
 }
예제 #8
0
        /// <summary>
        /// Attempts to assemble a chunk from a chunked messages,
        /// and sends a NACK when dropped chunks are detected.
        /// </summary>
        /// <param name="chunk">The received chunk to process</param>
        /// <returns>If the chunk was the last remaining chunk needed to complete a message,
        /// returns the deserialized message; otherwise, <code>null</code></returns>
        public IEnumerable<object> Add(Chunk chunk)
        {
            if (chunk.FrameSequence > 0) {
                // Check if we have seen this packet before. If so, do nothing.
                if (DisjointSet.Contains(ref this.m_ReceivedFrameSequences, chunk.FrameSequence))
                    yield break;
                DisjointSet.Add(ref this.m_ReceivedFrameSequences, chunk.FrameSequence);

                // Save space in the ReceivedSequenceNumbers queue by adding all chunks
                // that we can't expect to receive anymore.
                if (chunk.OldestRecoverableFrame > ulong.MinValue)
                    DisjointSet.Add(ref this.m_ReceivedFrameSequences, new Range(ulong.MinValue, chunk.OldestRecoverableFrame - 1));

                // Check the frame sequence number to see if any frames were skipped.
                // If so, then it is likely that the frame has been dropped, so it is
                // necessary to send a NACK.
                if (chunk.FrameSequence > this.m_GreatestFrameSequence + 1ul) {
                    //Debug.WriteLine(string.Format("Frames #{0}-{1} were dropped ({2} total)! Requesting replacements (oldest recoverable is #{3})...",
                    //    this.m_GreatestFrameSequence + 1ul, chunk.FrameSequence - 1ul, chunk.FrameSequence - this.m_GreatestFrameSequence - 1,
                    //    chunk.OldestRecoverableFrame), this.GetType().ToString());
                    if (this.Nack != null) {
                        // Send a NACK which requests rebroadcast of all the skipped frames.
                        Range range = new Range(
                            Math.Max(this.m_GreatestFrameSequence + 1ul, chunk.OldestRecoverableFrame),
                            chunk.FrameSequence - 1ul);
                        this.Nack(range);
                    }
                }

                // Otherwise, don't do anything special.  These two branches are for debugging.
                else if (chunk.FrameSequence == this.m_GreatestFrameSequence) {
                    // Duplicate chunk received, or else the first chunk ever received.
                } else if (chunk.FrameSequence < this.m_GreatestFrameSequence && chunk.FrameSequence > ulong.MinValue) {
                    Debug.WriteLine(string.Format("%%% Frame #{0} (message #{1}, chunk #{2} of {3}, {4} bytes) received out of order (off by {5})",
                        chunk.FrameSequence,
                        chunk.MessageSequence,
                        chunk.ChunkSequenceInMessage + 1,
                        chunk.NumberOfChunksInMessage,
                        chunk.Data.Length,
                        this.m_GreatestFrameSequence - chunk.FrameSequence),
                        this.GetType().ToString());
                }

                // Assuming the chunk has not been duplicated or received out of order,
                // update our variable containing the highest sequence number seen
                // so far so we know which future frames have been dropped.
                if (chunk.FrameSequence > this.m_GreatestFrameSequence)
                    this.m_GreatestFrameSequence = chunk.FrameSequence;
            }

            // If the message depends on a message that has not yet been received,
            // add it to the queue of chunks waiting for that message.
            // Then, STOP PROCESSING the message.
            // Assemble(chunk) will get called by FlushWaitingChunks after
            // the dependencies are satisfied.
            if (chunk.MessageDependency > ulong.MinValue && !DisjointSet.Contains(ref this.m_ReceivedMessages, chunk.MessageDependency)) {
                List<Chunk> waiters;
                if (!this.m_ChunksWaitingForDependencies.TryGetValue(chunk.MessageDependency, out waiters))
                    this.m_ChunksWaitingForDependencies[chunk.MessageDependency] = waiters = new List<Chunk>();
                Debug.WriteLine(string.Format("@@@ Unsatisfied dependency: message #{0} (chunk #{1} of {2}) depends on #{3}; {4} chunks are already waiting.",
                    chunk.MessageSequence,
                    chunk.ChunkSequenceInMessage + 1,
                    chunk.NumberOfChunksInMessage,
                    chunk.MessageDependency, waiters.Count));
                waiters.Add(chunk);
                yield break;
            }

            // Flush any dependencies of messages we can no longer expect to recover.
            // Do this by finding the difference between the range of all unrecoverable
            // message sequence numbers and the sequence numbers that have been received.
            // FlushWaitingChunks is called on each of the unsatisfiable sequence numbers.
            if (chunk.OldestRecoverableMessage > ulong.MinValue) {
                Range unrecoverable = new Range(ulong.MinValue, chunk.OldestRecoverableMessage - 1);
                DisjointSet unsatisfiable = unrecoverable;
                DisjointSet.Remove(ref unsatisfiable, this.m_ReceivedMessages);
                if(unsatisfiable != null)
                    foreach (ulong unsatisfied in unsatisfiable)
                        this.FlushWaitingChunks(unsatisfied);
                DisjointSet.Add(ref this.m_ReceivedMessages, unrecoverable);
            }

            // If the dependency is already satisfied (or not specified),
            // attempt to assemble the chunk into a completed message.
            foreach (object message in this.Assemble(chunk))
                yield return message;
        }
 public ChunksAndPriority(Chunk[] chunks, MessageTags tags)
 {
     this.Chunks = chunks;
     this.Tags = tags;
 }
예제 #10
0
            /// <summary>
            /// Adds a chunk to the message.
            /// The chunk must be part of the message;
            /// that is, <see cref="IsInRange">IsInRange(<paramref name="chunk"/>)</see>
            /// must return <c>true</c>.
            /// </summary>
            /// <param name="chunk">
            /// The chunk to add to the message.
            /// </param>
            public void Add(Chunk chunk)
            {
                ulong index = chunk.ChunkSequenceInMessage;
                ulong chunks = ((ulong)this.m_Data.LongLength);
                if (index < 0 || index >= chunks)
                    throw new ArgumentException("Chunk is not part of the message being assembled by this MessageAssembler.", "chunk");

                // If the chunk is not a duplicate, insert its contents into the data array.
                // (The chunked message cannot be deserialized until all chunks have been received.)
                if (this.m_Data[index] == null) {
                    Debug.Assert(this.m_Remaining >= 1);

                    // This chunk has not been received before.
                    this.m_Data[index] = chunk.Data;
                    this.m_Remaining--;

                    Debug.WriteLine(string.Format("Received message #{0}, chunk #{0} of {1} ({2} bytes); {3} chunks remaining.",
                        chunk.MessageSequence,
                        chunk.ChunkSequenceInMessage + 1,
                        chunk.NumberOfChunksInMessage,
                        chunk.Data.LongLength,
                        this.m_Remaining),
                        this.GetType().ToString());
                }
            }
예제 #11
0
 /// <summary>
 /// Gets whether the given chunk is part of the message
 /// being assembled by this <see cref="MessageAssembler"/>.
 /// </summary>
 /// <param name="chunk">
 /// The chunk to test.
 /// </param>
 /// <returns>
 /// Whether the chunk's sequence number is in range.
 /// </returns>
 public bool IsInRange(Chunk chunk)
 {
     return chunk.MessageSequence == this.m_MessageSequence;
 }
예제 #12
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)) {
                    yield return this.m_Formatter.Deserialize(ms);

                    // 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;
                }
            }
        }
예제 #13
0
 /// <summary>
 /// Writes a chunk to a data stream.
 /// </summary>
 public void EncodeChunk(Stream stream, Chunk chunk)
 {
     // FIXME: More efficient encoding of chunks might be possible here.
     //   Custom encoding would require a Decode method to be used by RTPMessageReceiver.
     #if GENERIC_SERIALIZATION
     chunk.Serialize().WriteToStream( stream );
     #else
     this.m_Formatter.Serialize( stream, chunk );
     #endif
 }
예제 #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);
                    }
                }
            }
        }
예제 #15
0
 public void Send(Chunk chunk)
 {
     using(MemoryStream ms = new MemoryStream()) {
         this.m_bf.Serialize(ms, chunk);
         this.m_Sender.Send(new BufferChunk(ms.GetBuffer(), 0, (int) ms.Length));
     }
 }
예제 #16
0
        /// <summary>
        /// Inserts the given chunk into the buffer.
        /// </summary>
        /// <remarks>
        /// <para>
        /// If the chunk's <see cref="Chunk.FrameSequence">frame sequence number</see> is
        /// higher than any previously inserted chunk, then <see cref="NewestRecoverableFrame"/>
        /// and <see cref="OldestRecoverableFrame"/> are updated to reflect the size of the buffer.
        /// Any chunks whose frame sequence numbers fall outside of that range are removed, and
        /// will no longer be recoverable under any circumnstances.
        /// </para>
        /// <para>
        /// On the other hand, if the chunk is older than <see cref="NewestRecoverableFrame"/>,
        /// then it is simply inserted into the buffer, unless it is also older than
        /// <see cref="OldestRecoverableFrame"/>, in which case it is discarded.
        /// </para>
        /// </remarks>
        /// <param name="value">The chunk to insert.</param>
        public void Insert(Chunk value)
        {
            if (value == null)
                throw new ArgumentNullException("value", "The data inserted into the buffer cannot be null.");

            // Compute the index into the buffer table where the new data will go.
            ulong index = value.FrameSequence % this.m_BufferSize;

            // If the chunk is not inserted in ascending order, then all we have to
            // do is insert it into the same index in the table as Take() would access.
            if (this.m_Newest >= value.FrameSequence && value.FrameSequence != 0) {
                if (this.m_Oldest <= value.FrameSequence && value.FrameSequence <= this.m_Newest)
                    this.Set(index, value);
                return;
            }

            // Store the new chunk in the buffer.
            this.Set(index, value);

            // We must special-case the first time a chunk is buffered.
            if (this.m_First == ulong.MaxValue) {
                this.m_Oldest = this.m_First = this.m_Newest = value.FrameSequence;
            } else {
                // If the frame sequences are not sequential, we need to clean up invalid entries
                // that were not already overwritten.  We use "++expired" to skip the most recent
                // valid entry.  The "failsafe" ensures that no more than m_BufferSize iterations
                // are made (more wouldn't hurt but would be wasteful).  The index is hashed into
                // the table each time in case it wraps around to the beginning.
                for (ulong failsafe = 0, expired = this.m_Newest; ++expired < value.FrameSequence && failsafe++ < this.m_BufferSize; )
                    this.Set(expired % this.m_BufferSize, null);

                // Regardless, we need to set the new m_Newest.
                Debug.Assert(value.FrameSequence > this.m_Newest);
                this.m_Newest = value.FrameSequence;

                // We need to find the new oldest entry.  Until the first m_BufferSize entries
                // are added, the first entry cannot have been overwritten and it is the oldest.
                Debug.Assert(value.FrameSequence > this.m_First);
                if (value.FrameSequence - this.m_First < this.m_BufferSize) {
                    this.m_Oldest = this.m_First;
                }

                    // Otherwise the oldest entry must increase.  If sequence numbers were skipped,
                    // it may increase by more than one if additional entries are null.
                    // The loop must terminate because the new, non-null entry was set above.
                else
                    while (this.m_Buffer[++this.m_Oldest % this.m_BufferSize] == null) ;
            }
        }
예제 #17
0
 public void Send(Chunk chunk)
 {
     this.Post(this.m_SendChunkDelegate, new object[] { chunk });
 }
예제 #18
0
 public NetworkChunkEvent(Chunk chunk, long timeIndex, string source)
     : base(timeIndex, source)
 {
     this.chunk = chunk;
 }