Как правильно использовать функцию recv для TCP-чата, python3?

#python-3.x #sockets

Вопрос:

Некоторые источники говорят, что recv должен иметь максимально возможную длину сообщения, например recv(1024):

 message0 = str(client.recv(1024).decode('utf-8'))
 

Но другие источники говорят, что в нем должно быть общее количество байтов принимающего сообщения. Если сообщение «привет»:

 message0 = str(client.recv(5).decode('utf-8'))
 

Как правильно использовать recv()?

Ответ №1:

Некоторые источники утверждают … Но другие источники говорят … сообщение …

Оба источника ошибочны.

Аргумент для recv этого-максимальное количество байтов, которые нужно прочитать одновременно.

С сокетом UDP это размер сообщения, которое нужно прочитать или больше, но recv в любом случае одно сообщение будет возвращено только одно. Если заданный размер меньше, чем сообщение, оно будет удалено, а остальное будет удалено.

С сокетом TCP (случай, о котором вы спрашиваете) в первую очередь нет понятия сообщения, поскольку TCP-это только поток байтов. recv просто вернет количество байтов, доступных для чтения, до заданного размера. В частности, один recv в получателе TCP не обязательно должен совпадать с одним send в отправителе. Это может совпадать, и часто будет совпадать, если объем данных невелик, но нет никакой гарантии, и никогда не следует полагаться на это.

… message0 = str(клиент.recv(5).декодирование(‘utf-8’))

Обратите внимание, что decode('utf-8') прямой вызов возвращаемых данных recv -плохая идея. Сначала нужно убедиться, что все ожидаемые данные считаны, и только потом звонить decode('utf-8') . Если считывается только часть данных, конец считываемых данных может находиться в середине символа, так как один символ в UTF-8 может быть закодирован в нескольких байтах (все, кроме символов ASCII). Если decode('utf-8') вызывается с неполным закодированным символом, это вызовет исключение, и ваш код сломается.

Комментарии:

1. Теперь я в замешательстве, как работает recv, потому что обе мои интерпретации были неправильными. Спасибо, я поищу больше о recv и о том, как он работает с TCP, потому что ваш ответ (в хорошем смысле) был для меня продвинут.

2. @g_odim_3: Предположение, что TCP имеет семантику сообщения, и вывод из этого, что a recv в получателе совпадает с a send в отправителе, к сожалению, является очень распространенной ошибкой. Трагедия в том. что часто кажется, что это работает, а затем, казалось бы, рабочий код отправляется. Который затем неожиданно и случайным образом выходит из строя для некоторых клиентов (нехватка памяти, нестабильность сети, разный MTU из-за использования VPN…), и никто не знает, почему.

3. Это произошло в моем коде. Где вы узнали о розетках? Это могло бы помочь мне освоить основы этих функций и сокетов в целом.

4. @g_odim_3: «Где вы узнали о сокетах?» — ничего особенного. Просто многолетний опыт.