What you need is some form of protocol. Some system that both ends of the communication channel agree on.
Most serial protocols use certain characters to indicate certain things, and indeed the ASCII character set has certain characters earmarked for certain jobs in communication, so it makes sens to use those.
One you can use is EOT - End Of Transmission - character 0x04. Note: there is no such thing as an "end of file character 'EOF'". That's purely a DOS construct.
And yes, you may wonder about a rogue 0x04 in the data stream triggering the end, and you're not the first to think about that. That's why there's character 0x10 - DLE. That's Data Link Escape. Its purpose is to tell the receiving end "The next character I send is a data character. Don't interpret it as a control character.".
So for every 0x04 in the data stream you should send 0x10,0x04 instead. Your receiver code then sees the 0x10 and makes a note that the next character should not be interpreted as a control character, but raw data.
But what, you ask, about there being a rogue 0x10 in the data stream? Isn't that going to cause the same problem?
Yes. And you deal with it in exactly the same way. Just prefix it with 0x10. Since the receiver should then know to always interpret the next character as data, regardless of what it is, it will see the next 0x10 as data.
So as an example, sending the following data stream:
would be done as:
(Added 0x10s highlighted with brackets).
It can be good to use some of the other control bytes as well, such as SOH for Start Of Header, STX for Start of TeXt, ETX for End of TeXt, etc. You can then do such things as tell the receiver where the start of your packet is, and what the different bits of it are. Adding a header with the length and a checksum can also be worthwhile too, for increased reliability and data integrity.