DirectX'te yerel yer işareti aktarımları

Azure Spatial Anchors'ı kullanamadığınız durumlarda, yerel yer işareti aktarımları bir HoloLens cihazının ikinci bir HoloLens cihazı tarafından içeri aktarılacak bir yer işareti dışarı aktarmasını sağlar.

Not

Yerel yer işareti aktarımları Azure Spatial Anchors'a göre daha az sağlam yer işareti geri çağırma sağlar ve iOS ve Android cihazlar bu yaklaşım tarafından desteklenmez.

Not

Bu makaledeki kod parçacıkları şu anda C++ holografik proje şablonunda kullanılan C++17 uyumlu C++/WinRT yerine C++/CX kullanımını göstermektedir. Kavramlar C++/WinRT projesi için eşdeğerdir, ancak kodu çevirmeniz gerekir.

Uzamsal çapaları aktarma

SpatialAnchorTransferManager'ı kullanarak uzamsal yer işaretleri Windows Mixed Reality cihazlar arasında aktarabilirsiniz. Bu API, dünyada tam olarak bu yeri bulmak için gereken tüm destekleyici algılayıcı verileriyle bir yer işareti paketlemenize ve ardından bu paketi başka bir cihaza içeri aktarmanıza olanak tanır. İkinci cihazdaki uygulama bu tutturucuyu içeri aktardıktan sonra, her uygulama bu paylaşılan uzamsal tutturucunun koordinat sistemini kullanarak hologramları işleyebilir ve bu da gerçek dünyada aynı yerde görünür.

Uzamsal tutturucuların farklı cihaz türleri arasında aktarılamadığını unutmayın; örneğin HoloLens uzamsal yer işareti, çevreleyici bir kulaklık kullanılarak locatable olmayabilir. Aktarılan tutturucular iOS veya Android cihazlarla da uyumlu değildir.

Uygulamanızı spatialPerception özelliğini kullanacak şekilde ayarlama

Uygulamanıza SpatialAnchorTransferManager'ı kullanabilmesi için önce SpatialPerception özelliğini kullanma izni verilmelidir. Uzamsal çapa aktarımı, hassas bilgiler içerebilecek, bu çapanın çevresinde zaman içinde toplanan algılayıcı görüntülerinin paylaşılmasını içerdiğinden bu gereklidir.

Uygulamanızın package.appxmanifest dosyasında bu özelliği bildirin. Aşağıda bir örnek verilmiştir:

<Capabilities>
  <uap2:Capability Name="spatialPerception" />
</Capabilities>

Bu özellik uap2 ad alanından gelir. Bildiriminizde bu ad alanına erişmek için package> öğesine xlmns özniteliği <olarak ekleyin. Aşağıda bir örnek verilmiştir:

<Package
    xmlns="https://schemas.microsoft.com/appx/manifest/foundation/windows10"
    xmlns:mp="https://schemas.microsoft.com/appx/2014/phone/manifest"
    xmlns:uap="https://schemas.microsoft.com/appx/manifest/uap/windows10"
    xmlns:uap2="https://schemas.microsoft.com/appx/manifest/uap/windows10/2"
    IgnorableNamespaces="uap mp"
    >

NOT: Uygulamanızın SpatialAnchor dışarı/içeri aktarma API'lerine erişebilmesi için bu özelliği çalışma zamanında istemesi gerekir. Aşağıdaki örneklerde RequestAccessAsync bölümüne bakın.

SpatialAnchorTransferManager ile dışarı aktararak yer işareti verilerini seri hale getirme

SpatialAnchor verilerini dışarı aktarmak (serileştirmek) için kod örneğine bir yardımcı işlevi eklenir. Bu dışarı aktarma API'si, dizeleri tutturucularla ilişkilendiren anahtar-değer çiftleri koleksiyonundaki tüm tutturucuları serileştirir.

