А всё просто )
Когда Вы работаете с протоколом TCP, вы не оперируете понятием "пакет": TCP скрывает от Вас тот факт, что сообщение при передаче каким-то образом разбивается на отдельно передаваемые фрагменты.
Причём, уровень шифрования, например, SSL/TLS, большинство библиотек реализуют прозрачно и незаметно для Вас, поэтому Ваша программа может и не знать, что в какой-то момент данные на отправке
зашифровываются, а на приёме
расшифровываются.
Логика отправки или приёма чего-то из TCP-соединения абсолютно такая же, как и при работе с дисковым файлом: есть некоторый поток байт, которые на уровне протокола никак на отдельные сообщения не разделены. Признак "началось новое сообщение" Вам надо придумать самому.
Например, популярной является разметка TLV: Tag-Length-Value
Tag: тип сообщения или некоторый маркер начала нового сообщения. Как правило, это поле фиксированной длины, например, 1 байт.
Length: длина сообщения. Поле фиксированной длины, например, 4 байта.
Value: само передаваемое сообщение переменной длины.
Какое какое именно значение передавать в Length - решать Вам.
Можно сделать так:
Length = длина_поля_в_байтах(Value)
А можно и так:
Length = длина_поля_в_байтах(Tag) + длина_поля_в_байтах(Length) + длина_поля_в_байтах(Value)
Не надо опасаться, что, начав читать из TCP-соединения, Вы начнёте читать его "с середины": протокол гарантирует, что Вы при чтении увидите байты в том же самом порядке, в котором они были отправлены в вашу сторону с той стороны.
Однако, внимание, грабли, на которые часто наступают новички:
1. После того, как Вы прочитали байт из соединения, он пропадает: прочитать его снова Вы уже не сможете. При следующей процедуре чтения Вы прочитаете уже следующий байт, отправленный сразу же после прочитанного Вами в прошлый раз.
2. Будьте готовы к тому, что Вы можете начать читать некоторое сообщение в тот момент, когда его отправка с той стороны уже начата, но ещё не завершена.
Зато, Вы можете в любой момент посмотреть, сколько там байт уже прилетело и накопилось в буфере приёма.
Пример: допустим, Вы ожидаете сообщение длиной 100 байт.
Возможна ситуация, когда Вы прочитали из соединения 50 байт, а затем Вы получаете уведомление "больше данных нет". Это означает, что оставшиеся 50 байт всё ещё где-то в пути; Вам следует периодически повторять процедуру чтения из соединения до тех пор, пока Вы не получите, вероятно, за несколько приёмов, оставшиеся данные.
Получается, на псевдокоде пусть не оптимальная, но зато простейшая логика чтения каждого нового сообщения в разметке TLV может иметь следующий вид:
пока (соединение_не_закрыто):
подождать_пока_в_буфере_приёма_не_окажется_байт(1)
tag = прочитать_байт(1)
подождать_пока_в_буфере_приёма_не_окажется_байт(4)
length = прочитать_байт(4)
подождать_пока_в_буфере_приёма_не_окажется_байт(length)
value = прочитать_байт(length)
обработать_новое_сообщение(tag, value)
Подобная логика позволит Вам нормально обработать ситуацию, когда кто-то отправил Вам два сообщения подряд, но сразу до Вас первое сообщение долетело целиком, а от второго - только первая половина: столько влезло в один TCP-пакет. Вторая же половина второго сообщения в следующем TCP-пакете может прилететь даже и через полчаса.