Надсилання Файлу Через Неблокуючий Сокет, GuardianeLinks - Форум з програмування на Delphi
Щоб вступити в ряди "Прийнятий кодер" Вам потрібно:Написати 10 корисних повідомлень або тим і Отримати 10 симпатій.Для того хто не хоче втрачати час, може пожертвувати кошти для підтримки сервеса, і вступити до лав VIP на місяць, додаткова інформація в лс.
Заступник Адміністратора

В інтернеті є безліч статей, що описують технологію відправлення та приймання файлу, в тому числі і на даному сайті. Але всі ці статті описують прийом лише одного шматочка даних або повідомлення, що прийшов до клієнта та подальшу обробку цього блоку у процедурі ClientSocket1Read/ServerSocket1ClientRead. Але мені потрібно було відправити файл, в ідеалі будь-якого розміру і все це супроводжувати анімацією прогрес бару. Отже, методи sendtext і sendstream не годилися, т.к. вони не дають можливості візуалізувати процес відправлення файлу шматочками.
//посилання файлу через сокет procedure TForm1.SendFileSocket(fName: string); var nSend : Integer; sBuf: Pointer; begin try // відкриття файлу для читання та подальшого відправлення fs := TFileStream.Create(edt1.Text, fmOpenRead); // курсор на початкову позицію, з якої потрібно надсилати файл fs.Position := 0;
repeat // виділення пам'яті під зчитувані дані GetMem(sBuf, bSize + 1); // читання шматка даних (bSize) з файлу nSend := fs.Read(sBuf^, bSize); // якщо щось прочиталося, то відправляємо клієнту if nSend > 0 then begin ServerSocket1.Socket.Connections[0].SendBuf(sBuf^, nSend); // коригування значень прогресу бару Progress(fs.Position, fs.Size); // затримка інакше будуть втрати пакетів Sleep(SleepTime); end; // звільнення ділянки пам'яті FreeMem(sBuf); Application.ProcessMessages; until nSend 0 then begin ServerSocket1.Socket.Connections[0].SendBuf(sBuf^, nSend);
Якщо було раховано будь-що в буфер sBuf, то можна це «що-небудь» відправити клієнту. У моєму випадку тестування проводилося з одним клієнтом, який селився в ServerSocket1.Socket.Connections[0] , але ви можете організувати, наприклад, відправку файлу всім клієнтам у циклі. Після того, як надіслали дані клієнту, необхідно цю справу візуалізувати на екрані:
Progress(fs.Position, fs.Size); // затримка інакше будуть втрати пакетів Sleep(SleepTime);
Тут використовую затримку, т.к. за відсутності неї бувають ситуації втрати пакетів. Наприклад, на сервері пішло 100% даних, а на клієнті прийшло лише 99%. У разі необхідно дописувати контроль цілісності переданих даних, але це зовсім інша історія.
Після відправлення шматка файлу необхідно звільнити пам'ять функцією FreeMem. Можливо, що можна виділити пам'ять лише раз до циклу відправки та звільнити її вже після виконання циклу, але це вже вам для роздумів.
процесор TForm1.ClientSocket1Read(Sender: TObject; // Socket: TCustomWinSocket); var nRead : Integer; rBuf: Pointer; begin . else // режим отримання файлу begin repeat Socket.Lock; // виділення пам'яті під прийнятий шматок даних GetMem(rBuf, bSize + 1); // зчитування даних nRead = кількість лічених байт nRead := Socket.ReceiveBuf(rBuf^, bSize); // якщо щось вважалося, запис даних у файл if nRead > 0 then begin fs.WriteBuffer(rBuf^, nRead); Gauge1.Progress := fs.Size; end; FreeMem (rBuf); Socket.Unlock; Application.ProcessMessages; until (nRead 0 then begin fs.WriteBuffer(rBuf^,nRead);
Якщо зчитується хоча б 1 байт, то пишемо його у файловий потік (у файл).
Як сказано в посібниках - при передачі шматків даних через сокет можливі ситуації, коли шматок даних прийде в незмінному вигляді, а можливо і його фрагментування або утворення більшого фрагмента даних. Тобто. якщо ми очікуємо на вході блок розміром bSize = 4000 байт, то практично може прилетіти 4500 байт. Я проводив досліди з виведенням розміру прийнятого фрагмента в журнал (RichEdit) і виявлялося, що при жорстко заданому розмірі 8000 байт після декількох секунд відправки даних він (розмір) міг поміняти на 8189 байт. Тому, щоб не орієнтуватися на фіксований розмір блоку, зчитування відбувається у циклі.