// ExportAnchorDataAsync: Exports a byte buffer containing all of the anchors in the given collection.
//
// This function will place data in a buffer using a std::vector<byte>. The ata buffer contains one or more
// Anchors if one or more Anchors were successfully imported; otherwise, it is ot modified.
//
task<bool> SpatialAnchorImportExportHelper::ExportAnchorDataAsync(
    vector<byte>* anchorByteDataOut,
    IMap<String^, SpatialAnchor^>^ anchorsToExport
    )
{

İlk olarak veri akışını ayarlamamız gerekir. Bu, 1'e kadar izin verir.) TryExportAnchorsAsync kullanarak verileri uygulamaya ait bir arabelleğe yerleştirin ve 2.) bir WinRT veri akışı olan dışarı aktarılan bayt arabellek akışından verileri std::vector<bayt> olan kendi bellek arabelleğimize okuma.

// Create a random access stream to process the anchor byte data.
InMemoryRandomAccessStream^ stream = ref new InMemoryRandomAccessStream();
// Get an output stream for the anchor byte stream.
IOutputStream^ outputStream = stream->GetOutputStreamAt(0);

Sistem tarafından dışarı aktarılan yer işaretleri de dahil olmak üzere uzamsal verilere erişmek için izin istememiz gerekir.

// Request access to spatial data.
auto accessRequestedTask = create_taskSpatialAnchorTransferManager::RequestAccessAsync()).then([anchorsToExport, utputStream](SpatialPerceptionAccessStatus status)
{
    if (status == SpatialPerceptionAccessStatus::Allowed)
    {
        // Access is allowed.
        // Export the indicated set of anchors.
        return create_task(SpatialAnchorTransferManager::TryExportAnchorsAsync(
            anchorsToExport,
            outputStream
            ));
    }
    else
    {
        // Access is denied.
        return task_from_result<bool>(false);
    }
});

İzin alırsak ve yer işaretleri dışarı aktarılırsa veri akışını okuyabiliriz. Burada ayrıca verileri okumak için kullanacağımız DataReader ve InputStream'in nasıl oluşturulacağını da göstereceğiz.

// Get the input stream for the anchor byte stream.
IInputStream^ inputStream = stream->GetInputStreamAt(0);
// Create a DataReader, to get bytes from the anchor byte stream.
DataReader^ reader = ref new DataReader(inputStream);
return accessRequestedTask.then([anchorByteDataOut, stream, reader](bool nchorsExported)
{
    if (anchorsExported)
    {
        // Get the size of the exported anchor byte stream.
        size_t bufferSize = static_cast<size_t>(stream->Size);
        // Resize the output buffer to accept the data from the stream.
        anchorByteDataOut->reserve(bufferSize);
        anchorByteDataOut->resize(bufferSize);
        // Read the exported anchor store into the stream.
        return create_task(reader->LoadAsync(bufferSize));
    }
    else
    {
        return task_from_result<size_t>(0);
    }

Akıştan baytları okuduktan sonra bunları kendi veri arabelleğimize kaydedebiliriz.

}).then([anchorByteDataOut, reader](size_t bytesRead)
{
    if (bytesRead > 0)
    {
        // Read the bytes from the stream, into our data output buffer.
        reader->ReadBytes(Platform::ArrayReference<byte>(&(*anchorByteDataOut)[0], bytesRead));
        return true;
    }
    else
    {
        return false;
    }
});
};

SpatialAnchorTransferManager kullanarak sabitleyici verilerini sisteme aktararak seri durumdan çıkarma

Daha önce dışarı aktarılan verileri yüklemek için kod örneğine bir yardımcı işlevi eklenir. Bu seri durumdan çıkarma işlevi, SpatialAnchorStore'nun sağladığına benzer bir anahtar-değer çifti koleksiyonu sağlar; ancak bu verileri ağ yuvası gibi başka bir kaynaktan aldık. Bu verileri çevrimdışı depolamadan önce, uygulama içi bellek kullanarak veya (varsa) uygulamanızın SpatialAnchorStore'sini kullanarak işleyebilir ve bu veriler hakkında neden oluşturabilirsiniz.

