TGStat
TGStat
Type to search
Advanced channel search
  • flag English
    Site language
    flag Russian flag English flag Uzbek
  • Sign In
  • Catalog
    Channels and groups catalog Regional compilations Thematic compilations Платные каналы Search for channels
    Add a channel/group
  • Ratings
    Rating of channels Rating of groups Posts rating
    Ratings of brands and people
  • Analytics
  • Search by posts
  • Telegram monitoring
  • Promotion
    Advertising through Yandex Business Advertising in channels through TGStat Agency Advertising on TGStat.ru website
Код на салфетке

9 Sep, 13:11

Open in Telegram Share Report

Знаете ли вы, почему TCP не сохраняет границы сообщений?

Клиент отправляет hello и world двумя отдельными вызовами sendall(). На тестах кажется, что сервер сможет получить их двумя соответствующими вызовами recv(), но TCP такой связи не гарантирует. Он передает упорядоченный поток байтов, а каждый recv() возвращает доступную часть этого потока в пределах указанного размера.

import socket

server = socket.create_server(("127.0.0.1", 0))
client = socket.create_connection(server.getsockname())
connection, _ = server.accept()

client.sendall(b"hello")
client.sendall(b"world")
client.shutdown(socket.SHUT_WR)

data = b""
while chunk := connection.recv(1024):
data += chunk

print(data)
# b'helloworld'

Получатель видит десять байтов подряд. В потоке нет информации о том, были они отправлены как hello и world или разбиты между вызовами иначе. Кроме того, один большой send() может потребовать нескольких чтений, потому что recv(1024) возвращает максимум 1024 байта.

Границы добавляет протокол приложения. Например, перед сообщением можно передать его длину:

import socket
import struct

def receive_exactly(sock, size):
data = b""
while len(data) < size:
chunk = sock.recv(size - len(data))
if not chunk:
raise EOFError("connection closed")
data += chunk
return data

def receive_message(sock):
size = struct.unpack("!I", receive_exactly(sock, 4))[0]
return receive_exactly(sock, size)

server = socket.create_server(("127.0.0.1", 0))
client = socket.create_connection(server.getsockname())
connection, _ = server.accept()

for message in (b"hello", b"world"):
client.sendall(struct.pack("!I", len(message)) + message)

print(receive_message(connection))
print(receive_message(connection))
# b'hello'
# b'world'

Первые четыре байта задают длину следующего сообщения. receive_exactly() продолжает читать поток, пока не соберет нужное количество байтов, поэтому код работает при любом разбиении данных между вызовами recv().

TCP гарантирует порядок байтов, а границы сообщений определяет протокол приложения: длиной, разделителем или форматом с собственным фреймингом.

Код на салфетке x Кусочки кода

502 2 3 4 5
Catalog
Channels and groups catalog Channels compilations Search for channels Add a channel/group
Ratings
Rating of Telegram channels Rating of Telegram groups Posts rating Ratings of brands and people
API
API statistics Search API of posts API Callback
Our channels
@TGStat @TGStat_Chat @telepulse @TGStatAPI
Read
Академия TGStat Telegram Research 2019 Telegram Research 2021 Telegram Research 2023
Contacts
Справочный центр Support Email Jobs
Miscellaneous
Terms and conditions Privacy policy Public offer
Our bots
@TGStat_Bot @SearcheeBot @TGAlertsBot @tg_analytics_bot @TGStatChatBot