1. Что это такое. Это режим работы binkp, на 100% защищенный от повторной передачи файлов (и, естественно, от потерь) независимо от того, в какой момент оборвалось соединение. Это достигается за счет некоторого замедления протокола (информация о следующем файле передается только после получения M_GOT на предыдущий), задержки перед переименованием файла в его настоящее имя (это происходит только после получения M_FILE на следующий файл, т.е. когда мы уверены, что на той стороне файл прибит), и сохранения статуса сессий в файлах *.stc. 2. Как оно работает. Протокол binkp оказался очень хорошо адаптированным для введения этой фичи. Не пришлось его менять вообще, меняется только поведение сторон. Основные тезисы: - сессия в ND-mode включается только если обе стороны на нее согласны. То есть если обе стороны сказали OPT ND при handshake. Отвечающая сторона всегда говорит OPT ND, если она поддерживает этот режим. Таким образом, не нужно менять (и даже проверять) версию протокола. - опция ND включает в себя опцию NR. То есть ND без NR не бывает. Вызывающая сторона при handshake говорит "OPT NR ND", отвечающая - просто "OPT ND". - начало передачи следующего файла (offset request) происходит только после приема M_GOT или M_SKIP на предыдущий. - файл на приеме переименовывается только после начала передачи следующего, когда мы точно знаем, что там получили M_GOT и удалили файл. Введено новое поле state.in_complete - полностью принятый файл, который ждет переименования. - на передающей стороне между удалением файла и получением M_GET на следующий в файле *.stc (status-file) сохраняются параметры предыдущего файла - мы не знаем, переименовался ли он на принимающей стороне, и при обрыве сессии в следующий раз мы его предложим (но передавать не будем - нечего, предложим только для переименования). - статус физически сохраняется на диск каждый раз, чтобы не было потерь не только в случае обрыва соединения, но и при сбое питания, скажем. - передача файлов на узел всегда начинается с передачи статуса, независимо от того, включена ли ND- или NR-mode. Иначе файл может потеряться (не переименоваться) при выключении ND-mode. 3. Некоторые подробности реализации. Все было бы совсем просто, если бы у remote было всего одно AKA. Тогда status можно было бы передавать всегда в начале сессии, и забыть о нем. В случае нескольких AKA пришлось обрабатывать status-файлы на этапе сканирования outbound (модуль ftnq); появились новые поля state'а: cur_addr (адрес, для которого в данный момент передается почта) и ND_addr (адрес, для которого мы будем обновлять status - он несколько запаздывает по отношению к cur_addr). Если remote согласился принимать файл, указанный в status (т.е. он его уже переименовал и считает этот новым), то мы не можем просто так отказаться его передавать и перейти к следующему файлу - на remote не совпадет GET_FILE_balance. Поэтому нам надо всегда на M_GET отвечать "M_FILE name offset time 0", и только потом переходить к новому файлу (remote скажет "file transfer interrupted", и все будет нормально). Очень помогает двойной EOB при окончании сессии на binkp/1.1. Это дает нам возможность убедиться в переименовании последнего файла без дополнительных сообщений протокола. Было большое искушение не удалять файл на передающей стороне до его переименования на принимающей, а в статусе сохранять информацию о том, что этот файл передавать не нужно (только предложить). В этом случае при удалении статуса или невозможности его сохранить может возникнуть дуп (как на обычном -NR), но не будет потери файла. Но от этого пришлось отказаться: если файл лежит в outbound, туда может быть что-то допаковано, и его прийдется передавать опять, т.е. возникнут те же дупы. Поэтому при невозможности сохранения статуса приходится разрывать сессию, а при его удалении ручками файл может не быть переименован принимающей стороной. Что ж, при удалении самого файла тоже возможна его потеря - файл *.stc ничем не хуже. ;-) Можно было бы сделать режим защиты от дупов и без замедления протокола (при -NR), за счет того, что перед передачей файла идет две посылки (запрос смещения и собственно начало передачи): - при получении M_GOT мы выставляем status и удаляем файл; - при получении "M_FILE 0" мы переименовываем файл, поскольку знаем, что remote получил ответ на offset request, а значит, и M_GOT от предыдущего файла; - при получении M_GOT мы можем удалить предыдущий статус, потому что знаем, что принят следующий файл, а значит, было принято сообщение M_FILE о нем, т.е. предыдущий был переименован. Недостаток этого метода заключается в том, что status всегда непустой, т.е. следующая сессия всегда будет начинаться с передачи fake-файла, даже если предыдущая сессия была завершена успешно (иначе нужно делать еще один цикл подтверждений в конце сессии, т.е. изменение самого binkp) В общем, я от этого способа решил отказаться. Пусть лучше медленнее, но без непонятных юзеру действий binkd и без толпы файлов .stc в outbound (в том, что реализовано, файл .stc появляется достаточно редко, только при обрыве сессии между получением M_GOT на один файл и M_GET на следующий). 4. Недостатки. - замедление работы протокола в режиме ND. Протокол в режиме ND работает примерно настолько же медленнее режима NR, насколько режим NR медленнее нормального режима; - отъедается чуть больше памяти (увеличился размер структуры state); - при ответе на входящее соединение всегда передается "OPT ND", показывающая, что мы поддерживаем этот режим - она может удивлять (а может и рекламировать), а кто-то скажет, что это избыточный траффик ;-) - очистка статуса последнего переданного файла происходит только непосредственно перед окончанием сессии, т.е. после полной передачи всех файлов в _обе_ стороны. 5. Грабли (выявлены в процессе тестирования): - файл передался, от него есть статус, на принимающей стороне ждет переименования, произошел обрыв, и при следующей сессии оказалась bsy на главное aka передающей стороны. Тогда сессия проводится с вторым AKA (но та сторона об этом не знает), принимается статус, расценивается как другой файл (from-address ведь уже не тот), подтверждаем прием, передающая сторона думает, что раз хотят принимать с начала, значит, уже переименовали, и со спокойной совестью статус удаляет. Решение: при приеме файла проверять все предъявленные aka, в т.ч. занятые. - возможна ситуация, когда файл есть только в "недокаченном" (а на самом деле, непереименованном) виде, когда переименование планируется при передаче статуса. Если же другой линк предложит файл с тем же именем, то при kill-dup-partial-files "недокаченный" файл будет прибит. Решение: при kill-dup-partial-files прибивать только файлы с тем же именем от того же линка, а не все файлы с таким именем. - опция ND не включает в себя NR, ведь при ответе всем говорится ND, и если это включает в себя NR, то все сессии будут проводиться в режимах ND или NR, независимо от желания "звонящего". То есть, для корректной работы, опция ND может предъявляться только при binkp 1.1 или выше, и звонящий может предъявлять ND только вместе с NR, но не саму по себе. Кстати, если при ответе говорить "OPT NR", то можно всем звонящим принудительно навязывать NR-mode (если они это умеют, конечно) - интересная фича. ;-) - при согласии принимать файл с начала нельзя предлагать сразу следующий файл из-за внутренних счетчиков binkd. Нужно сначала сообщить, что мы будем передавать файл с начала, и только после этого переходить к следующему файлу, тогда это воспринимается нормально. 6. Пример сессии в режиме ND (рассмотрена только передача в одну сторону): master slave #if status=="X size time" >> M_FILE X