// ImportAnchorDataAsync: Imports anchors from a byte buffer that was previously exported.
//
// This function will import all anchors from a data buffer into an in-memory ollection of key, value
// pairs that maps String objects to SpatialAnchor objects. The Spatial nchorStore is not affected by
// this function unless you provide it as the target collection for import.
//
task<bool> SpatialAnchorImportExportHelper::ImportAnchorDataAsync(
    std::vector<byte>& anchorByteDataIn,
    IMap<String^, SpatialAnchor^>^ anchorMapOut
    )
{

İlk olarak, yer işareti verilerine erişmek için akış nesneleri oluşturmamız gerekir. Verileri arabelleğimizden bir sistem arabelleğine yazacağımız için, bayt arabelleğinden sisteme SpatialAnchors olarak yer işaretleri alma hedefimizi gerçekleştirmek için bellek içi veri akışına yazan bir DataWriter oluşturacağız.

// Create a random access stream for the anchor data.
InMemoryRandomAccessStream^ stream = ref new InMemoryRandomAccessStream();
// Get an output stream for the anchor data.
IOutputStream^ outputStream = stream->GetOutputStreamAt(0);
// Create a writer, to put the bytes in the stream.
DataWriter^ writer = ref new DataWriter(outputStream);

Bir kez daha uygulamanın uzamsal yer işareti verilerini dışarı aktarma izni olduğundan emin olmamız gerekir. Bu izin kullanıcının ortamı hakkında özel bilgiler içerebilir.

// Request access to transfer spatial anchors.
return create_task(SpatialAnchorTransferManager::RequestAccessAsync()).then(
    [&anchorByteDataIn, writer](SpatialPerceptionAccessStatus status)
{
    if (status == SpatialPerceptionAccessStatus::Allowed)
    {
        // Access is allowed.

Erişime izin veriliyorsa arabellekten sistem veri akışına bayt yazabiliriz.

// Write the bytes to the stream.
        byte* anchorDataFirst = &anchorByteDataIn[0];
        size_t anchorDataSize = anchorByteDataIn.size();
        writer->WriteBytes(Platform::ArrayReference<byte>(anchorDataFirst, anchorDataSize));
        // Store the stream.
        return create_task(writer->StoreAsync());
    }
    else
    {
        // Access is denied.
        return task_from_result<size_t>(0);
    }

Veri akışında bayt depolamada başarılı olursak SpatialAnchorTransferManager kullanarak bu verileri içeri aktarmayı deneyebiliriz.

}).then([writer, stream](unsigned int bytesWritten)
{
    if (bytesWritten > 0)
    {
        // Try to import anchors from the byte stream.
        return create_task(writer->FlushAsync())
            .then([stream](bool dataWasFlushed)
        {
            if (dataWasFlushed)
            {
                // Get the input stream for the anchor data.
                IInputStream^ inputStream = stream->GetInputStreamAt(0);
                return create_task(SpatialAnchorTransferManager::TryImportAnchorsAsync(inputStream));
            }
            else
            {
                return task_from_result<IMapView<String^, SpatialAnchor^>^>(nullptr);
            }
        });
    }
    else
    {
        return task_from_result<IMapView<String^, SpatialAnchor^>^>(nullptr);
    }

Veriler içeri aktarılabiliyorsa, dizeleri tutturucularla ilişkilendiren anahtar-değer çiftlerinin harita görünümünü elde ederiz. Bunu kendi bellek içi veri koleksiyonumuza yükleyebilir ve kullanmak istediğimiz yer işaretleri için bu koleksiyonu kullanabiliriz.

}).then([anchorMapOut](task<Windows::Foundation::Collections::IMapView<String^, SpatialAnchor^>^>  previousTask)
{
    try
    {
        auto importedAnchorsMap = previousTask.get();
        // If the operation was successful, we get a set of imported anchors.
        if (importedAnchorsMap != nullptr)
        {
            for each (auto& pair in importedAnchorsMap)
            {
                // Note that you could look for specific anchors here, if you know their key values.
                auto const& id = pair->Key;
                auto const& anchor = pair->Value;
                // Append "Remote" to the end of the anchor name for disambiguation.
                std::wstring idRemote(id->Data());
                idRemote += L"Remote";
                String^ idRemoteConst = ref new String (idRemote.c_str());
                // Store the anchor in the current in-memory anchor map.
                anchorMapOut->Insert(idRemoteConst, anchor);
            }
            return true;
        }
    }
    catch (Exception^ exception)
    {
        OutputDebugString(L"Error: Unable to import the anchor data buffer bytes into the in-memory anchor collection.\n");
    }
    return false;
});
}

