int MulticastClientTransport::send(
        ClientTransportCallback &clientStub,
        const std::vector<ByteBuffer> &data,
        unsigned int timeoutMs)
    {
        // TODO: in some cases, may need to make a full copy of data for 
        // each individual sub-transport, as they might transform the data.

        bringInNewTransports();

        Lock lock(mClientTransportsMutex);

        // TODO: hardcoded
        timeoutMs = 1000;
        bool needToRemove = false;

        ClientTransportList::iterator iter;
        for (
            iter = mClientTransports.begin();
            iter != mClientTransports.end();
            ++iter)
        {
            try
            {
                if ((**iter)->isInProcess())
                {
                    ClientStub & stub = static_cast<ClientStub &>(clientStub);
                    (**iter)->doInProcessCall(stub);
                }
                else
                {
                    // We used to check *iter->isConnected() here, and if so not
                    // try the send at all. Appear to have been synchronization 
                    // issues when doing that though.

                    // Sending synchronously, so no use for the callback
                    DummyCallback dummyCallback;
                    (**iter)->send(dummyCallback, data, timeoutMs);
                }
            }
            catch(const Exception &e)
            {
                RCF_LOG_1()(e) << "Error publishing to subscriber.";
                needToRemove = true;
                iter->reset();
            }
        }

        if (needToRemove)
        {
            mClientTransports.remove( ClientTransportAutoPtrPtr() );
        }       

        clientStub.onSendCompleted();

        return 1;
    }
 void MulticastClientTransport::connect(ClientTransportCallback &clientStub, unsigned int timeoutMs)
 {
     RCF_UNUSED_VARIABLE(clientStub);
     RCF_UNUSED_VARIABLE(timeoutMs);
     clientStub.onConnectCompleted(true);
 }
    int MulticastClientTransport::send(
        ClientTransportCallback &           clientStub,
        const std::vector<ByteBuffer> &     data,
        unsigned int                        timeoutMs)
    {
        // NB: As the same buffer is sent on all transports, the transports and
        // filters should never modify the buffer. Any transport that transforms
        // data needs to do so in a separate per-transport buffer.

        RCF_UNUSED_VARIABLE(timeoutMs);

        RCF_LOG_2()(lengthByteBuffers(data))(timeoutMs) 
            << "MulticastClientTransport::send() - entry.";

        mLastRequestSize = lengthByteBuffers(data);
        mRunningTotalBytesSent += mLastRequestSize;

        bringInNewTransports();

        Lock lock(mClientTransportsMutex);

        std::size_t transportsInitial = mClientTransports.size();

        PublishCompletionInfo info( mClientTransports.size() );

        // Setup completion handlers.
        std::vector<PublishCompletionHandler> handlers( mClientTransports.size() );
        for (std::size_t i=0; i<mClientTransports.size(); ++i)
        {
            ClientTransport * pTransport = (*mClientTransports[i]).get();
            handlers[i] = PublishCompletionHandler(pTransport, &info);
        }

        // Async send on all transports.
        for (std::size_t i=0; i<handlers.size(); ++i)
        {
            try
            {

                handlers[i].mpClientTransport->setAsync(true);
                handlers[i].mpClientTransport->send(handlers[i], data, 0);
            }
            catch(const Exception &e)
            {
                Exception err( _RcfError_SyncPublishError(e.what()) );
                handlers[i].onError(err);
            }
        }

        // Wait for async completions.
        boost::uint32_t completionDurationMs = 0;
        {
            Timer timer;
            info.wait(timeoutMs);
            completionDurationMs = timer.getDurationMs();
        }

        // Cancel any outstanding sends.
        for (std::size_t i=0; i<handlers.size(); ++i)
        {
            if (!handlers[i].mCompleted)
            {
                (*mClientTransports[i])->cancel();

                RCF_LOG_2()(i)
                    << "MulticastClientTransport::send() - cancel send.";
            }
        }

        // Wait for canceled ops to complete.
        boost::uint32_t cancelDurationMs = 0;
        {
            Timer timer;
            info.wait(timeoutMs);
            cancelDurationMs = timer.getDurationMs();
        }

        RCF_ASSERT(info.getCompletionCount() == handlers.size());

        // Close and remove any subscriber transports with errors.
        std::size_t transportsRemoved = 0;
        for (std::size_t i=0; i<handlers.size(); ++i)
        {
            RCF_ASSERT(handlers[i].mCompleted);
            if (!handlers[i].mOk)
            {
                mClientTransports[i] = ClientTransportAutoPtrPtr();
                ++transportsRemoved;

                RCF_LOG_2()(i)(handlers[i].mCompleted)(handlers[i].mOk)(handlers[i].mError) 
                    << "MulticastClientTransport::send() - remove subscriber transport.";
            }
        }
        eraseRemove(mClientTransports, ClientTransportAutoPtrPtr());

        clientStub.onSendCompleted();

        std::size_t transportsFinal = transportsInitial - transportsRemoved;

        RCF_LOG_2()
            (lengthByteBuffers(data))(completionDurationMs)(cancelDurationMs)(transportsInitial)(transportsFinal)
            << "MulticastClientTransport::send() - exit.";

        return 1;
    }