Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Переслать несколько больших файлов Client/Serv Socket [D5, WinXP]


Страницы:   1 2

kami ©   (2005-07-23 20:31:00) 

ClientSocket, получив запрос на пересылку нескольких больших файлов, честно пытается их передать:

var
 FileNames:TStringList;
 fs:TFileStream;
//==================
 for i:=0 to FileNames.Count-1 do
   begin
     //========
     ClientSocket.Socket.SendStream(fs);(упрощенно, но суть та же, если нужно, приведу более полный код)
    end;

Проблема в том, что на середине передачи первого файлового потока во внутренностях ClientSocket.SendStream "вылазит" ошибка WSAEWOULDBLOCK - вроде, действительно, нефатально,  но передача прерывается и происходит возврат в приведенный код => попытка отправить след.файл - и, естественно, безрезультатно, т.к. Socket.FSendStream занят :(

Пока помогает ProcessMessages, но предполагается работа в сервисе, так что отпадает(?).

Как можно подождать, когда все-таки освободится передаваемый поток? или как можно поступить по другому?


Alexander Panov   (2005-07-25 17:08:00) [21]

Alexander Panov ©   (25.07.05 17:00) [17]
лучше всего получается установить буфер размером 1024 байта.


Хотя нет, все зависит от качества канала.
Например, при локальной передаче размер буфера в 4096,8192 дает наименьшее число коллизий(WSAEWOULDBLOCK).


Alexander Panov   (2005-07-25 17:00:00) [22]

kami ©   (25.07.05 16:45) [16]

Зачем сохранять потоки?
Нужен всего лишь список файлов.

При окончании передачи одного файла стартуем передачу следующего.

Естественно, нужно немного изменить протокол обмена.

PS.

Потестировал у себя - лучше всего получается установить буфер размером 1024 байта.


kami   (2005-07-25 16:45:00) [23]

[15] Verg ©
> Функцией SendStream нельзя отсылать следующий stream, пока
> не закончена передача текущего

Это-то я давно понял, это "нельзя" лежит на самом верху SendStream. В общем, пока вижу выход в том, чтобы сохранять потоки в чем-нибудь типа StringList, отсылать по одному(в смысле, по частям), как [11] Alexander Panov ©, и переданные удалять. Если что-то не так понял, поправьте :)


Verg   (2005-07-25 10:46:00) [24]

Функцией SendStream нельзя отсылать следующий stream, пока не закончена передача текущего.


Alexander Panov   (2005-07-25 09:13:00) [25]

vers ©   (24.07.05 7:53) [12]
PS: А SendStream - это ж вроде тот же SendBuf, только в цикле? Так что без разницы, чем слать потоком или буфером.


При работе с сокетами не совсем так.


atruhin   (2005-07-24 10:33:00) [26]

>>WSAEWOULDBLOCK - означает "Resource temporarily unavailable", написано, что может возникать при попытке чтения, когда читать нечего
Несовсем. Возникает при передаче если заполнен внутренний буфер, это некритическая ошибка. Необходимо дождаться события OnWrite, и продолжить передачу.
см. первоисточник
AmountSent := send(FSocket, Buffer, AmountInBuf, 0);
         if AmountSent = SOCKET_ERROR then
         begin
           ErrorCode := WSAGetLastError;
           if ErrorCode <> WSAEWOULDBLOCK then
           begin
             Error(Self, eeSend, ErrorCode);
             Disconnect(FSocket);
             DropStream;
             if FAsyncStyles <> [] then Abort;
             Break;
           end else
           begin
             FSendStream.Position := StartPos;
             Break;
           end;


vers   (2005-07-24 07:53:00) [27]


> kami ©

Отправлял 20 восьмимегабайтных mp3-шек вышеуказанным тобой способом. Все отправились без ошибок (отправлял на localhost, на виртуалку и на другой комп в локалке). Посмотрел в SDK, что за ошибка WSAEWOULDBLOCK - означает "Resource temporarily unavailable", написано, что может возникать при попытке чтения, когда читать нечего. Пара вопросов: у тебя сокет блокирующий или нет? Если неблокирующий, то где в цикле ты определишь, что файл уже передался, и можно освобождать fs:TFileStream? Или у тебя для каждого файла создается по одному своему потоку? Если блокирующий - так тормозить же будет при передаче.
PS: А SendStream - это ж вроде тот же SendBuf, только в цикле? Так что без разницы, чем слать потоком или буфером.


Alexander Panov   (2005-07-24 02:15:00) [28]

Обещанный пример:

unit Unit1;

interface

uses
 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
 Dialogs, StdCtrls, ScktComp, ComCtrls, ExtCtrls;

