Search code examples
c++buildertcpclientindy

How to write raw binary data using Indy TCP Client in C++ Builder


Using Embarcadero C++ Builder 10.3.

I have a DynamicArray<uint8_t> myData object. I want to send/write its raw binary content (bytes) to a server using the TIdTcpClient component. I'm going about it like this:

TIdTcpClient tcpClient1;
// Bla Bla Bla
tcpClient1->IOHandler->Write(rawData);

Where rawData should be of type TIdBytes or TIdStream

So basically, it boils down to the following: How to convert myData object to a rawData type of either TIdBytes or TIdStream?


Solution

  • First off, TIdStream has not been part of Indy in a VERY VERY LONG time, which makes me wonder if you are using a very old version of Indy, not the one that shipped with C++Builder 10.3. Indy has supported the RTL's standard TStream class for a very long time.

    That being said...

    TIdBytes is an alias for System::DynamicArray<System::Byte>, where System::Byte is an alias for unsigned char, which is the same size and sign-ness as uint8_t (depending on compiler, uint8_t might even just be an alias for unsigned char).

    So, the simplest solution, without having to make a separate copy of your data, is to simply type-cast it, eg:

    tcpClient1->IOHandler->Write(reinterpret_cast<TIdBytes&>(myData));
    

    This is technically undefined behavior, since DynamicArray<uint8_t> and DynamicArray<Byte> are unrelated types (unless uint8_t and Byte are both aliases for unsigned char), but it will work in your case since it is the same underlying code behind both arrays, and uint8_t and Byte have the same underlying memory layout.

    Alternatively, the next simplest solution, without copying data or invoking undefined behavior, is to use Indy's TIdReadOnlyMemoryBufferStream class in IdGlobal.hpp, eg:

    TIdReadOnlyMemoryBufferStream *ms = new TIdReadOnlyMemoryBufferStream(&myData[0], myData.Length);
    try {
        tcpClient1->IOHandler->Write(ms);
    }
    __finally {
        delete ms;
    }
    

    Or:

    {
    auto ms = std::make_unique<TIdReadOnlyMemoryBufferStream>(&myData[0], myData.Length);
    tcpClient1->IOHandler->Write(ms.get());
    }
    

    Otherwise, the final solution is to just copy the data into a TIdBytes, eg:

    {
    TIdBytes bytes;
    bytes.Length = myData.Length;
    
    memcpy(&bytes[0], &myData[0], myData.Length);
    or:
    std::copy(myData.begin(), myData.end(), bytes.begin());
    
    tcpClient1->IOHandler->Write(bytes);
    }