Beispiel #1
0
void MessageConnection::SplitAndQueueMessage(NetworkMessage *message, bool internalQueue, size_t maxFragmentSize)
{
    using namespace std;

#ifdef KNET_THREAD_CHECKING_ENABLED
    if (internalQueue)
        AssertInWorkerThreadContext();
    else
        AssertInMainThreadContext();
#endif

    assert(message);
    assert(!message->obsolete);

    // We need this many fragments to represent the whole message.
    const size_t totalNumFragments = (message->dataSize + maxFragmentSize - 1) / maxFragmentSize;
    assert(totalNumFragments > 1); // Shouldn't be calling this function if the message can well fit into one fragment.

    LOG(LogVerbose, "Splitting a message of %db into %d fragments of %db size at most.",
        (int)message->dataSize, (int)totalNumFragments, (int)maxFragmentSize);

    /** \todo Would like to do this:
    	FragmentedSendManager::FragmentedTransfer *transfer;
    	{
    		Lock<FragmentedSendManager> sends = fragmentedSends.Acquire();
    		transfer = sends->AllocateNewFragmentedTransfer();
    	}
    */
    // But instead, have to resort to function-wide lock.
    Lock<FragmentedSendManager> sends = fragmentedSends.Acquire();
    FragmentedSendManager::FragmentedTransfer *transfer = sends->AllocateNewFragmentedTransfer();

    size_t currentFragmentIndex = 0;
    size_t byteOffset = 0;

    assert(transfer != 0);
    transfer->totalNumFragments = totalNumFragments;

    if (!message->reliable)
    {
        LOG(LogVerbose, "Upgraded a nonreliable message with ID %d and size %d to a reliable message since it had to be fragmented!", (int)message->id, (int)message->dataSize);
    }

    if (message->contentID != 0)
    {
        LOG(LogVerbose, "Warning: Content IDs are not supported with fragmented transfers. Removing the content ID %d of message %d of size %d.",
            (int)message->contentID, (int)message->id, (int)message->Size());
        message->contentID = 0;
    }

    // Split the message into fragments.
    while(byteOffset < message->dataSize)
    {
        const size_t thisFragmentSize = min(maxFragmentSize, message->dataSize - byteOffset);

        NetworkMessage *fragment = StartNewMessage(message->id, thisFragmentSize);
        fragment->contentID = message->contentID;
        fragment->inOrder = message->inOrder;
        fragment->reliable = true; // We don't send fragmented messages as unreliable messages - the risk of a fragment getting lost wastes bandwidth.
        fragment->messageNumber = outboundMessageNumberCounter++; ///\todo Convert to atomic increment, or this is a race condition.
        fragment->priority = message->priority;
        fragment->sendCount = 0;

        fragment->transfer = transfer;
        fragment->fragmentIndex = currentFragmentIndex++;
        fragment->reliableMessageNumber = outboundReliableMessageNumberCounter++; ///\todo Convert to atomic increment, or this is a race condition.
#ifdef KNET_NETWORK_PROFILING
        fragment->profilerName = message->profilerName + "_Fragment";
#endif

        // Copy the data from the old message that's supposed to go into this fragment.
        memcpy(fragment->data, message->data + byteOffset, thisFragmentSize);
        byteOffset += thisFragmentSize;

        transfer->AddMessage(fragment);

        if (internalQueue) // if true, we are accessing from the worker thread, and can directly access the outboundQueue member.
        {
//			assert(ContainerUniqueAndNoNullElements(outboundQueue));
#ifdef KNET_NO_MAXHEAP
            outboundQueue.InsertWithResize(fragment);
#else
            outboundQueue.Insert(fragment);
#endif
//			assert(ContainerUniqueAndNoNullElements(outboundQueue));
        }
        else
        {
            if (!outboundAcceptQueue.Insert(fragment))
            {
                ///\todo Is it possible to check beforehand if this criteria is avoided, or if we are doomed?
                LOG(LogError, "Critical: Failed to add message fragment to outboundAcceptQueue! Queue was full. Do not know how to recover here!");
                assert(false);
            }
        }
    }

    // Signal the worker thread that there are new outbound events available.
    if (!bOutboundSendsPaused)
        eventMsgsOutAvailable.Set();

    // The original message that was split into fragments is no longer needed - it is represented by the newly created fragments
    // that have now been queued.
    FreeMessage(message);
}