Как
Mercury13 уже отметил, TCP даёт вам "виртуальный провод", где уже нет никаких пакетов, и в этом его (TCP) удобство.
В прикладных протоколах поверх TCP всегда так или иначе определяется способ передачи сообщений известной длины, либо же способ определить конец текущего сообщения и начало следующего. Ваш метод, а именно
Решение пришло само собой: перед отправкой самих данных сперва отправлять длину этих самых данных, чтобы сервер знал сколько ему прочитать байт и после скольки байт начнется новый пакет.
является более чем стандартным для двоичных протоколов, и нисколько не костыль. Другого надёжного способа для двоичных данных и не придумаешь (именно поэтому, кстати, нуль-терминатор в Сишных строках - не очень хорошая идея в принципе).
Для текстовых или смешанных протоколов могут использоваться другие подходы. Например, в том же HTTP в простейшем случае заголовки читаются до первого двойного \r\n, а затем читается столько байт, сколько указано в Content-Length.
В вашем случае, если вы передаёте JSON, который всё равно будет подвергаться синтаксическому разбору (т.к. JSON протокол текстовый, а не двоичный, и начало-конец сообщения в нём определяется
синтаксисом, а не заранее переданной длиной), то вы можете воспользоваться одним из соглашений
JSON Streaming. Более удобным и безопасным является
Concatenated JSON, однако тогда используемый вами парсер должен быть достаточно умён, чтобы вовремя оставиться при чтении значения из потока. Тогда вы сможете просто читать подряд из потока все переданные объекты.
P.S. Уже не уверен, что у вас JSON, т.к. у вас одинарная кавычка в сообщении.