TCP/IP is a stream protocol
TCP/IP is a stream protocol. Think of it as a pipe - data comes in sequence in a continuous stream.
It’s not a packet protocol like say UDP which doesn’t guarantee delivery nor when the packets will arrive.
This means that there is no one to one relationship with the size of the packets of data written by the sender and the packets read by the sender.
So a sending application might call write “ABC” and the receiving application could receive one chunk “ABC” or “A” and “BC” or “AB” and “C” or “A” “B” and “C”. Intermediate applications which pass the TCP/IP packets can choose to fragment or combine packets in any way they see fit, perfectly legally.
So robust TCP/IP applications should have an abstraction layer to assemble whole application chunks of data from the stream and then pass those chunks up to the application.
TCP/IP stacks can also choose to combine packets if they choose (see Nagle algorithm) etc. Routers can break packets into smaller pieces over slow connections which only support smaller maximum transmission units (MTU). The Nagle algorithm is generally something I would disable within a modern socket library because it’s better to let the application logic have control of that.
Any protocol implemented on top of TCP/IP needs to solve the problem of how to understand when a piece of data begins and ends. i.e. a framing problem.
See: