У меня есть программа на C ++, которая читает кадры с высокоскоростной камеры и записывает каждый кадр в сокет (сокет unix). Каждая запись имеет 4096 байтов. Каждый кадр примерно 5 МБ. (Нет гарантии, что размер кадра будет постоянным, но он всегда кратен 4096 байтам.)
Существует скрипт Python, который читает из сокета: 10 * 4096 байт при каждом вызове recv
, Часто я получаю неожиданное поведение, которое, я думаю, сводится к тому, чтобы понять следующее о сокетах. Я считаю, что обе мои программы write
/recv
в режиме блокировки.
В идеале я хотел бы, чтобы мое приложение записывало в сокет как можно быстрее. Если никто не читает данные, тогда перезапись — это хорошо. Если кто-то читает данные из сокета, но недостаточно быстро, я хотел бы сохранить все данные в буфере. Тогда как я могу заставить свой сокет увеличить размер буфера при медленном чтении?
Могу ли я написать весь кадр за один раз (написать вызов с 5 МБ данных)? Это
рекомендуемые? Скорость является главной проблемой здесь.
Ну можно конечно пытаться, но не удивляйтесь, если вызов socket.send () отправит только часть байтов, которую вы попросили отправить. В частности, вы всегда должны проверять возвращаемое значение socket.send (), чтобы увидеть, сколько его байтов на самом деле принят от вас, потому что это значение может быть больше нуля, но меньше количества байтов, которые вы передали в вызов. (Если оно меньше, то вам, вероятно, понадобится снова вызвать socket.send (), чтобы отправить из буфера оставшиеся байты, которые не были обработаны первым вызовом … и повторить при необходимости; или же вы можете вызвать socket.sendall () вместо socket.send (), и это сделает для вас необходимый цикл и повторный вызов команды socket.send (), так что вам не нужно об этом беспокоиться … компромисс является то, что socket.sendall () может не возвращаться в течение длительного времени, в зависимости от скорости вашего сетевого подключения и количества данных, которые вы указали socket.sendall () для отправки)
Обратите внимание, что при отправке дейтаграмм обычно применяются максимальные размеры пакетов; пакеты большего размера либо будут фрагментированы на более мелкие пакеты для передачи (и, будем надеяться, повторно собраны на принимающей стороне), либо могут быть просто отброшены. Например, при отправке пакетов UDP через Ethernet, обычно МТУ 1500 байт. При отправке через сокет Unix MTU, вероятно, будет больше, чем это, но, вероятно, все еще будет предел.
Если клиент Python не может читать или читать медленнее, чем писать, значит ли это
что через некоторое время операция записи в сокет не добавит
буфер? Или они перезапишут буфер? Если никто не читает
сокет, я не против перезаписать буфер.
Если вы отправляете через сокет в стиле потока (SOCK_STREAM), то медленный клиент вызовет блокировку вызовов send () вашего сервера, если / когда буфер заполнится. Если вы отправляете через сокет в стиле дейтаграммы (SOCK_DGRAM) и буфер заполняется, дейтаграммы «переполнения» будут просто отброшены.
Тогда как я могу заставить свой сокет увеличить размер буфера, когда
чтение медленное?
Вы можете установить размер буфера отправки сокета через socket.setsockopt (SOL_SOCKET, SO_SNDBUF, xxx). Обратите внимание, что это обычно делается заранее (например, сразу после создания сокета) вместо того, чтобы пытаться сделать это «на лету» в ответ на медленное чтение.
Это звучит как недостаток дизайна, что для начала вам нужно отправить столько данных через сокет, и есть риск того, что читатель не поспевает за автором. В качестве альтернативы вы можете рассмотреть возможность использования дельта-кодирования, когда вы чередуете «ключевые кадры» (целые кадры) и несколько кадров, закодированных как дельты из предыдущего кадра. Возможно, вы также захотите записать данные в локальный буфер, а затем в своем сокете домена UNIX реализовать собственный протокол, который позволяет считывать последовательность кадров, начинающуюся с заданной временной отметки или одного кадра с заданной временной отметкой. Если все чтения проходят через такой буфер, а не напрямую из источника, я думаю, вы могли бы также добавить дополнительные опции кодирования / сжатия в этот протокол. Кроме того, если серверное приложение, которое экспортирует данные в сокет UNIX, является отдельным приложением от того, которое считывает данные и записывает их в буфер, вам не нужно беспокоиться о блокировке приема данных медленными читателями. ,