NOT: Bir tutturucu içeri aktarabilmeniz, hemen kullanabileceğiniz anlamına gelmez. Yer işareti farklı bir odada veya tamamen başka bir fiziksel konumda olabilir; yer işareti, alan cihaz, tutturucunun bilinen geçerli ortama göre konumunu geri yüklemek için tutturucunun oluşturulduğu ortam hakkında yeterli görsel bilgiye sahip olana kadar locatable olmaz. İstemci uygulaması, canlı içerik için kullanmaya devam etmeden önce yerel koordinat sisteminize veya başvuru çerçevenize göre tutturucuyu bulmaya çalışmalıdır. Örneğin, tutturucu locatable olmaya başlayana kadar, sabit noktasını geçerli koordinat sistemine göre düzenli aralıklarla bulmayı deneyin.

Dikkat Edilmesi Gereken Özel Noktalar

TryExportAnchorsAsync API'si, birden çok SpatialAnchor'ın aynı opak ikili bloba dışarı aktarılmasını sağlar. Ancak, tek bir SpatialAnchor veya birden çok SpatialAnchor'ın tek bir çağrıda dışarı aktarılıp aktarılmadığına bağlı olarak blob'un içereceği verilerde küçük bir fark vardır.

Tek bir SpatialAnchor dışarı aktarma

Blob, SpatialAnchor'un yakınında ortamın bir gösterimini içerir, böylece ortam SpatialAnchor'u içeri aktaran cihazda tanınabilir. İçeri aktarma işlemi tamamlandıktan sonra yeni SpatialAnchor cihaz tarafından kullanılabilir hale gelecek. Kullanıcının yakın zamanda çapanın yakınında olduğunu varsayarsak, locatable olur ve SpatialAnchor'a bağlı hologramlar işlenebilir. Bu hologramlar SpatialAnchor'u dışarı aktaran orijinal cihazda yaptıkları fiziksel konumda gösterilir.

Tek bir SpatialAnchor dışarı aktarma

Birden çok SpatialAnchor dışarı aktarma

Tek bir SpatialAnchor'un dışarı aktarımı gibi blob da belirtilen tüm SpatialAnchor'ların yakınında ortamın bir gösterimini içerir. Ayrıca blob, dahil edilen SpatialAnchor'lar arasındaki bağlantılar (aynı fiziksel alanda yer alıyorsa) hakkında bilgi içerir. Bu, yakındaki iki SpatialAnchor içeri aktarılırsa ikinci SpatialAnchor'a bağlı bir hologramın, cihaz yalnızca ilk SpatialAnchor etrafındaki ortamı tanısa bile locatable olacağı anlamına gelir, çünkü bloba iki SpatialAnchor arasındaki dönüşümü hesaplamak için yeterli veri dahil edildi. İki SpatialAnchor ayrı ayrı dışarı aktarıldıysa (TryExportSpatialAnchors'a iki ayrı çağrı) bloba, ikinci SpatialAnchor'a bağlı hologramların ilki bulunduğunda locatable olması için yeterli veri bulunmayabilir.

Tek bir TryExportAnchorsAsync çağrısı kullanılarak dışarı aktarılan birdençok yer işareti Her yer işareti için ayrı bir TryExportAnchorsAsync çağrısı kullanılarak dışarı aktarılan birden çok yer işareti

Örnek: Windows::Networking::StreamSocket kullanarak bağlayıcı verileri gönderme

Burada dışarı aktarılan yer işareti verilerini bir TCP ağı üzerinden göndererek nasıl kullanacağınıza yönelik bir örnek sağlıyoruz. Bu HolographicSpatialAnchorTransferSample'dan alınıyor.

WinRT StreamSocket sınıfı PPL görev kitaplığını kullanır. Ağ hataları söz konusu olduğunda, yeniden oluşan bir özel durum kullanılarak hata zincirdeki bir sonraki göreve döndürülür. Özel durum, hata durumunu belirten bir HRESULT içerir.

Dışarı aktarılan yer işareti verilerini göndermek için TCP ile Windows::Networking::StreamSocketListener kullanma

Bağlantı dinleyen bir sunucu örneği oluşturun.

void SampleAnchorTcpServer::ListenForConnection()
{
    // Make a local copy to avoid races with Closed events.
    StreamSocketListener^ streamSocketListener = m_socketServer;
    if (streamSocketListener == nullptr)
    {
        OutputDebugString(L"Server listening for client.\n");
        // Create the web socket connection.
        streamSocketListener = ref new StreamSocketListener();
        streamSocketListener->Control->KeepAlive = true;
        streamSocketListener->BindEndpointAsync(
            SampleAnchorTcpCommon::m_serverHost,
            SampleAnchorTcpCommon::m_tcpPort
            );
        streamSocketListener->ConnectionReceived +=
            ref new Windows::Foundation::TypedEventHandler<StreamSocketListener^, StreamSocketListenerConnectionReceivedEventArgs^>(
                std::bind(&SampleAnchorTcpServer::OnConnectionReceived, this, _1, _2)
                );
        m_socketServer = streamSocketListener;
    }
    else
    {
        OutputDebugString(L"Error: Stream socket listener not created.\n");
    }
}

Bağlantı alındığında, bağlantı noktası verilerini göndermek için istemci yuva bağlantısını kullanın.

void SampleAnchorTcpServer::OnConnectionReceived(StreamSocketListener^ listener, StreamSocketListenerConnectionReceivedEventArgs^ args)
{
    m_socketForClient = args->Socket;
    if (m_socketForClient != nullptr)
    {
        // In this example, when the client first connects, we catch it up to the current state of our anchor set.
        OutputToClientSocket(m_spatialAnchorHelper->GetAnchorMap());
    }
}

Artık dışarı aktarılan yer işareti verilerini içeren bir veri akışı göndermeye başlayabiliriz.

void SampleAnchorTcpServer::OutputToClientSocket(IMap<String^, SpatialAnchor^>^ anchorsToSend)
{
    m_anchorTcpSocketStreamWriter = ref new DataWriter(m_socketForClient->OutputStream);
    OutputDebugString(L"Sending stream to client.\n");
    SendAnchorDataStream(anchorsToSend).then([this](task<bool> previousTask)
    {
        try
        {
            bool success = previousTask.get();
            if (success)
            {
                OutputDebugString(L"Anchor data sent!\n");
            }
            else
            {
                OutputDebugString(L"Error: Anchor data not sent.\n");
            }
        }
        catch (Exception^ exception)
        {
            HandleException(exception);
            OutputDebugString(L"Error: Anchor data was not sent.\n");
        }
    });
}

Akışın kendisini göndermeden önce bir üst bilgi paketi göndermemiz gerekir. Bu üst bilgi paketi sabit uzunlukta olmalı ve sabit veri akışı olan değişken bayt dizisinin uzunluğunu da belirtmelidir; bu örnekte gönderilecek başka üst bilgi verimiz olmadığından üst bilgimiz 4 bayt uzunluğundadır ve 32 bit işaretsiz bir tamsayı içerir.

Concurrency::task<bool> SampleAnchorTcpServer::SendAnchorDataLengthMessage(size_t dataStreamLength)
{
    unsigned int arrayLength = dataStreamLength;
    byte* data = reinterpret_cast<byte*>(&arrayLength);
    m_anchorTcpSocketStreamWriter->WriteBytes(Platform::ArrayReference<byte>(data, SampleAnchorTcpCommon::c_streamHeaderByteArrayLength));
    return create_task(m_anchorTcpSocketStreamWriter->StoreAsync()).then([this](unsigned int bytesStored)
    {
        if (bytesStored > 0)
        {
            OutputDebugString(L"Anchor data length stored in stream; Flushing stream.\n");
            return create_task(m_anchorTcpSocketStreamWriter->FlushAsync());
        }
        else
        {
            OutputDebugString(L"Error: Anchor data length not stored in stream.\n");
            return task_from_result<bool>(false);
        }
    });
}
Concurrency::task<bool> SampleAnchorTcpServer::SendAnchorDataStreamIMap<String^, SpatialAnchor^>^ anchorsToSend)
{
    return SpatialAnchorImportExportHelper::ExportAnchorDataAsync(
        &m_exportedAnchorStoreBytes,
        anchorsToSend
        ).then([this](bool anchorDataExported)
    {
        if (anchorDataExported)
        {
            const size_t arrayLength = m_exportedAnchorStoreBytes.size();
            if (arrayLength > 0)
            {
                OutputDebugString(L"Anchor data was exported; sending data stream length message.\n");
                return SendAnchorDataLengthMessage(arrayLength);
            }
        }
        OutputDebugString(L"Error: Anchor data was not exported.\n");
        // No data to send.
        return task_from_result<bool>(false);

Akış uzunluğu bayt cinsinden istemciye gönderildikten sonra veri akışının kendisini yuva akışına yazmaya devam edebiliriz. Bu, yer işareti deposu baytlarının istemciye gönderilmesine neden olur.

}).then([this](bool dataLengthSent)
    {
        if (dataLengthSent)
        {
            OutputDebugString(L"Data stream length message sent; writing exported anchor store bytes to stream.\n");
            m_anchorTcpSocketStreamWriter->WriteBytes(Platform::ArrayReference<byte>(&m_exportedAnchorStoreBytes[0], m_exportedAnchorStoreBytes.size()));
            return create_task(m_anchorTcpSocketStreamWriter->StoreAsync());
        }
        else
        {
            OutputDebugString(L"Error: Data stream length message not sent.\n");
            return task_from_result<size_t>(0);
        }
    }).then([this](unsigned int bytesStored)
    {
        if (bytesStored > 0)
        {
            PrintWstringToDebugConsole(
                std::to_wstring(bytesStored) +
                L" bytes of anchor data written and stored to stream; flushing stream.\n"
                );
        }
        else
        {
            OutputDebugString(L"Error: No anchor data bytes were written to the stream.\n");
        }
        return task_from_result<bool>(false);
    });
}

