This example is based on writing a file from asynchronously received TCP packets. FileStream->Write is not thread safe, so this approach deals with letting file write occur in the background and waiting for it to complete before writing the next received block of data bytes.
In your header file
FileStream ^ClientRxFileStream1;
IAsyncResult ^ClientRxFileStreamAsyncResult1;
array<Byte> ^ClientReceiveFileWriteBytes;
Receiving the first packet of bytes
//Create the file to write to
ClientRxFileStream1 = gcnew FileStream(ClientRxFilename, FileMode::Create, FileAccess::Write, FileShare::None);
//Copy rx data to memory so it can be written to the file in the background
ClientReceiveFileWriteBytes = gcnew array<Byte>(so->bufSize);
Array::Copy(so->message, DataStart, ClientReceiveFileWriteBytes, 0, so->bufSize);
//Write it to the file in the background
ClientRxFileStreamAsyncResult1 = ClientRxFileStream1->BeginWrite(ClientReceiveFileWriteBytes, 0, so->bufSize, nullptr, nullptr);
Receiving subsequent packets of bytes
if (ClientRxFileStream1 != nullptr)
{
if (ClientRxFileStreamAsyncResult1 != nullptr)
{
ClientRxFileStreamAsyncResult1->AsyncWaitHandle->WaitOne(10000); //Block current thread until operation completes, timeout in mS
ClientRxFileStream1->EndWrite(ClientRxFileStreamAsyncResult1); //Last step to the write
if (ClientRxFileStreamAsyncResult1->IsCompleted) //Make sure the write really completed.
{
//Copy rx data to memory so it can be written to the file in the background
ClientReceiveFileWriteBytes = gcnew array<Byte>(Convert::ToInt32(Size));
Array::Copy(so->message, 0, ClientReceiveFileWriteBytes, 0, Convert::ToInt32(Size));
//Write it to the file in the background
ClientRxFileStreamAsyncResult1 = ClientRxFileStream1->BeginWrite(ClientReceiveFileWriteBytes, 0, Convert::ToInt32(Size), nullptr, nullptr);
}
else
{
//ERROR HERE!
}
}
}
Once All Bytes Received
if (ClientRxFileStreamAsyncResult1 != nullptr)
{
ClientRxFileStreamAsyncResult1->AsyncWaitHandle->WaitOne(10000); //Block current thread until operation completes, timeout in mS
ClientRxFileStream1->EndWrite(ClientRxFileStreamAsyncResult1);
}
ClientRxFileStream1->Close();
ClientRxFileStream1 = nullptr;
ClientRxFileStreamAsyncResult1 = nullptr;
Working Example Of Doing It With Multiple Clients
In your .h file
using namespace System::Collections::Generic;
ref class ClientRxFileStreamHandler
{
public:
property String ^IpAddress;
property String ^FileName;
DateTime ClientLastActivity;
FileStream ^ClientRxFileStream;
IAsyncResult ^ClientRxFileStreamAsyncResult;
array ^ClientReceiveFileWriteBytes;
};
List<ClientRxFileStreamHandler^> ^OurClientRxFileStreamHandlers;
In your .c file
//***********************
//***** CONSTRUCTOR *****
//***********************
OurClientRxFileStreamHandlers = gcnew List(0);
//***********************************
//***** GOT NEXT PACKET OF DATA *****
//***********************************
//----- FIND THIS CLIENTS FILE STREAM HANDLER OR CREATE A NEW ONE FOR IT -----
Index = -1;
for (Count = 0; Count < OurClientRxFileStreamHandlers->Count; Count++) //If we already have this client then overwrite it
{
if ((OurClientRxFileStreamHandlers[Count]->IpAddress == ClientIpAddress) && (OurClientRxFileStreamHandlers[Count]->FileName == FileName))
{
//----- EXISTING CLIENT -----
Index = Count;
//Ensure last write has completed
if ((OurClientRxFileStreamHandlers[Index]->ClientRxFileStream != nullptr) && (OurClientRxFileStreamHandlers[Index]->ClientRxFileStreamAsyncResult != nullptr))
{
OurClientRxFileStreamHandlers[Index]->ClientRxFileStreamAsyncResult->AsyncWaitHandle->WaitOne(10000); //Block current thread until operation completes, timeout in mS
OurClientRxFileStreamHandlers[Index]->ClientRxFileStream->EndWrite(OurClientRxFileStreamHandlers[Index]->ClientRxFileStreamAsyncResult); //Last step to the write
}
break;
}
}
if (Index == -1)
{
//----- NEW CLIENT -----
OurClientRxFileStreamHandlers->Add(gcnew ClientRxFileStreamHandler);
Index = OurClientRxFileStreamHandlers->Count - 1;
OurClientRxFileStreamHandlers[Index]->IpAddress = ClientIpAddress;
OurClientRxFileStreamHandlers[Index]->FileName = FileName;
}
OurClientRxFileStreamHandlers[Index]->ClientLastActivity = DateTime::Now;
//----- QUICKLY CHECK FOR REMOVING ANY OLD CLIENTS THAT HAVE VANISHED -----
DateTime RemoveBeforeDateTime = DateTime::Now - TimeSpan(0, 5, 0); //We delete clients > 5 mins since last activity
for (Count = 0; Count < OurClientRxFileStreamHandlers->Count; Count++) //If we already have this client then overwrite it
{
if (OurClientRxFileStreamHandlers[Count]->ClientLastActivity < DeleteBeforeDateTime)
{
//REMOVE THIS CLIENT FROM THE ARRAY
OurClientRxFileStreamHandlers[Count]->ClientRxFileStream->Close();
OurClientRxFileStreamHandlers[Count]->ClientRxFileStream = nullptr;
OurClientRxFileStreamHandlers[Count]->ClientRxFileStreamAsyncResult = nullptr;
OurClientRxFileStreamHandlers->RemoveAt(Count);
Count--;
}
}
if ((ClientRxFilename != "") && (DataLength > 0))
{
if (File::Exists(ClientRxFilename))
{
System::IO::FileInfo ^FileInfo1 = gcnew System::IO::FileInfo(ClientRxFilename);
FileSize = FileInfo1->Length;
}
else
{
FileSize = 0;
}
if (DataStart == FileSize) //Data start must match the current file size
{
//----- WRITE THE FILE DATA -----
if (OurClientRxFileStreamHandlers[Index]->ClientRxFileStream == nullptr)
{
if (FileSize == 0)
OurClientRxFileStreamHandlers[Index]->ClientRxFileStream = gcnew FileStream(ClientRxFilename, FileMode::Create, FileAccess::Write, FileShare::None);
else
OurClientRxFileStreamHandlers[Index]->ClientRxFileStream = gcnew FileStream(ClientRxFilename, FileMode::Append, FileAccess::Write, FileShare::None);
}
OurClientRxFileStreamHandlers[Index]->ClientReceiveFileWriteBytes = gcnew array<Byte>(RxLength - PacketDataStart);
Array::Copy(RxData, PacketDataStart, OurClientRxFileStreamHandlers[Index]->ClientReceiveFileWriteBytes, 0, (RxLength - PacketDataStart));
OurClientRxFileStreamHandlers[Index]->ClientRxFileStreamAsyncResult = OurClientRxFileStreamHandlers[Index]->ClientRxFileStream->BeginWrite(OurClientRxFileStreamHandlers[Index]->ClientReceiveFileWriteBytes, 0, (RxLength - PacketDataStart), nullptr, nullptr);
Success = true;
}
}
else if ((FileName != "") && (DataLength == 0))
{
//----- END OF FILE -----
//Close it and remove client
if (OurClientRxFileStreamHandlers[Index]->ClientRxFileStream != nullptr)
{
OurClientRxFileStreamHandlers[Index]->ClientRxFileStream->Close();
OurClientRxFileStreamHandlers[Index]->ClientRxFileStream = nullptr;
OurClientRxFileStreamHandlers[Index]->ClientRxFileStreamAsyncResult = nullptr;
OurClientRxFileStreamHandlers->RemoveAt(Index);
}
Success = true;
}
Feel free to comment if you can add help to this page or point out issues and solutions you have found. I do not provide support on this site, if you need help with a problem head over to stack overflow.