Kanál s dělením dat do bloků
Ukázka ChunkingChannel ukazuje, jak se dá vlastní protokol nebo vrstvený kanál použít k vytváření bloků dat a odsunutí libovolných velkých zpráv.
Při odesílání velkých zpráv pomocí technologie Windows Communication Foundation (WCF) je často žádoucí omezit množství paměti používané k ukládání těchto zpráv do vyrovnávací paměti. Jedním z možných řešení je streamování textu zprávy (za předpokladu, že je většina dat v těle). Některé protokoly však vyžadují ukládání do vyrovnávací paměti celé zprávy. Spolehlivé zasílání zpráv a zabezpečení jsou dva takové příklady. Dalším možným řešením je rozdělit velkou zprávu na menší zprávy označované jako bloky dat, odeslat tyto bloky po jednom bloku a rozředět velkou zprávu na přijímající straně. Samotná aplikace může provést toto vytváření bloků dat a odstranění bloků dat nebo k tomu může použít vlastní kanál.
Bloky dat by se měly vždy používat až po vytvoření celé zprávy, která se má odeslat. Blokovací kanál by měl být vždy vrstvený pod kanálem zabezpečení a spolehlivým kanálem relace.
Poznámka:
Postup nastavení a pokyny k sestavení pro tuto ukázku najdete na konci tohoto tématu.
Předpoklady a omezení kanálu bloků dat
Struktura zpráv
Kanál bloků dat předpokládá následující strukturu zpráv, aby zprávy byly blokovány:
<soap:Envelope>
<!-- headers -->
<soap:Body>
<operationElement>
<paramElement>data to be chunked</paramElement>
</operationElement>
</soap:Body>
</soap:Envelope>
Při použití modelu ServiceModel operace kontraktu, které mají 1 vstupní parametr, vyhovují tomuto tvaru zprávy pro jejich vstupní zprávu. Podobně operace kontraktů, které mají 1 výstupní parametr nebo návratovou hodnotu, odpovídají tomuto tvaru zprávy pro jejich výstupní zprávu. Tady jsou příklady těchto operací:
[ServiceContract]
interface ITestService
{
[OperationContract]
Stream EchoStream(Stream stream);
[OperationContract]
Stream DownloadStream();
[OperationContract(IsOneWay = true)]
void UploadStream(Stream stream);
}
Přednášky
Kanál bloků dat vyžaduje, aby se zprávy doručily přesně jednou v seřazených doručení zpráv (bloků). To znamená, že základní zásobník kanálu musí být relační. Relace mohou být poskytovány přenosem (například přenosem TCP) nebo kanálem protokolu sessionful (například kanál ReliableSession).
Asynchronní odesílání a příjem
Asynchronní metody odesílání a příjmu nejsou v této verzi ukázky kanálu bloků dat implementovány.
Protokol bloků dat
Kanál bloků dat definuje protokol, který označuje začátek a konec řady bloků dat a také pořadové číslo každého bloku dat. Následující tři ukázkové zprávy ukazují počáteční, blokové a koncové zprávy s komentáři, které popisují klíčové aspekty každého.
Úvodní zpráva
<s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing"
xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Header>
<!--Original message action is replaced with a chunking-specific action. -->
<a:Action s:mustUnderstand="1">http://samples.microsoft.com/chunkingAction</a:Action>
<!--
Original message is assigned a unique id that is transmitted
in a MessageId header. Note that this is different from the WS-Addressing MessageId header.
-->
<MessageId s:mustUnderstand="1" xmlns="http://samples.microsoft.com/chunking">
53f183ee-04aa-44a0-b8d3-e45224563109
</MessageId>
<!--
ChunkingStart header signals the start of a chunked message.
-->
<ChunkingStart s:mustUnderstand="1" i:nil="true" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://samples.microsoft.com/chunking" />
<!--
Original message action is transmitted in OriginalAction.
This is required to re-create the original message on the other side.
-->
<OriginalAction xmlns="http://samples.microsoft.com/chunking">
http://tempuri.org/ITestService/EchoStream
</OriginalAction>
<!--
All original message headers are included here.
-->
</s:Header>
<s:Body>
<!--
Chunking assumes this structure of Body content:
<element>
<childelement>large data to be chunked<childelement>
</element>
The start message contains just <element> and <childelement> without
the data to be chunked.
-->
<EchoStream xmlns="http://tempuri.org/">
<stream />
</EchoStream>
</s:Body>
</s:Envelope>
Zpráva bloku dat
<s:Envelope
xmlns:a="http://www.w3.org/2005/08/addressing"
xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Header>
<!--
All chunking protocol messages have this action.
-->
<a:Action s:mustUnderstand="1">
http://samples.microsoft.com/chunkingAction
</a:Action>
<!--
Same as MessageId in the start message. The GUID indicates which original message this chunk belongs to.
-->
<MessageId s:mustUnderstand="1"
xmlns="http://samples.microsoft.com/chunking">
53f183ee-04aa-44a0-b8d3-e45224563109
</MessageId>
<!--
The sequence number of the chunk.
This number restarts at 1 with each new sequence of chunks.
-->
<ChunkNumber s:mustUnderstand="1"
xmlns="http://samples.microsoft.com/chunking">
1096
</ChunkNumber>
</s:Header>
<s:Body>
<!--
The chunked data is wrapped in a chunk element.
The encoding of this data (and the entire message)
depends on the encoder used. The chunking channel does not mandate an encoding.
-->
<chunk xmlns="http://samples.microsoft.com/chunking">
kfSr2QcBlkHTvQ==
</chunk>
</s:Body>
</s:Envelope>
Ukončit zprávu
<s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing"
xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Header>
<a:Action s:mustUnderstand="1">
http://samples.microsoft.com/chunkingAction
</a:Action>
<!--
Same as MessageId in the start message. The GUID indicates which original message this chunk belongs to.
-->
<MessageId s:mustUnderstand="1"
xmlns="http://samples.microsoft.com/chunking">
53f183ee-04aa-44a0-b8d3-e45224563109
</MessageId>
<!--
ChunkingEnd header signals the end of a chunk sequence.
-->
<ChunkingEnd s:mustUnderstand="1" i:nil="true"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://samples.microsoft.com/chunking" />
<!--
ChunkingEnd messages have a sequence number.
-->
<ChunkNumber s:mustUnderstand="1"
xmlns="http://samples.microsoft.com/chunking">
79
</ChunkNumber>
</s:Header>
<s:Body>
<!--
The ChunkingEnd message has the same <element><childelement> structure
as the ChunkingStart message.
-->
<EchoStream xmlns="http://tempuri.org/">
<stream />
</EchoStream>
</s:Body>
</s:Envelope>
Architektura bloků kanálů
Kanál pro vytváření bloků dat je takový IDuplexSessionChannel
, že na vysoké úrovni se řídí typickou architekturou kanálu. Existuje, ChunkingBindingElement
že může vytvořit a ChunkingChannelFactory
ChunkingChannelListener
. Vytvoří ChunkingChannelFactory
instance ChunkingChannel
, kdy se zobrazí výzva. Vytvoří ChunkingChannelListener
instance ChunkingChannel
, kdy je přijat nový vnitřní kanál. Samotný ChunkingChannel
je zodpovědný za odesílání a přijímání zpráv.
Na další úrovni dolů ChunkingChannel
spoléhá na několik komponent pro implementaci protokolu bloků dat. Na straně odeslání kanál používá vlastní XmlDictionaryWriter volaný, ChunkingWriter
který provede skutečný blok dat. ChunkingWriter
používá vnitřní kanál přímo k odesílání bloků dat. Použití vlastního XmlDictionaryWriter
kódu nám umožňuje odesílat bloky dat, protože se píše velké tělo původní zprávy. To znamená, že neuvolníme vyrovnávací paměť celé původní zprávy.
Na straně ChunkingChannel
příjmu přetáhne zprávy z vnitřního kanálu a předá je vlastní XmlDictionaryReader volaný ChunkingReader
, který rekonstituuje původní zprávu z příchozích bloků dat. ChunkingChannel
zabalí toto ChunkingReader
do vlastní Message
implementace volané ChunkingMessage
a vrátí tuto zprávu do vrstvy výše. Tato kombinace ChunkingReader
a ChunkingMessage
umožňuje de-chunkovat původní text zprávy, protože je přečtena vrstvou výše, a nemusí ukládat do vyrovnávací paměti celý původní text zprávy. ChunkingReader
obsahuje frontu, ve které uloží příchozí bloky do vyrovnávací paměti až do maximálního konfigurovatelného počtu bloků dat v vyrovnávací paměti. Když dosáhnete tohoto maximálního limitu, čtenář čeká na vyprázdnění zpráv z fronty vrstvou výše (tj. čtením z původního textu zprávy) nebo až do dosažení maximálního časového limitu příjmu.
Vytváření bloků programovacího modelu
Vývojáři služeb můžou určit, které zprávy mají být blokovány, použitím atributu ChunkingBehavior
na operace v rámci kontraktu. Atribut zveřejňuje AppliesTo
vlastnost, která umožňuje vývojáři určit, zda se bloky dat vztahují na vstupní zprávu, výstupní zprávu nebo obojí. Následující příklad ukazuje použití atributu ChunkingBehavior
:
[ServiceContract]
interface ITestService
{
[OperationContract]
[ChunkingBehavior(ChunkingAppliesTo.Both)]
Stream EchoStream(Stream stream);
[OperationContract]
[ChunkingBehavior(ChunkingAppliesTo.OutMessage)]
Stream DownloadStream();
[OperationContract(IsOneWay=true)]
[ChunkingBehavior(ChunkingAppliesTo.InMessage)]
void UploadStream(Stream stream);
}
Z tohoto programovacího modelu ChunkingBindingElement
zkompiluje seznam identifikátorů URI akcí, které identifikují zprávy, které mají být blokovány. Akce každé odchozí zprávy se porovná s tímto seznamem a určí, jestli má být zpráva blokována nebo odeslána přímo.
Implementace operace odeslání
Na vysoké úrovni operace Odeslání nejprve zkontroluje, jestli musí být odchozí zpráva blokovaná, a pokud ne, odešle zprávu přímo pomocí vnitřního kanálu.
Pokud zpráva musí být blokována, Send vytvoří novou ChunkingWriter
a volání WriteBodyContents
odchozí zprávy, která jí tuto ChunkingWriter
zprávu předává . Potom ChunkingWriter
provede blok zpráv (včetně kopírování původních hlaviček zpráv do počáteční zprávy bloku) a odesílá bloky dat pomocí vnitřního kanálu.
Několik podrobností stojí za zmínku:
Odešlete první volání
ThrowIfDisposedOrNotOpened
, abyste měli jistotu,CommunicationState
že je otevřený.Odesílání se synchronizuje, aby se pro každou relaci posílala vždy jenom jedna zpráva. Při odesílání blokované zprávy existuje
ManualResetEvent
názevsendingDone
, který se resetuje. Po odeslání zprávy koncového bloku je tato událost nastavena. Metoda Send čeká na nastavení této události, než se pokusí odeslat odchozí zprávu.Odešle zámky
CommunicationObject.ThisLock
, aby se zabránilo změnám synchronizovaného stavu při odesílání. Další informace o stavech a stavovém počítači najdete v CommunicationObject CommunicationObject dokumentaci.Časový limit předaný odeslání se použije jako časový limit pro celou operaci odeslání, která zahrnuje odesílání všech bloků dat.
XmlDictionaryWriter Vlastní návrh byl zvolen, aby se zabránilo ukládání do vyrovnávací paměti celého textu původní zprávy. Kdybysme se dostali XmlDictionaryReader na tělo pomocí
message.GetReaderAtBodyContents
celého těla, bylo by do vyrovnávací paměti. Místo toho máme vlastní XmlDictionaryWriter , který se předávámessage.WriteBodyContents
. Když zpráva volá WriteBase64 na zapisovači, zapisovač zabalí bloky do zpráv a odešle je pomocí vnitřního kanálu. WriteBase64 blokuje, dokud se blok nepředá.
Implementace operace příjmu
Na vysoké úrovni operace Přijmout nejprve zkontroluje, zda příchozí zpráva není null
a že její akce je .ChunkingAction
Pokud nesplňuje obě kritéria, zpráva se vrátí beze změny z funkce Přijmout. Jinak funkce Receive vytvoří novou ChunkingReader
a novou ChunkingMessage
obálku kolem ní (voláním GetNewChunkingMessage
). Před vrácením této nové ChunkingMessage
funkce receive použije vlákno fondu vláken ke spuštění ReceiveChunkLoop
, které volá innerChannel.Receive
ve smyčce a ruce bloky dat do ChunkingReader
doby, než se přijme zpráva koncového bloku dat nebo dojde k vypršení časového limitu příjmu.
Několik podrobností stojí za zmínku:
Stejně jako funkce Odeslat přijímat první volání
ThrowIfDisposedOrNotOpened
, abyste měli jistotu, žeCommunicationState
je otevřená.Příjem je také synchronizovaný, takže z relace lze přijmout pouze jednu zprávu najednou. To je zvlášť důležité, protože jakmile se přijme počáteční zpráva bloku dat, očekává se, že všechny následné přijaté zprávy budou v této nové sekvenci bloků dat do přijetí zprávy koncového bloku. Příjem nemůže přijímat zprávy z vnitřního kanálu, dokud nebudou přijaty všechny bloky dat, které patří do zprávy, která právě probíhá. K tomuto účelu používá funkce Receive pojmenovanou
ManualResetEvent
currentMessageCompleted
, která je nastavena při přijetí zprávy koncového bloku a resetování při přijetí nové počáteční zprávy bloku.Na rozdíl od funkce Odeslat, příjem nezabrání přechodům synchronizovaného stavu při přijímání. Při přijímání je například možné volat zavřít a čekat, až se dokončí čekání na přijetí původní zprávy nebo se dosáhne zadané hodnoty časového limitu.
Časový limit předaný službě Receive se používá jako časový limit pro celou operaci příjmu, která zahrnuje příjem všech bloků dat.
Pokud vrstva, která zprávu spotřebovává, spotřebovává text zprávy rychlostí nižší než rychlost příchozích bloků zpráv,
ChunkingReader
vyrovnávací paměti tyto příchozí bloky dat až do limitu určenéhoChunkingBindingElement.MaxBufferedChunks
. Po dosažení limitu se z nižší vrstvy nepřebírají žádné další bloky dat, dokud se nevyužívají bloky dat ve vyrovnávací paměti nebo se nedosáhne časového limitu příjmu.
Přepsání objektu CommunicationObject
OnOpen
OnOpen
volání innerChannel.Open
pro otevření vnitřního kanálu.
OnClose
OnClose
nejprve se nastaví stopReceive
tak, aby true
signalizoval čekající ReceiveChunkLoop
na zastavení. Pak počká na receiveStopped
ManualResetEventnastavení , která se nastaví při ReceiveChunkLoop
zastavení. Za předpokladu, že se ReceiveChunkLoop
zastaví v zadaném časovém limitu, OnClose
volání innerChannel.Close
se zbývajícím časovým limitem.
OnAbort
OnAbort
volání innerChannel.Abort
pro přerušení vnitřního kanálu. Pokud čeká na vyřízení ReceiveChunkLoop
, dojde k výjimce z čekajícího innerChannel.Receive
volání.
OnFaulted
Nevyžaduje ChunkingChannel
zvláštní chování, pokud je kanál chybný, takže OnFaulted
není přepsán.
Implementace objektu pro vytváření kanálů
Zodpovídá ChunkingChannelFactory
za vytváření instancí ChunkingDuplexSessionChannel
a za kaskádové přechody stavu do objektu pro vytváření vnitřních kanálů.
OnCreateChannel
k vytvoření vnitřního kanálu používá továrnu IDuplexSessionChannel
vnitřního kanálu. Potom vytvoří nový ChunkingDuplexSessionChannel
předaný tento vnitřní kanál spolu se seznamem akcí zpráv, které mají být blokovány, a maximální počet bloků dat, které se mají ukládat do vyrovnávací paměti při příjmu. Seznam akcí zpráv, které mají být blokovány, a maximální počet bloků dat do vyrovnávací paměti jsou dva parametry předané ChunkingChannelFactory
v jeho konstruktoru. Tato část ChunkingBindingElement
popisuje, odkud tyto hodnoty pocházejí.
, OnOpen
OnClose
OnAbort
a jejich asynchronní ekvivalenty volají odpovídající metodu přechodu stavu ve vnitřní kanálu továrny.
Implementace naslouchacího procesu kanálu
Jedná se ChunkingChannelListener
o obálku kolem naslouchacího procesu vnitřního kanálu. Její hlavní funkcí kromě volání delegáta na tento naslouchací proces vnitřního kanálu je zabalit nové ChunkingDuplexSessionChannels
kanály přijaté z naslouchacího procesu vnitřního kanálu. To se provádí v OnAcceptChannel
a OnEndAcceptChannel
. Nově vytvořený kanál ChunkingDuplexSessionChannel
se předává spolu s dalšími parametry, které jsme popsali dříve.
Implementace elementu vazby a vazby
ChunkingBindingElement
je zodpovědný za vytvoření a ChunkingChannelFactory
ChunkingChannelListener
. Kontroluje ChunkingBindingElement
, zda T v CanBuildChannelFactory
<T> a CanBuildChannelListener
<T> je typu IDuplexSessionChannel
(jediný kanál podporovaný kanálem bloků dat) a že ostatní prvky vazby v vazbě podporují tento typ kanálu.
BuildChannelFactory
<T> nejprve zkontroluje, jestli je možné sestavit požadovaný typ kanálu, a pak získá seznam akcí zpráv, které se mají zapsat do bloku. Další informace najdete v následující části. Potom vytvoří nové ChunkingChannelFactory
předání objektu pro vytvoření vnitřního kanálu (jak je vráceno z context.BuildInnerChannelFactory<IDuplexSessionChannel>
), seznamu akcí zpráv a maximálního počtu bloků dat do vyrovnávací paměti. Maximální počet bloků dat pochází z vlastnosti označované jako MaxBufferedChunks
vystavená objektem ChunkingBindingElement
.
BuildChannelListener<T>
má podobnou implementaci pro vytvoření ChunkingChannelListener
a předání vnitřního kanálu naslouchací proces.
V této ukázce je zahrnuta ukázková vazba s názvem TcpChunkingBinding
. Tato vazba se skládá ze dvou prvků vazby: TcpTransportBindingElement
a ChunkingBindingElement
. Kromě zveřejnění MaxBufferedChunks
vlastnosti vazba také nastaví některé vlastnosti TcpTransportBindingElement
, například MaxReceivedMessageSize
(nastaví ho na ChunkingUtils.ChunkSize
+ 100 kB pro hlavičky).
TcpChunkingBinding
také implementuje IBindingRuntimePreferences
a vrací hodnotu true z ReceiveSynchronously
metody označující, že jsou implementována pouze synchronní volání receive.
Určení, které zprávy se mají blokovat
Bloky bloků kanálů blokují pouze zprávy identifikované prostřednictvím atributu ChunkingBehavior
. Třída ChunkingBehavior
implementuje IOperationBehavior
a je implementována voláním AddBindingParameter
metody. V této metodě ChunkingBehavior
prozkoumá hodnotu jeho AppliesTo
vlastnosti (InMessage
nebo obojí) a určí, OutMessage
které zprávy mají být blokovány. Pak získá akci každé z těchto zpráv (z kolekce Messages on OperationDescription
) a přidá ji do kolekce řetězců obsažené v instanci ChunkingBindingParameter
. Pak se ChunkingBindingParameter
přidá do poskytnutého BindingParameterCollection
souboru .
To BindingParameterCollection
se předá uvnitř BindingContext
každého elementu vazby v vazbě, když tento element vazby sestaví objekt pro vytváření kanálů nebo naslouchací proces kanálu. Provádění ChunkingBindingElement
a BuildChannelListener<T>
vytáhnutí tohoto ChunkingBindingParameter
BindingContext'
z těchto BindingParameterCollection
BuildChannelFactory<T>
Kolekce akcí obsažených v tomto objektu ChunkingBindingParameter
ChunkingChannelFactory
se pak předá nebo ChunkingChannelListener
, která je následně předána ChunkingDuplexSessionChannel
do .
Spuštění ukázky
Nastavení, sestavení a spuštění ukázky
Pomocí následujícího příkazu nainstalujte ASP.NET 4.0.
%windir%\Microsoft.NET\Framework\v4.0.XXXXX\aspnet_regiis.exe /i /enable
Ujistěte se, že jste pro ukázky windows Communication Foundation provedli jednorázovou instalační proceduru.
Pokud chcete sestavit řešení, postupujte podle pokynů v části Sestavení ukázek Windows Communication Foundation.
Pokud chcete spustit ukázku v konfiguraci s jedním nebo více počítači, postupujte podle pokynů v části Spuštění ukázek windows Communication Foundation.
Nejprve spusťte Service.exe a pak spusťte Client.exe a sledujte výstup v obou oknech konzoly.
Při spuštění ukázky se očekává následující výstup.
Klient:
Press enter when service is available
> Sent chunk 1 of message 867c1fd1-d39e-4be1-bc7b-32066d7ced10
> Sent chunk 2 of message 867c1fd1-d39e-4be1-bc7b-32066d7ced10
> Sent chunk 3 of message 867c1fd1-d39e-4be1-bc7b-32066d7ced10
> Sent chunk 4 of message 867c1fd1-d39e-4be1-bc7b-32066d7ced10
> Sent chunk 5 of message 867c1fd1-d39e-4be1-bc7b-32066d7ced10
> Sent chunk 6 of message 867c1fd1-d39e-4be1-bc7b-32066d7ced10
> Sent chunk 7 of message 867c1fd1-d39e-4be1-bc7b-32066d7ced10
> Sent chunk 8 of message 867c1fd1-d39e-4be1-bc7b-32066d7ced10
> Sent chunk 9 of message 867c1fd1-d39e-4be1-bc7b-32066d7ced10
> Sent chunk 10 of message 867c1fd1-d39e-4be1-bc7b-32066d7ced10
< Received chunk 1 of message 5b226ad5-c088-4988-b737-6a565e0563dd
< Received chunk 2 of message 5b226ad5-c088-4988-b737-6a565e0563dd
< Received chunk 3 of message 5b226ad5-c088-4988-b737-6a565e0563dd
< Received chunk 4 of message 5b226ad5-c088-4988-b737-6a565e0563dd
< Received chunk 5 of message 5b226ad5-c088-4988-b737-6a565e0563dd
< Received chunk 6 of message 5b226ad5-c088-4988-b737-6a565e0563dd
< Received chunk 7 of message 5b226ad5-c088-4988-b737-6a565e0563dd
< Received chunk 8 of message 5b226ad5-c088-4988-b737-6a565e0563dd
< Received chunk 9 of message 5b226ad5-c088-4988-b737-6a565e0563dd
< Received chunk 10 of message 5b226ad5-c088-4988-b737-6a565e0563dd
Server:
Service started, press enter to exit
< Received chunk 1 of message 867c1fd1-d39e-4be1-bc7b-32066d7ced10
< Received chunk 2 of message 867c1fd1-d39e-4be1-bc7b-32066d7ced10
< Received chunk 3 of message 867c1fd1-d39e-4be1-bc7b-32066d7ced10
< Received chunk 4 of message 867c1fd1-d39e-4be1-bc7b-32066d7ced10
< Received chunk 5 of message 867c1fd1-d39e-4be1-bc7b-32066d7ced10
< Received chunk 6 of message 867c1fd1-d39e-4be1-bc7b-32066d7ced10
< Received chunk 7 of message 867c1fd1-d39e-4be1-bc7b-32066d7ced10
< Received chunk 8 of message 867c1fd1-d39e-4be1-bc7b-32066d7ced10
< Received chunk 9 of message 867c1fd1-d39e-4be1-bc7b-32066d7ced10
< Received chunk 10 of message 867c1fd1-d39e-4be1-bc7b-32066d7ced10
> Sent chunk 1 of message 5b226ad5-c088-4988-b737-6a565e0563dd
> Sent chunk 2 of message 5b226ad5-c088-4988-b737-6a565e0563dd
> Sent chunk 3 of message 5b226ad5-c088-4988-b737-6a565e0563dd
> Sent chunk 4 of message 5b226ad5-c088-4988-b737-6a565e0563dd
> Sent chunk 5 of message 5b226ad5-c088-4988-b737-6a565e0563dd
> Sent chunk 6 of message 5b226ad5-c088-4988-b737-6a565e0563dd
> Sent chunk 7 of message 5b226ad5-c088-4988-b737-6a565e0563dd
> Sent chunk 8 of message 5b226ad5-c088-4988-b737-6a565e0563dd
> Sent chunk 9 of message 5b226ad5-c088-4988-b737-6a565e0563dd
> Sent chunk 10 of message 5b226ad5-c088-4988-b737-6a565e0563dd