Bu konuda daha önce belirtildiği gibi, ağ hata durumu iletilerini içeren özel durumları işlemeye hazırlıklı olmamız gerekir. Beklenmeyen hatalar için hata ayıklama konsoluna özel durum bilgilerini yazabiliriz. Bu, kod örneğimiz bağlantıyı tamamlayamazsa veya yer işareti verilerini göndermeyi bitiremezse ne olduğuna ilişkin bir ipucu verir.

void SampleAnchorTcpServer::HandleException(Exception^ exception)
{
    PrintWstringToDebugConsole(
        std::wstring(L"Connection error: ") +
        exception->ToString()->Data() +
        L"\n"
        );
}

Dışarı aktarılan yer işareti verilerini almak için TCP ile Windows::Networking::StreamSocket kullanma

İlk olarak sunucuya bağlanmalıyız. Bu kod örneği, StreamSocket oluşturma ve yapılandırmayı ve yuva bağlantısını kullanarak ağ verilerini almak için kullanabileceğiniz bir DataReader oluşturmayı gösterir.

NOT: Bu örnek kodu çalıştırırsanız, istemciyi başlatmadan önce sunucuyu yapılandırdığınızdan ve başlattığınızdan emin olun.

task<bool> SampleAnchorTcpClient::ConnectToServer()
{
    // Make a local copy to avoid races with Closed events.
    StreamSocket^ streamSocket = m_socketClient;
    // Have we connected yet?
    if (m_socketClient == nullptr)
    {
        OutputDebugString(L"Client is attempting to connect to server.\n");
        EndpointPair^ endpointPair = ref new EndpointPair(
            SampleAnchorTcpCommon::m_clientHost,
            SampleAnchorTcpCommon::m_tcpPort,
            SampleAnchorTcpCommon::m_serverHost,
            SampleAnchorTcpCommon::m_tcpPort
            );
        // Create the web socket connection.
        m_socketClient = ref new StreamSocket();
        // The client connects to the server.
        return create_task(m_socketClient->ConnectAsync(endpointPair, SocketProtectionLevel::PlainSocket)).then([this](task<void> previousTask)
        {
            try
            {
                // Try getting all exceptions from the continuation chain above this point.
                previousTask.get();
                m_anchorTcpSocketStreamReader = ref new DataReader(m_socketClient->InputStream);
                OutputDebugString(L"Client connected!\n");
                m_anchorTcpSocketStreamReader->InputStreamOptions = InputStreamOptions::ReadAhead;
                WaitForAnchorDataStream();
                return true;
            }
            catch (Exception^ exception)
            {
                if (exception->HResult == 0x80072741)
                {
                    // This code sample includes a very simple implementation of client/server
                    // endpoint detection: if the current instance tries to connect to itself,
                    // it is determined to be the server.
                    OutputDebugString(L"Starting up the server instance.\n");
                    // When we return false, we'll start up the server instead.
                    return false;
                }
                else if ((exception->HResult == 0x8007274c) || // connection timed out
                    (exception->HResult == 0x80072740)) // connection maxed at server end
                {
                    // If the connection timed out, try again.
                    ConnectToServer();
                }
                else if (exception->HResult == 0x80072741)
                {
                    // No connection is possible.
                }
                HandleException(exception);
                return true;
            }
        });
    }
    else
    {
        OutputDebugString(L"A StreamSocket connection to a server already exists.\n");
        return task_from_result<bool>(true);
    }
}

