Some tips for SPI protocols: Make sure to have a way to clear the buffer/data if invalid data is received (either a timer to clear received data, or a start/stop sequence to signal new messages). You will also need a way to send the default bus value (0x00 or 0xFF depending on your default logic level). An escape character to signal that the next default bus value is valid is the approach I took, and was used in ASCII as well.
For implementing TCP, take a look at the 7-layer OSI architecture, as it helps lay out and define each layer of the connection with its functionality: TCP/IP vs. OSI: What’s the Difference Between them?
Make sure to build in error handling for communications on the TCP/IP stack as well.