![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Переслать несколько больших файлов Client/Serv Socket [D5, WinXP]
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 не поддерживает