const
   BUF_LEN=8192*2;

type

 PRecFile=^TRecFile;
 TRecFile=record
   FileName: String;
   FStream: TFileStream;
   Length: Int64;
   Readed: Int64;
   Buf: array[0..BUF_LEN-1] of Char;
 end;

 TForm1 = class(TForm)
   cs: TClientSocket;
   ss: TServerSocket;
   Button1: TButton;
   pb: TProgressBar;
   Memo1: TMemo;
   procedure Button1Click(Sender: TObject);
   procedure FormCreate(Sender: TObject);
   procedure csConnect(Sender: TObject; Socket: TCustomWinSocket);
   procedure csWrite(Sender: TObject; Socket: TCustomWinSocket);
   procedure ssClientRead(Sender: TObject; Socket: TCustomWinSocket);
   procedure ssClientConnect(Sender: TObject; Socket: TCustomWinSocket);
   procedure ssClientDisconnect(Sender: TObject;
     Socket: TCustomWinSocket);
   procedure csError(Sender: TObject; Socket: TCustomWinSocket;
     ErrorEvent: TErrorEvent; var ErrorCode: Integer);
   procedure csDisconnect(Sender: TObject; Socket: TCustomWinSocket);
 private
   { Private declarations }
 public
   { Public declarations }
 end;

var
 Form1: TForm1;
 FS: TFileStream;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
   cs.Port := 5555;
   cs.Address := '127.0.0.1';
   cs.ClientType := ctNonBlocking;
   cs.Open;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
   ss.Port := 5555;
   ss.ServerType := stNonBlocking;
   ss.Open;
   Memo1.Lines.CLear;
   DoubleBuffered := True;
end;

procedure TForm1.csConnect(Sender: TObject; Socket: TCustomWinSocket);
var
   Buf: array[0..8191] of Char;
   Readed: Int64;
   Writed: Int64;
begin
   FS := TFIleStream.Create('z:\Mult\Shrek2_Goblin_1.avi',fmOpenRead);
   pb.Min := 0;
   pb.Max := FS.Size;
   pb.Tag := Trunc(FS.Size/100);
   Memo1.Lines.Add('Connected to '+Socket.RemoteAddress);
end;

procedure TForm1.csWrite(Sender: TObject; Socket: TCustomWinSocket);
var
   Buf: array[0..8191] of Char;
   Readed: Int64;
   Writed: Int64;
begin
   if pb.Position=0 then
   begin
       Memo1.Lines.Add('Send File Size - '+FormatFloat('#,##0',FS.Size)+' bytes');
       Writed := FS.Size;
       Socket.SendBuf(Writed,8);
       Memo1.Lines.Add('Start send File');
   end;

   while pb.Position<=pb.Max do
   begin
       Readed := FS.Read(Buf[0],8192);
       if Readed=0 then
       begin
         pb.Position := FS.Position;
         Memo1.Lines[Memo1.Lines.Count-1] := 'Transferred '+ FormatFloat('#,##0',pb.Position)+' bytes';
         cs.Close;
         Exit;
       end;
       Writed := Socket.SendBuf(Buf[0],Readed);
       if Writed=-1 then
       begin
           FS.Position := FS.Position-Readed;
           Exit;
       end;
       if Writed<Readed then FS.Position := FS.Position-Readed+Writed;
       pb.Position := FS.Position;
       if pb.Position>pb.Tag then
       begin
           pb.Tag := pb.Tag+Trunc(FS.Size/100);
           Memo1.Lines[Memo1.Lines.Count-1] := 'Transferred '+ FormatFloat('#,##0',pb.Position)+' bytes';
       end;
   end;
end;

procedure TForm1.ssClientRead(Sender: TObject; Socket: TCustomWinSocket);
var
   Len: Int64;
   RF: PRecFile;
begin
   Len := Socket.ReceiveLength;
   repeat
       if Socket.Data=nil then
       begin
           New(RF);
           RF^.FStream := TFileStream.Create('ReceivedFile.tst',fmCreate);
           RF^.Readed := 0;
           RF^.Length := 0;
           Socket.Data := RF;
       end
       else RF := Socket.Data;

       if (RF^.Length=0) then
       begin
           if Len<8 then Exit
           else
           begin
               Socket.ReceiveBuf(RF^.Length,8);
           end;
       end;

       RF^.Readed := Socket.ReceiveBuf(RF^.Buf[0],BUF_LEN);
       if RF^.Readed=-1 then Break;
       try
           RF^.FStream.Write(RF^.Buf[0],RF^.Readed);
       except
           Socket.Close;
           Exit;
       end;

   until RF^.Readed=0;
   if RF^.FStream.Size=RF^.Length then Socket.Close;