Bağlantı kurulduktan sonra sunucunun veri göndermesini bekleyebiliriz. Bunu, akış veri okuyucusunun LoadAsync öğesini çağırarak yaparız.

Aldığımız ilk bayt kümesi her zaman, önceki bölümde açıklandığı gibi yer işareti veri akışı bayt uzunluğunu gösteren üst bilgi paketi olmalıdır.

void SampleAnchorTcpClient::WaitForAnchorDataStream()
{
    if (m_anchorTcpSocketStreamReader == nullptr)
    {
        // We have not connected yet.
        return;
    }
    OutputDebugString(L"Waiting for server message.\n");
    // Wait for the first message, which specifies the byte length of the string data.
    create_task(m_anchorTcpSocketStreamReader->LoadAsync(SampleAnchorTcpCommon::c_streamHeaderByteArrayLength)).then([this](unsigned int numberOfBytes)
    {
        if (numberOfBytes > 0)
        {
            OutputDebugString(L"Server message incoming.\n");
            return ReceiveAnchorDataLengthMessage();
        }
        else
        {
            OutputDebugString(L"0-byte async task received, awaiting server message again.\n");
            WaitForAnchorDataStream();
            return task_from_result<size_t>(0);
        }

...

task<size_t> SampleAnchorTcpClient::ReceiveAnchorDataLengthMessage()
{
    byte data[4];
    m_anchorTcpSocketStreamReader->ReadBytes(Platform::ArrayReference<byte>(data, SampleAnchorTcpCommon::c_streamHeaderByteArrayLength));
    unsigned int lengthMessageSize = *reinterpret_cast<unsigned int*>(data);
    if (lengthMessageSize > 0)
    {
        OutputDebugString(L"One or more anchors to be received.\n");
        return task_from_result<size_t>(lengthMessageSize);
    }
    else
    {
        OutputDebugString(L"No anchors to be received.\n");
        ConnectToServer();
    }
    return task_from_result<size_t>(0);
}

Üst bilgi paketini aldıktan sonra, kaç bayt sabit veri beklememiz gerektiğini biliyoruz. Bu baytları akıştan okumaya devam edebiliriz.

}).then([this](size_t dataStreamLength)
    {
        if (dataStreamLength > 0)
        {
            std::wstring debugMessage = std::to_wstring(dataStreamLength);
            debugMessage += L" bytes of anchor data incoming.\n";
            OutputDebugString(debugMessage.c_str());
            // Prepare to receive the data stream in one or more pieces.
            m_anchorStreamLength = dataStreamLength;
            m_exportedAnchorStoreBytes.clear();
            m_exportedAnchorStoreBytes.resize(m_anchorStreamLength);
            OutputDebugString(L"Loading byte stream.\n");
            return ReceiveAnchorDataStream();
        }
        else
        {
            OutputDebugString(L"Error: Anchor data size not received.\n");
            ConnectToServer();
            return task_from_result<bool>(false);
        }
    });
}

