Появилась необходимость в передачи файлов по локальной сети, с одного компьютера на несколько других. Собрал вот такую схему code_34122.txt
Только ни как не могу понять как после приема данных из порта сохранить полученный файл на компьютер. Файлы планирую передавать разных форматов и размеров, например txt, exe, dll. Может кто то подобное уже делал? И с помощью каких компонентов в hiasm это лучше реализовывать?
Этот топик читают: Гость
Ответов: 48
Рейтинг: 0
|
|||
карма: 0 |
| ||
файлы: 1 | code_34122.txt [2KB] [342] |
Ответов: 4631
Рейтинг: 749
|
|||
Как мы уже обсуждали, протокол TCP и существующие у нас компоненты не предусматривают простой передачи файлов. Требуется протокол более высокого уровня.
Могу предложить такой вариант. На управляющем компьютере настраиваешь веб-сервер (Apache). Размещаешь нужные файлы для доступа из сети. Клиентам отправляешь простые команды "скачать файл по такому-то адресу". Скачиваешь файлы компонентом HTTP_Get. [offtop]В качестве анонса. Планирую сделать компонент на базе самодельного протокола, который будет иметь возможность передавать файлы и строки по сети с полезными возможностями (типа предупреждение другой стороны о начале/конце передачи, размере данных, возможности прерывать передачу). При этом хочу реализовать некий "стек протоколов": когда один компонент (протокол) является транспортом для другого. Скажем этот компонент отправки файлов использует в качестве транспорта TCP. Можно заменить транспорт, например, на MailSlot. Можно между ними вклинить какой-нибудь другой протокол, например, шифрование SSL[/offtop] |
|||
карма: 26 |
|
Разработчик
Ответов: 4698
Рейтинг: 426
|
|||
Netspirit писал(а): Как мы уже обсуждали, протокол TCP и существующие у нас компоненты не предусматривают простой передачи файлов.Немного не понял: почему? Считал аттрибуты, передал аттрибуты, на той стороне создал файл с такими аттрибутами, взял содержимое файла, передал на ту сторону и записал там на диск. А если атрибуты не важны, так тогда вообще в три шага - считал, передал, записал. Чем TCP не годится то? |
|||
карма: 10 |
|
Ответов: 4631
Рейтинг: 749
|
|||
Я, например, не представляю, как это сделать в режиме DataType=dtStream. Как это можно сделать в режиме dtString я описывал в теме о новых TCP компонентах.
Основная сложность - отслеживание начала-конца данных. То-есть: шлешь, например, команду "отправляю файл такого-то размера" ("атрибуты"). Затем начинаешь отправлять файл. На принимающей стороне происходит серия событий onRead. Причем, эти события выдают данные определенными порциями, размер которых нельзя контролировать. Принимающая сторона получает первую порцию, которая содержит "команду"+первую часть файла (нет гарантии, что 2 вызова doSend дадут два события onRead). Значит, уже нужно как-то отделять команду от собственно файла. Например, разделителем, или фиксированной длиной команды. Соответственно, в обработчике onRead нужен сложный накопитель: накапливаем данные, пока не получим разделитель. Парсим команду. Переключаем ветку накопителя, чтобы он последующие принятые данные считал содержимым файла и сохранял на диск. Как только весь файл указанного размера получен, нужно переключить накопитель опять на ожидание команды. Если я не ошибаюсь, то реализация этого на дискретных компонентах весьма громоздкая. Опять же, существующий DataToFile не приспособлен для чтения-записи файлов порциями. И если для отправки можно использовать StreamToString, то прием - с костылями. У кого есть другие теоретические/практические соображения - выкладывайте. |
|||
карма: 26 |
|
Разработчик
Ответов: 4698
Рейтинг: 426
|
|||
На dtStream:
code_34123.txt Скрин схемы ------------ Дoбавленo в 23.13: Можно еще и сразу целиком файл в StreamPack отправлять (тогда схема совсем простая), но это не есть хорошо, файлы могут быть и больше нескольких мегабайт. |
|||
карма: 10 |
| ||
файлы: 1 | code_34123.txt [4KB] [331] |
Ответов: 48
Рейтинг: 0
|
|||
Assasin Спасибо большое за наглядный пример. Испытал в действии, работает отлично. Единственное, отправлял файл в 9мб. первый раз выбило с ошибкой, второй раз передал, но если сравнивать по свойствам файла то размер у переданного немного меньше чем у оригинала
|
|||
карма: 0 |
|
Разработчик
Ответов: 4698
Рейтинг: 426
|
|||
ruin писал(а): Единственное, отправлял файл в 9мб. первый раз выбило с ошибкой, второй раз передал, но если сравнивать по свойствам файла то размер у переданного немного меньше чем у оригиналаВот это странно. На сколько точно отличаются размеры? Я тестировал на файлах 2МБ |
|||
карма: 10 |
|
Ответов: 4631
Рейтинг: 749
|
|||
Глянул коды. Как я понял, при dtStream, первые 4 байта указывают на размер передаваемого файла.
Ну, тогда можно использовать такой подход. Код чтения в сервере отличается от кода в клиенте, может из-за этого ошибка. Assasin, не совсем понял, зачем в твоей схеме три связи на отправку. Как мне кажется, нужно сначала сформировать поток в памяти с упаковкой всех необходимых атрибутов, затем один раз выполнить doSend. |
|||
карма: 26 |
|
Разработчик
Ответов: 26170
Рейтинг: 2127
|
|||
Netspirit писал(а): нужно сначала сформировать поток в памятиА тебе не кажется, что размер памяти ограничивает размер передаваемого файла |
|||
карма: 22 |
|
Разработчик
Ответов: 4698
Рейтинг: 426
|
|||
Netspirit писал(а): Assasin, не совсем понял, зачем в твоей схеме три связи на отправку. Как мне кажется, нужно сначала сформировать поток в памяти с упаковкой всех необходимых атрибутов, затем один раз выполнить doSend.Netspirit, я использую что-то типа самопального протокола: 1. Сначала отправляется пакет с id = 0 и именем файла (сюда же можно атрибуты) 2. Отправляются куча последовательных пакетов с id = 1, куском данных (4096 байт или меньше) и размером переданного куска (чтобы если до конца файла меньше 4кб, то принимающая сторона не заполнила нулями остаток файла у себя) 3. Отправляется пакет с id = 2, означающий конец передачи файла. Netspirit писал(а): Глянул коды. Как я понял, при dtStream, первые 4 байта указывают на размер передаваемого файла.Да, только не файла, а потока данных. Передавать можно что угодно, но вот этот тип dtStream - это уже можно сказать протокол поверх TCP. Вообще как сейчас сделаны компоненты для работы по протоколу TCP мне не очень нравится: различные типы передачи данных (dtString, dtStream, dtInteger) мне кажутся больше похожими на костыли. Я думаю, не стоило делать еще один слой абстракции, а сделать просто три компонента: TCP_Client, TCP_Server (лучше реализацией контейнера, как в TCP_ServerEx), StreamRW. Первые два просто бы работали чисто с протоколом TCP и предоставляли пользователю по нижней точки безразмерный поток данных (Stream), с которым уже можно работать с помощью StreamRW (что-то вроде DataToFile, только для безразмерных потоков данных). Как раз StreamRW и должен был бы считывать/записывать различные типы данных (строки, штатные Stream, числа и т.д.). ------------ Дoбавленo в 13.14: Интересные там в клиенте строчки кстати:
|
|||
карма: 10 |
|
Ответов: 4631
Рейтинг: 749
|
|||
nesco писал(а): А тебе не кажется, что размер памяти ограничивает размер передаваемого файла?- во-первых, идет отправка только из st.Memory, следовательно поддерживается только MemoryStream; - во-вторых, весь объем отправляется за раз, а в TSocket.Send нет разбиения на меньшие порции. Это приводит к тому, что если MemoryStream и может иметь размер несколько сотен Мб, то передать можно не больше нескольких мегабайт (или десятков Мб) - у сокета есть свой буфер, переполнение которого даст ошибку отправки. Assasin писал(а): предоставляли пользователю по нижней точки безразмерный поток данных (Stream), с которым уже можно работать с помощью StreamRW- полностью "асинхронный" режим, когда ожидание и отправка данных "независимы", как сделано у нас благодаря событию onRead. Удобно применять в программах типа чатов, которые держат постоянное соединение, чтобы получать уведомления с серверов. - "диалоговый" режим, когда программа работает в режиме запрос-ответ: одна сторона отправляет данные, затем переключается в ожидание ответа. И принятые данные будут интерпретированы в зависимости от последней посланной команды. Так работают протоколы типа HTTP, FTP, SMTP. Вот этот режим удобно реализовать компонентом SocketRW. Если же приём будет вестись, как у нас, через событие onRead, то компонент SocketRW можно будет применять только для отправки данных. Assasin писал(а): я использую что-то типа самопального протоколаdoSendString
doSendStringAsync doSendFile doSendFileAsync doSendStream doSendStreamAsync doAbortSend doRejectRecv onSend onSendAborted - передача отменена другой стороной onBeforeString - будет принята строка. Точка Length Содержит длину строки. Можно отказаться от приема через doRejectRecv onBeforeFile - аналогично onBeforeString, можно указать имя для сохранения. Можно предусмотреть получение оригинального имени. onBeforeStream - аналогично onBeforeString onString - выдаёт принятую строку onFile - происходит после получения файла onStream onErrorSend onErrorProto - неправильные ответы другой стороны Данные: Stream - поток, куда будет записан принятый поток. OutFileName - имя файла для сохранения. Свойства: Length - длина принимаемых данных Assasin писал(а): Это получается, что если мы принимаем integer или realAssasin писал(а): если нам придет больше одного значения за раз, то остальные мы потеряемwhile count > 0 do
begin end; |
|||
карма: 26 |
|
Разработчик
Ответов: 4698
Рейтинг: 426
|
|||
Netspirit писал(а): А case _prop_DataType of нужно ставить внутрь циклаНе, не поможет: придет нам буфер размером 7 байт, а мы работаем в режиме dtInteger - первый раз считаем нормально, а второй раз попадется один мусорный байт (если вообще ошибка доступа к памяти не возникнет). |
|||
карма: 10 |
|
Ответов: 48
Рейтинг: 0
|
|||
Assasin писал(а): Вот это странно. На сколько точно отличаются размеры? Я тестировал на файлах 2МБ |
|||
карма: 0 |
|
Ответов: 4631
Рейтинг: 749
|
|||
Assasin писал(а): Не, не поможетЯ даже сделал соответствующий компонент, чтобы обрабатывать приходящие данные на уровне схемы. Например, можно накапливать данные, пока не будет найден некий разделитель. Данные до разделителя выдаются, данные после остаются в накопителе. Можно переключить накопитель, чтобы он выдавал данные и генерировал событие после указанного количества данных. Таким образом можно было послать размер файла, а после разделителя - содержимое. Переключая режимы накопителя можно разбирать такие протоколы. Таким же образом можно обрабатывать HTTP ответы: до разделителя #13#10#13#10 - заголовки, после - тело. Получили заголовки, можем решать, что делать с телом. В примерах HTTP через TCP мы обычно накапливаем ответ аж до разрыва соединения, а только потом разбираем его. Что не очень подходит для закачки больших файлов и работы с постоянным соединением. До практического применения компонента пока не дошел. |
|||
карма: 26 |
|
14