end;

procedure TForm1.ssClientConnect(Sender: TObject;
 Socket: TCustomWinSocket);
begin
   Socket.Data := nil;
end;

procedure TForm1.ssClientDisconnect(Sender: TObject;
 Socket: TCustomWinSocket);
begin
   if Socket.Data<>nil then
   begin
       PRecFile(Socket.Data)^.FStream.Free;
       Dispose(PRecFile(Socket.Data));
   end;
end;

procedure TForm1.csError(Sender: TObject; Socket: TCustomWinSocket;
 ErrorEvent: TErrorEvent; var ErrorCode: Integer);
begin
   if Sender = ss
       then Memo1.Lines.Add('Server: '+IntToStr(ErrorCode))
       else Memo1.Lines.Add('Client: '+IntToStr(ErrorCode));
   Socket.Close;
end;

procedure TForm1.csDisconnect(Sender: TObject; Socket: TCustomWinSocket);
begin
   Memo1.Lines.Add('File transferred');
   FS.Free;
end;

end.


Alexander Panov   (2005-07-23 22:31:00) [29]

[8] и [9] не читать.
Это я кусок с работы у себя выдрал, и неверно.
Сейчас сделаю рабочий пример.


Alexander Panov   (2005-07-23 22:18:00) [30]

Ну и забыл добавить Src.Free в концею


Alexander Panov   (2005-07-23 22:17:00) [31]

1. Не используй SendStream, используй SendBuf.
 
Схема такая:

В OnWrite:

var
 Buf: array[0..BUF_LEN] of char;
 Src: TFileStream;
 Counter,n,n1: Int64;
begin
 Src := TFileStream.Create();
 Counter := FSrc.Size;
 while Counter>0 do
 begin
   n := Src.Read(Buf[0],8192);
   if n>0 then
   begin
     n1:=Socket.SendBuf(Buf[0],n);
     if n1=-1 then
     begin
       Src.Position := Src.Position-n;
       break;
     end;
     if n1<n then Src.Position := Src.Position-n+n1;
   end;
   Dec(Counter,n);
 end;
 if Counter<=0 then Src.Free;
end;


kami   (2005-07-23 21:37:00) [32]

[6] kami ©
имеется ввиду при передаче первого файла


kami   (2005-07-23 21:36:00) [33]

2  [4] vers ©
2 файла по 3 с небольшим метра (.mp3), WSAEWOULDBLOCK возникает без отладки на примерно 100 кб, с отладкой - 600-800 кб


kami   (2005-07-23 21:34:00) [34]

[3] vers ©
Хе.
Сейчас сам себе напоминаю тех, кто задает вопрос, а советов просто не воспринимает :)), так что ветка уходит в пустую...

Чтобы не быть голословным, несколько шатких аргументов:
1. С Indy, если не считать idUDP не работал, да и то, только чтобы узнать IP сервера
2. Опять-таки, переделывать все, что нажито непосильным трудом - лень, если честно, ведь есть извечный принцип - работает - не трожь, а оно все работает, за исключением этого случая :)
3.Это заново возиться над отладкой с незнакомыми компонентами(ффууу.., хотя и понимаю, что с Indy нужно познакомится поближе, но уж лучше с нового проекта)
4. Увеличится размер программы, и немало (слабо волнует, но все же)


vers   (2005-07-23 21:29:00) [35]

Насколько большой файл? Попробую потестить.


vers   (2005-07-23 21:21:00) [36]

Хе.
А я Indy посоветую :)
В десятой например так: (в клиенте)
IdTCPClient.Socket.WriteFile('c:\filename.ext');
или также потоком можно передать.
Передается довольно быстро - 10-11 Мбайт/сек в 100мбит-локалке.
PS: Ethernet вроде 1500 байт?


kami   (2005-07-23 21:12:00) [37]

2 [1] tesseract ©

А смысл? в Socket.SendStream тоже идет передача буферами по 4096 байт, так что в принципе то на то и выйдет. Посмотрел "первоисточники" - практически SendBuf = 1 цикл SendStream. Хотя, можно попробовать.
UDP использовать не буду при всем желании - все давным-давно, включая несколько своих классов, завязано именно на Client/ServerSocket :(, кроме того, говорят, у UDP пакетов есть плохая привычка теряться.


tesseract   (2005-07-23 20:58:00) [38]

Передавай файл небольшими блоками примерно по 1500 бит (Ethernet) Используй  UDP и проверяй их контрольную сумму - UDP быстрее но CRC не поддерживает


Страницы:   1 2

Наверх

Титульная страница Поиск, карта сайта Написать письмо