Yer işareti veri akışını almak için kodumuz aşağıdadır. Yine, önce akıştan baytları yükleyeceğiz; StreamSocket ağdan bu miktarda bayt almayı beklediğinden bu işlemin tamamlanması biraz zaman alabilir.

Yükleme işlemi tamamlandığında, bu bayt sayısını okuyabiliriz. Yer işareti veri akışı için beklediğimiz bayt sayısını aldıysak, devam edip yer işareti verilerini içeri aktarabiliriz; değilse, bir tür hata olmalı. Örneğin, sunucu örneği veri akışını göndermeyi bitirmeden sonlandırıldığında veya tüm veri akışının istemci tarafından alınabilmesi için ağ kapandığında bu durum oluşabilir.

task<bool> SampleAnchorTcpClient::ReceiveAnchorDataStream()
{
    if (m_anchorStreamLength > 0)
    {
        // First, we load the bytes from the network socket.
        return create_task(m_anchorTcpSocketStreamReader->LoadAsync(m_anchorStreamLength)).then([this](size_t bytesLoadedByStreamReader)
        {
            if (bytesLoadedByStreamReader > 0)
            {
                // Once the bytes are loaded, we can read them from the stream.
                m_anchorTcpSocketStreamReader->ReadBytes(Platform::ArrayReference<byte>(&m_exportedAnchorStoreBytes[0],
                    bytesLoadedByStreamReader));
                // Check status.
                if (bytesLoadedByStreamReader == m_anchorStreamLength)
                {
                    // The whole stream has arrived. We can process the data.
                    // Informational message of progress complete.
                    std::wstring infoMessage = std::to_wstring(bytesLoadedByStreamReader);
                    infoMessage += L" bytes read out of ";
                    infoMessage += std::to_wstring(m_anchorStreamLength);
                    infoMessage += L" total bytes; importing the data.\n";
                    OutputDebugStringW(infoMessage.c_str());
                    // Kick off a thread to wait for a new message indicating another incoming anchor data stream.
                    WaitForAnchorDataStream();
                    // Process the data for the stream we just received.
                    return SpatialAnchorImportExportHelper::ImportAnchorDataAsync(m_exportedAnchorStoreBytes, m_spatialAnchorHelper->GetAnchorMap());
                }
                else
                {
                    OutputDebugString(L"Error: Fewer than expected anchor data bytes were received.\n");
                }
            }
            else
            {
                OutputDebugString(L"Error: No anchor bytes were received.\n");
            }
            return task_from_result<bool>(false);
        });
    }
    else
    {
        OutputDebugString(L"Warning: A zero-length data buffer was sent.\n");
        return task_from_result<bool>(false);
    }
}

Yine bilinmeyen ağ hatalarını işlemeye hazırlıklı olmamız gerekir.

void SampleAnchorTcpClient::HandleException(Exception^ exception)
{
    std::wstring error = L"Connection error: ";
    error += exception->ToString()->Data();
    error += L"\n";
    OutputDebugString(error.c_str());
}

İşte bu kadar! Şimdi, ağ üzerinden alınan tutturucuları bulmak için yeterli bilgiye sahip olmanız gerekir. Yine, istemcinin yer işaretine başarılı bir şekilde yer açmak için yeterli görsel izleme verilerine sahip olması gerektiğini unutmayın; Hemen işe yaramazsa bir süre gezinmeyi deneyin. Yine de işe yaramazsa, sunucunun daha fazla yer işareti göndermesini sağlayın ve ağ iletişimlerini kullanarak istemci için uygun olan bağlantılarda anlaşın. HolographicSpatialAnchorTransferSample'ı indirerek, istemci ve sunucu IP'lerinizi yapılandırarak ve bunu istemci ve sunucu HoloLens cihazlarına dağıtarak bunu deneyebilirsiniz.

Ayrıca bkz.