Текущий архив: 2005.11.13;
Скачать: CL | DM;
Вниз
Переслать несколько больших файлов Client/Serv Socket Найти похожие ветки
← →
kami © (2005-07-23 20:31) [0]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, но предполагается работа в сервисе, так что отпадает(?).
Как можно подождать, когда все-таки освободится передаваемый поток? или как можно поступить по другому?
← →
tesseract © (2005-07-23 20:58) [1]Передавай файл небольшими блоками примерно по 1500 бит (Ethernet) Используй UDP и проверяй их контрольную сумму - UDP быстрее но CRC не поддерживает
← →
kami © (2005-07-23 21:12) [2]2 [1] tesseract ©
А смысл? в Socket.SendStream тоже идет передача буферами по 4096 байт, так что в принципе то на то и выйдет. Посмотрел "первоисточники" - практически SendBuf = 1 цикл SendStream. Хотя, можно попробовать.
UDP использовать не буду при всем желании - все давным-давно, включая несколько своих классов, завязано именно на Client/ServerSocket :(, кроме того, говорят, у UDP пакетов есть плохая привычка теряться.
← →
vers © (2005-07-23 21:21) [3]Хе.
А я Indy посоветую :)
В десятой например так: (в клиенте)IdTCPClient.Socket.WriteFile("c:\filename.ext");
или также потоком можно передать.
Передается довольно быстро - 10-11 Мбайт/сек в 100мбит-локалке.
PS: Ethernet вроде 1500 байт?
← →
vers © (2005-07-23 21:29) [4]Насколько большой файл? Попробую потестить.
← →
kami © (2005-07-23 21:34) [5][3] vers ©
Хе.
Сейчас сам себе напоминаю тех, кто задает вопрос, а советов просто не воспринимает :)), так что ветка уходит в пустую...
Чтобы не быть голословным, несколько шатких аргументов:
1. С Indy, если не считать idUDP не работал, да и то, только чтобы узнать IP сервера
2. Опять-таки, переделывать все, что нажито непосильным трудом - лень, если честно, ведь есть извечный принцип - работает - не трожь, а оно все работает, за исключением этого случая :)
3.Это заново возиться над отладкой с незнакомыми компонентами(ффууу.., хотя и понимаю, что с Indy нужно познакомится поближе, но уж лучше с нового проекта)
4. Увеличится размер программы, и немало (слабо волнует, но все же)
← →
kami © (2005-07-23 21:36) [6]2 [4] vers ©
2 файла по 3 с небольшим метра (.mp3), WSAEWOULDBLOCK возникает без отладки на примерно 100 кб, с отладкой - 600-800 кб
← →
kami © (2005-07-23 21:37) [7][6] kami ©
имеется ввиду при передаче первого файла
← →
Alexander Panov © (2005-07-23 22:17) [8]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;
← →
Alexander Panov © (2005-07-23 22:18) [9]Ну и забыл добавить Src.Free в концею
← →
Alexander Panov © (2005-07-23 22:31) [10][8] и [9] не читать.
Это я кусок с работы у себя выдрал, и неверно.
Сейчас сделаю рабочий пример.
← →
Alexander Panov © (2005-07-24 02:15) [11]Обещанный пример:
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.
← →
vers © (2005-07-24 07:53) [12]
> kami ©
Отправлял 20 восьмимегабайтных mp3-шек вышеуказанным тобой способом. Все отправились без ошибок (отправлял на localhost, на виртуалку и на другой комп в локалке). Посмотрел в SDK, что за ошибка WSAEWOULDBLOCK - означает "Resource temporarily unavailable", написано, что может возникать при попытке чтения, когда читать нечего. Пара вопросов: у тебя сокет блокирующий или нет? Если неблокирующий, то где в цикле ты определишь, что файл уже передался, и можно освобождать fs:TFileStream? Или у тебя для каждого файла создается по одному своему потоку? Если блокирующий - так тормозить же будет при передаче.
PS: А SendStream - это ж вроде тот же SendBuf, только в цикле? Так что без разницы, чем слать потоком или буфером.
← →
atruhin © (2005-07-24 10:33) [13]>>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;
← →
Alexander Panov © (2005-07-25 09:13) [14]vers © (24.07.05 7:53) [12]
PS: А SendStream - это ж вроде тот же SendBuf, только в цикле? Так что без разницы, чем слать потоком или буфером.
При работе с сокетами не совсем так.
← →
Verg © (2005-07-25 10:46) [15]Функцией SendStream нельзя отсылать следующий stream, пока не закончена передача текущего.
← →
kami © (2005-07-25 16:45) [16][15] Verg ©
> Функцией SendStream нельзя отсылать следующий stream, пока
> не закончена передача текущего
Это-то я давно понял, это "нельзя" лежит на самом верху SendStream. В общем, пока вижу выход в том, чтобы сохранять потоки в чем-нибудь типа StringList, отсылать по одному(в смысле, по частям), как [11] Alexander Panov ©, и переданные удалять. Если что-то не так понял, поправьте :)
← →
Alexander Panov © (2005-07-25 17:00) [17]kami © (25.07.05 16:45) [16]
Зачем сохранять потоки?
Нужен всего лишь список файлов.
При окончании передачи одного файла стартуем передачу следующего.
Естественно, нужно немного изменить протокол обмена.
PS.
Потестировал у себя - лучше всего получается установить буфер размером 1024 байта.
← →
Alexander Panov © (2005-07-25 17:08) [18]Alexander Panov © (25.07.05 17:00) [17]
лучше всего получается установить буфер размером 1024 байта.
Хотя нет, все зависит от качества канала.
Например, при локальной передаче размер буфера в 4096,8192 дает наименьшее число коллизий(WSAEWOULDBLOCK).
← →
Verg © (2005-07-25 17:25) [19]
> WSAEWOULDBLOCK
- Это не число коллизий.
Строго говоря, это сообщение ядра, о том, что вызов данной функции при данных обстоятельствах привел бы к блокировке потока, если бы режим сокета был бы блокирующим, т.к. нет возможности принять в обработку ни единого байта из указанных в параметрах это ф-ции.
Причин тому - вагон и маленькая тележка...
> пока вижу выход в том, чтобы сохранять потоки в чем-нибудь
> типа StringList, отсылать по одному(в смысле, по частям),
> как [11] Alexander Panov ©, и переданные удалять.
Это есть правильно. Только не нажо все так усложнять. Достаточно помнить, что момент окончания передачи потока ф-цией SendStream однозначно детектируется вызовом деструктора этого потока. Деструктор - ессно виртуальный. Делай выводы...
← →
kami © (2005-07-25 18:49) [20]Строго говоря, передаются-то не одни файлы, а еще много другой информации в MemoryStream по запросу сервера(файлы, само собой FileStream), причем когда что передавать - решает сервер. Естественно, сохранить список только имен файлов было бы великолепно, но - неполучится.
А насчет перекрытия деструктора - здОрово, щаз буду думать и делать :)
← →
Alexander Panov © (2005-07-25 19:15) [21]Verg © (25.07.05 17:25) [19]
- Это не число коллизий
Это понятно. я имел ввиду количество возникающих ситуаций "занятости" сокета при передаче больших объемов информации.
← →
kami © (2005-07-25 21:46) [22]Спасайте...После наряда голова не работает, а хочется...
Вообще запутался, не могу придумать, как это использовать. Нет, с перекрытием деструктора проблем никаких, но вот что с ним делать - не соображу :(
В общем, на данный момент ситуация такая:
На стороне сервера каждому CustomWinSocket сопоставлен мой экз.класса(runtime-created); вызываемый при OnClientRead и при необходимости передачи клиенту информации. Он "единолично" отвечает за прием/передачу, и "родителю" отдает полностью принятый Stream и его хедер.На стороне клиента - точно такой же, но, ессно, один.
При получении запроса на передачу какой-либо информации, в StringList заносятся Stream`ы, каждый со своим хедером и вызывается метод передачи инф-и моего компонента.
С самим приемом проблем нет никаких - склейка и разбиение по пакетам "разруливается на ура". Не могу придумать организацию передачи, чтобы Stream "предупреждал"(как и кого?), что его уничтожают, и, соответственно, можно начинать передачу следующего.
Пока сделал так(thnks to Alexander Panov © за идею и Verg © за поддержку): при вызове метода пересылки инф-и она просто заносится в StringList(Sorted:=False,Duplicates:=dupAccept), затем вызывается непосредственно процедура пересылки:
1. записываем в сокет части потока [0], пока не скажут "хватит"
2. если поток [0] закончился, удаляем его и продолжаем с нового [0]
3. выходим.
у CustomWinSocket переопределяем событие OnSocketEvent, в котором проверяем, если пришло seWrite, то goto 1.
Пока - работает, но - правильно ли сделано?
Если да, то остается только сказать:
"Да здравствует пиво - главная движущая сила IT индустрии !" (с) Набережных С.
← →
Alexander Panov © (2005-07-25 22:30) [23]kami © (25.07.05 21:46) [22]
С самим приемом проблем нет никаких - склейка и разбиение по пакетам "разруливается на ура". Не могу придумать организацию передачи, чтобы Stream "предупреждал"(как и кого?),
А зачем тебе предупреждать? У тебя есть очередь на отправку. После завершения отправки предудущего файла ты сразу же стартуешь новую отправку.
Честно говоря, было бы лучше, если бы ты описал конечную задачу.
← →
Alexander Panov © (2005-07-25 22:36) [24]В интернете, к сожалению, лежат все примеры кривые(по крайней мере, я не нашел правильного) для реализации сервера в stThreadBlocking.
Если надо, выложу пример снервера и в этом режиме...
Если у тебя идет интенсивный обмен, то скорее всего в режиме stNonBlocking сервер не устроит тебя.
Почему я спросил, какова конечная задача? Потому что непонятно, в каком месте у тебя проблемы.
← →
kami © (2005-07-25 22:55) [25]Сейчас, после глобального тестирования с пересылкой кучи файлов разных размеров, проблем вроде не наблюдаю. Интересно просто было узнать, как оценивают такой подход профи. Да и (главное!) узнать, как все-таки справиться с задачей перекрытием Stream.Destroy по предложению[19] Verg © - сам дойти до этого не могу.
Кроме того, я работаю в stNonBlocking
Ну а конечная задача - http://www.kladovka.net.ru/index.cgi?pid=list&rid=202; основной обмен - при пересылке измененных частей экрана и файлов. Детская игрушка, в которой захотелось поменять протокол обмена и саму систему взаимодействия, как уже описал, чтобы потом на любую программу для локалки можно было кинуть 2 или 1 компонента(в зависимости от задачи) - и практически все готово.
АААаааа..... чуть не забыл !!!
Может ли быть такая ситуация,
From [11] Alexander Panov ©
> Readed := FS.Read(Buf[0],8192);
что данные в потоке еще есть, а ничего не считано?
← →
Alexander Panov © (2005-07-25 23:06) [26]kami © (25.07.05 22:55) [25]
что данные в потоке еще есть, а ничего не считано?
Нет. Возможен только случай, описанный в Help - достигнут конец файла.
При ошибке возникнет Exception.
В [11] только макет. Обязательно нужна обработка ошибок.
kami © (25.07.05 22:55) [25]
Ну а конечная задача - http://www.kladovka.net.ru/index.cgi?pid=list&rid=202; основной обмен - при пересылке измененных частей экрана и файлов. Детская игрушка, в которой захотелось поменять протокол обмена и саму систему взаимодействия, как уже описал, чтобы потом на любую программу для локалки можно было кинуть 2 или 1 компонента(в зависимости от задачи) - и практически все готово.
Тебе нужно реализовать протокол обмена, тогда вопросов об извещении кого-либо не возникнет.
Самые зачатки протокола в [11] есть, но все несколко сложнее.
← →
kami © (2005-07-25 23:17) [27]Так протокол обмена есть (там выложена ранняя-ранняя версия, с тех пор всё поменялось). Единственное, может обработка ошибок не совсем реализована, но даже в той версии глюков при передаче не было - не говоря уже об этой. (правда, тестировалось все в (не знаю, как правильно назвать) домашних сетях без сервера, с 1-2 хабами.
К стати, где можно почитать про это (в смысле, возможные ошибки при передаче и их обработку. Ну, обработку, это понятно, сам реализую - хоть и чайник, но понимаю быстро).
Еще раз повторю - основная задача - кинуть на форму 2 компонента - и забыть о приеме/передаче, заниматься только обработкой данных.
← →
Alexander Panov © (2005-07-26 01:28) [28]kami © (25.07.05 23:17) [27]
Еще раз повторю - основная задача - кинуть на форму 2 компонента - и забыть о приеме/передаче, заниматься только обработкой данных.
Сейчас как раз тоже занимаюсь разработкой таких классов.-)
Свои изыскания выложу, если будет интересно.
← →
kami © (2005-07-26 09:17) [29]Конечно, интересно !
← →
Verg © (2005-07-26 14:43) [30]Посмотри, может поможет
http://webfile.ru/424325
← →
kami © (2005-07-26 17:32) [31]2 [30] Verg ©
Больше всего порадовала организация TMyFileStream, особенно методы Seek&Read - мне даже в голову не приходило так хранить заголовок и оперировать им:))
Единственное - TStringList.Delete(x) почему-то (у меня, по крайней мере) не закрывает файлы (обнаружил в ProcessExplorer), поэтому сперва делаю (Objects[0] as TStream).Free, а потом уже - Delete.
У меня по смыслу примерно такая же организация приема/передачи, за исключением потоков :), осталось только внести некоторые изменения в соотв. с Вашим примером, касающиеся Disconnect`ов и времени, отведенного на прием очередного пакета.
Блин!!!
Посмотрел, как и советовали WSAAsyncSelect - ну и редиски эти Борланды!
в хелпе (из всей литературы пользуюсь только им)
OnWrite : should write information to the socket connection - должен (если переводить буквально, как я и делаю) записать инф-ю в сокет.
MSDN:
FD_WRITE: Socket s ready for writing - сокет(ы) готов к записи.
Согласитесь, разница между "должен" и "можно" - довольно большая, вот я и не представлял, как OnWrite можно использовать до сего времени :(.
← →
Verg © (2005-07-26 18:45) [32]FD_WRITE возникает в двух случаях:
1. Успешное установление TCP соединения. (собтвенно OnWrite -это и есть признак успешно установленного соединения)
2. Сокет способен снова принять данные после случая отказа (WSAEWOULDBLOCK) это сделать.
← →
Verg © (2005-07-26 18:51) [33]
> поэтому сперва делаю (Objects[0] as TStream).Free
Free сделает сам SendStream, так что, твое действие даже врдно, чем просто лишнее.
← →
kami © (2005-07-26 22:01) [34]Да, если пользоваться SendStream, но у меня по примеру [11] Alexander Panov © отсылается буферами, а Delete(0) не делает Objects[0]=nil и, ко всему прочему, если это файловый поток, не освобождает сам файл.
← →
Verg © (2005-07-26 22:10) [35]
> kami © (26.07.05 22:01) [34]
Оно все отсылает буферами - "сутба такой". Будешь ли ползоваться SendStream, который сам это делеает (внутри), то ли как Alexander Panov (снаружи), у котрого некий эквивалент - дело твое. Суть же та же.
← →
kami © (2005-07-30 17:44) [36]Итак, проблема вроде решена (относительно, конечно - всегда найдется какое-нибудь узкое место).
Остался один вопрос, уже озвученный здесь - обработку чего еще (из "плохих" ситуаций) нужно предусмотреть.
На данный момент сделано следующее:
0. Преполнение буфера передачи (спасибо всем !)
1. Таймаут ожидания очередного пакета (спасибо Verg ©)
2. Потеря части инфы по пути (реализована грубо, просто идет пропуск до следующего заголовка)
3. Disconnect, SocketError (c последним пришлось довольно долго помучиться, особенно когда создавал ошибку при передаче нескольких файлов)
4. Разбиение и склейка пакетов .
← →
Масяня © (2005-07-30 18:42) [37]
> tesseract © (23.07.05 20:58) [1]
> Передавай файл небольшими блоками примерно по 1500 бит (Ethernet)
> Используй UDP и проверяй их контрольную сумму - UDP быстрее
> но CRC не поддерживает
лучше через TCP - хоть медленней UDP, зато надежней
← →
kami © (2005-07-30 18:50) [38]2 [37] Масяня ©
Лучше прочитай ветку полностью - UDP я использую только для обнаружения сервера в сети, потом вступают в действие Client/ServerSocket в ctNonBlocking. Более того - это уже пройденный этап, всё протестено неоднократно на файлах разного объема и количества. Теперь меня интересует [36]
PS. Прошу не принять за оскорбление.
Страницы: 1 вся ветка
Текущий архив: 2005.11.13;
Скачать: CL | DM;
Память: 0.6 MB
Время: 0.031 c