Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Сети";
Текущий архив: 2003.09.01;
Скачать: [xml.tar.bz2];

Вниз

Socket.OnWrite   Найти похожие ветки 

 
Akni   (2003-06-17 14:40) [0]

Ув. Мастера, помогите, пожалуйста, разобраться с событием OnWrite класса TCustomWinSocket.

Например, данные передаюся след. образом:
Var Res: integer;


procedure TForm1.Button1Click(Sender: TObject);
begin
Res:=ClientSocket1.Socket.SendBuf(iBuffer, BufSize);
end;

Если SendBuf() возвращает -1, то это значит, что данные не были переданы, т.к. буфер WinSock занят. И, как я понимаю, через некоторое время, когда освобождается место в буфере, должно возникнуть событие OnWrite, в котором можно повторить передачу данных:

procedure TForm1.ClientSocket1Write(Sender: TObject;
Socket: TCustomWinSocket);
begin
if Res=-1 then ClientSocket1.Socket.SendBuf(iBuffer, BufSize);
end;

Правильный ли это метод для избежания потерь данных при передаче через сокет?


 
Digitman   (2003-06-17 15:14) [1]

Нет, не совсем правильный.

procedure TForm1.ClientSocket1Write(Sender: TObject;
Socket: TCustomWinSocket);
begin
if Res=-1 then
( iBuffer, BufSize) Нет, не совсем правильный.

procedure TForm1.ClientSocket1Write(Sender: TObject;
Socket: TCustomWinSocket);
begin
if Res=-1 then
Res := Socket.SendBuf(iBuffer, BufSize); // здесь тоже нужно фиксировать результат на случай, если между двумя последовательными событиями параметры iBuffer, BufSize изменились (например, ты переопределил буфер (увеличил его и записал в его конец очер.порцию данных, требуемых к передаче)
end;

кр.того, ты не учитываешь результат ваызова SendBuf() >= 0
при таком результате следует либо изменять адрес и размер буфера либо водить понятие "смещение 1-го передаваемого байта в буфере в событии OnWrite() и размер еще непереданных данных в этом буфере", если буфер - фиксированный и не переопределяется тобой


 
Akni   (2003-06-17 16:23) [2]

Спасибо, буду думать дальше


 
Digitman   (2003-06-17 16:37) [3]

во многих случаях предпочтительней использовать SendStream(), дабы не заботиться о логике перераспределения буфера и позиции передаваемых данных в нем

Поток в любой момент времени (между событиями OnWrite) можно наполнять вновь появляющимися данными, требующими передачи. А метод SendStream() сам позаботится о передаче потока и продвижении позиции в нем, если не удается за один вызов этого метода передать все тек.содержимое потока. Кр.того, этот метод сам уничтожит поток, когда передаст полностью все его тек.содержимое. Важно лишь поймать момент, когда этот метод разрушил поток, и создать его вновь, чтобы не схлопотать AV в момент очередного его наполнения. Но это уже - дело простейшей техники)


 
Akni   (2003-06-17 18:04) [4]

Еще один вопрос возник. Нужно передавать через сокеты и данные (напр. структуры), и файлы.
С тем, что до самих данных надо передать их идентификатор и размер, все понятно.

Общая идея такова:
Неблокирующий сокет работает в отдельном потоке, принимая и получая данные.
Потоку посылаются сообщения (PostThreadMessage), указывающие, что нужно передавать те или иные данные.
Если не передавать файлов, то тогда все понятно: можно писать все данные в один MemStream, а дальше сокет будет из него отправлять.
А как поступить, если нужно еще и файлы слать? Ведь при такой логике файл можно передавать только после того, как будет передана «сопровождающая» его информация – размер, имя и т.п.
Пока представляются возможными два варианта:
1. Переписывать файл в MemStream, тогда будет гарантирована правильная последовательность отправки (но при больших размерах файла это никуда не годится)
2. ничего не писать в MemStream после записи туда «сопровождающей» файл информации; проверять, отправлены ли все данные из MemStream’а и потом отправлять файл отдельным потоком Socket.SendStream(TFileStream.Create(filename)). Но при этом все прочие данные, которые должны быть переданы через сокет, как бы «подвисают в воздухе» до тех пор, пока не очистится MemStream

Есть ли какие нибудь более правильные методы для реализации такой задачи?


 
Digitman   (2003-06-18 09:30) [5]

можно поступить так :

транспортному треду посылается сообщение, например, TM_SEND_DATABLOCK со след. параметрами :

wParam = указатель на структуру с именем файла
lParam = ThreadId треда, посылающего данное сообщение

транспортный тред, получив сообщение TM_SEND_DATABLOCK, берет по ссылке в wParam имя/путь файла, самостоятельно определяет его атрибуты, записывает их в MemStream, передает MemStream, далее создает TFileStream, передает его (оба потока передаются за один или несколько приемов с использованием OnWrite)

при успешной или неуспешной (разрыв соединения, таймаут и т.п.) передаче транспортный тред, имея ThreadId треда-отправителя (если он был указан в lParam при TM_SEND_DATABLOCK), извещает его с помощью, например, сообщения TM_DATABLOCK_SENT о результатах передачи, используя wParam и lParam для детализации результатов (успешно или неуспешно, если неуспешно, то - причина)

если тред-отправитель указал в lParam при TM_SEND_DATABLOCK свой ThreadId, то он тем самым известил трансп.тред о том, что он заинтересован в ожидании результатов отправки. Поэтому после успешной отправки TM_SEND_DATABLOCK тред-отправитель организует цикл ожидания/приема/обработки сообщений, выход из цикла - по получению от трансп.треда сообщения TM_DATABLOCK_SENT.

Память под структуру, ссылку на которую тред-отправитель передал ранее в wParam при TM_SEND_DATABLOCK, безусловно освобождает сам тред-отправитель, если lParam = ThreadId (тред-отправитель ждет результатов), в противном случае ее безусловно освобождает трансп.тред (по условию полученного им в TM_SEND_DATABLOCK lParam = 0) после успешной или неуспешной операции передачи.


 
Akni   (2003-06-18 14:42) [6]


> Поэтому после успешной отправки TM_SEND_DATABLOCK тред-отправитель
> организует цикл ожидания/приема/обработки сообщений, выход
> из цикла - по получению от трансп.треда сообщения TM_DATABLOCK_SENT.
>


Если я правильно понимаю Вашу идею, это значит, что тред-отправитель не должен посылать транспортному треду новые сообщения о готовности след. порции данных до тех пор, пока не получит от транспортного треда подтверждения того, что последняя порция данных отправлена?


 
Digitman   (2003-06-18 14:55) [7]


> Akni © (18.06.03 14:42)


ну почему же ? вовсе необязательно.


> тред-отправитель


тред отправитель может сразу "накидать" несколько сообщений TM_SEND_DATABLOCK треду-транспорту с указаниями передать такие-то и такие-то файлы, а затем ждать при необходимости в цикле сообщения TM_DATABLOCK_SENT, анализируя каждое на предмет причины отказа (если таковые возвращены).

дело в том, что передача данных может завершиться неуспешно только при отказе самого транспортного канала (таймаут, разрыв соединения по непредвиденным причинам и т.п.) либо при дисконнекте по инициативе удаленного партнера по соединению

в этом случае транспортный тред должен либо известить спец.сообщением тред-отправитель о том, что транспортный канал разорван, либо завершиться немедленно (тред-отправитель при этом должен контролировать момент завершения трансп.треда, интерпретируя это как факт отказа транспорта)


 
Akni   (2003-06-18 15:40) [8]

Чего-то я все-таки не понимаю...

Представим себе ситуацию, когда нужно подряд отправить два файла.
Тред-отправитель послал трансп. треду соотв. сообщение об отправке первого файла.
Трансп. тред записал в MemStream данные о файле и создал FileStream для этого файла.
FileStream можно передавать только после того, как будет передана информация о файле из MemStream-а.
Но отправить эту информацию полностью трансп. тред не смог по причине занятости WinSocket-ного буфера.
В это время тред-отправитель посылает трансп. треду еще один Message с указанием на то, что должен быть отправлен другой файл (ну или какие другие данные).
Ничем в это время не занятый трансп. тред это сообщение получает. И что делать теперь трансп. треду? Реакцией его на это сообщение должна быть запись файловой информ. в MemStream.. и т.д., точно так же, как и с первым файлом.
Но в MemStream-то писать нельзя, пока он не будет полностью отправлен!


 
Digitman   (2003-06-18 16:21) [9]


> отправить эту информацию полностью трансп. тред не смог
> по причине занятости WinSocket-ного буфера.


а это его (трансп.треда) проблемы !)

треду-отправителю глубоко по барабану. как там трансп.тред выполняет передачу)

а вот трансп.тред, если он работает с неблок.гнездом, должен, как мы с тобой говорили чуть выше, организовать назначение и обработку события OnWrite() !


 
Akni   (2003-06-20 11:22) [10]

С теорией-то все понятно, да вот с реализацией, увы, проблемы...

Пишу след. оработчик сообщения TM_SEND_DATABLOCK:

TISServerThread – потомок TThread
fSendStream - потомок TMemoryStream

procedure TISServerThread.SendData(var Msg: TMessage);
var iCode: integer;
iSize: integer;
DatBlock: TISDataBlock;
CurPos: integer;
begin
if not Assigned(fSendStream)
then fSendStream:=TISMemoryStream.Create(DestroyStream);
try
CurPos:=fSendStream.Position;
fSendStream.Position:=fSendStream.Size;

case msg.wParam of
cdREC: //отправить структуру
begin
DatBlock:=TISDataBlock.Create;
try
DatBlock:=SendQueue.Pop; // DatBlock содержит тип данных, размер и сами данные
fSendStream.WriteBuffer(DatBlock.Data^,DatBlock.BlockSize);
finally
DatBlock.Free;
end;
end;

cdFILE: // отправить файл
begin
fFileStream:=TFileStream.Create(fFileDat.fFileName,fmOpenRead); //FileStream создать
try fFileDat.fSize:=fFileStream.size; //размер файла
//записать в поток структуру с именем и размером файла
iCode:=cdFILE_TOCLIENT;
fSendStream.WriteBuffer(iCode, SizeOf(Integer));
iSize:=SizeOf(ISFileDat);
fSendStream.WriteBuffer(iSize, SizeOf(Integer));
fSendStream.WriteBuffer(fFileDat, SizeOf(fFileDat));
//записать в поток информ. о том, что сейчас должен последовать файл опред. размера
iCode:=cdFILE;
fSendStream.WriteBuffer(iCode, SizeOf(Integer));
iSize:=fFileDat.fSize;
fSendStream.WriteBuffer(iSize, SizeOf(Integer));
except
raise
end;
end;
end;

fSendStream.Position:=CurPos;
//отправить МемСтрим
fServerSocket.Socket.Connections[0].SendStream(fSendStream);
//отправить файл после отправки МемСтрим-а
if assigned(fFilestream) then
if not assigned(fSendStream) then
fServerSocket.Socket.Connections[0].SendStream(fFileStream);

except
FreeAndNil(fSendStream);
if assigned(fFileStream) then FreeAndNil(fFileStream);
PostMessage(fOwnerForm.Handle,wm_RefreshClients,cdERROR,0);
raise ESendError.CreateRes(@SSendError);
end;
end;

в OnWrite пишу следующее:

procedure TISServerThread.SocketWrite(Sender: TObject;
Socket: TCustomWinSocket);
begin
Socket.SendStream(fSendStream); // отправить memStream
if assigned(fFileStream) then //если есть файл на отправку, то отправить его только
if not assigned(fSendStream) then //после того, как был отправлен memStream
( fFileStream) С теорией-то все понятно, да вот с реализацией, увы, проблемы...

Пишу след. оработчик сообщения TM_SEND_DATABLOCK:

TISServerThread – потомок TThread
fSendStream - потомок TMemoryStream

procedure TISServerThread.SendData(var Msg: TMessage);
var iCode: integer;
iSize: integer;
DatBlock: TISDataBlock;
CurPos: integer;
begin
if not Assigned(fSendStream)
then fSendStream:=TISMemoryStream.Create(DestroyStream);
try
CurPos:=fSendStream.Position;
fSendStream.Position:=fSendStream.Size;

case msg.wParam of
cdREC: //отправить структуру
begin
DatBlock:=TISDataBlock.Create;
try
DatBlock:=SendQueue.Pop; // DatBlock содержит тип данных, размер и сами данные
fSendStream.WriteBuffer(DatBlock.Data^,DatBlock.BlockSize);
finally
DatBlock.Free;
end;
end;

cdFILE: // отправить файл
begin
fFileStream:=TFileStream.Create(fFileDat.fFileName,fmOpenRead); //FileStream создать
try fFileDat.fSize:=fFileStream.size; //размер файла
//записать в поток структуру с именем и размером файла
iCode:=cdFILE_TOCLIENT;
fSendStream.WriteBuffer(iCode, SizeOf(Integer));
iSize:=SizeOf(ISFileDat);
fSendStream.WriteBuffer(iSize, SizeOf(Integer));
fSendStream.WriteBuffer(fFileDat, SizeOf(fFileDat));
//записать в поток информ. о том, что сейчас должен последовать файл опред. размера
iCode:=cdFILE;
fSendStream.WriteBuffer(iCode, SizeOf(Integer));
iSize:=fFileDat.fSize;
fSendStream.WriteBuffer(iSize, SizeOf(Integer));
except
raise
end;
end;
end;

fSendStream.Position:=CurPos;
//отправить МемСтрим
fServerSocket.Socket.Connections[0].SendStream(fSendStream);
//отправить файл после отправки МемСтрим-а
if assigned(fFilestream) then
if not assigned(fSendStream) then
fServerSocket.Socket.Connections[0].SendStream(fFileStream);

except
FreeAndNil(fSendStream);
if assigned(fFileStream) then FreeAndNil(fFileStream);
PostMessage(fOwnerForm.Handle,wm_RefreshClients,cdERROR,0);
raise ESendError.CreateRes(@SSendError);
end;
end;

в OnWrite пишу следующее:

procedure TISServerThread.SocketWrite(Sender: TObject;
Socket: TCustomWinSocket);
begin
Socket.SendStream(fSendStream); // отправить memStream
if assigned(fFileStream) then //если есть файл на отправку, то отправить его только
if not assigned(fSendStream) then //после того, как был отправлен memStream
Socket.SendStream(fFileStream);
end;

Но ведь между двумя событиями OnWrite вполне может прийти очередное сообщение TM_SEND_DATABLOCK. И вся моя проблема заключается в том, как отложить запись данных в МемСтрим до тех пор, пока не будет отправлен файл?


 
Digitman   (2003-06-20 14:01) [11]

Попробуй поступить примерно так :

procedure TISServerThread.SendData(var Msg: TMessage);
begin
// помещаем в очередь передачи очередной элемент, отражающий параметры того, что нужно будет передать в соответствии с текущей очередностью сообщения (в соответствии с анализом тек. wParam):
// если некий блок данных, то - указатель на него
// если файл, то - имя файла или тут же создаваемый объект TFileStream (как поле нового объекта-элемента очереди)

if not Assigned(fSendStream) then
begin
// инициируем механизм асинхр.передачи
SocketWrite(MySocketComp; MySocketComp.Socket);
end;

procedure TISServerThread.SocketWrite(Sender: TObject;
Socket: TCustomWinSocket);
begin
if not Assigned(fSendStream) then
begin
fSendStream:=TISMemoryStream.Create(DestroyStream);
end
try
CurPos:=fSendStream.Position;
fSendStream.Position:=fSendStream.Size;
fSendStream.Position:=CurPos;
// !!!!!!!!!!!!!

// (1) здесь, если передается текущий FileStream (ссылка на который взята из поля тек. объекта-элемента очереди) и он не вычерпан полностью, то записываем в конец fSendStream не более 4к из этого FileStream

// уничтожаем текущий FileStream, если он вычерпан
// и читаем очер.эл-т очереди с созданием нового FileStream, если очередь непуста и того требует эл-т очереди, с переходом на (1).
// !!!!!!!!!!!!!

// если передаются иные данные, на которые указывает тек.эл-т очереди, то записываем очер. их порцию не более 4к в в конец fSendStream
//иначе читаем очер.эл-т очереди с переходом на (1).

// здесь повторяем отложенную попытку передачи потока

( fSendStream) Попробуй поступить примерно так :

procedure TISServerThread.SendData(var Msg: TMessage);
begin
// помещаем в очередь передачи очередной элемент, отражающий параметры того, что нужно будет передать в соответствии с текущей очередностью сообщения (в соответствии с анализом тек. wParam):
// если некий блок данных, то - указатель на него
// если файл, то - имя файла или тут же создаваемый объект TFileStream (как поле нового объекта-элемента очереди)

if not Assigned(fSendStream) then
begin
// инициируем механизм асинхр.передачи
SocketWrite(MySocketComp; MySocketComp.Socket);
end;

procedure TISServerThread.SocketWrite(Sender: TObject;
Socket: TCustomWinSocket);
begin
if not Assigned(fSendStream) then
begin
fSendStream:=TISMemoryStream.Create(DestroyStream);
end
try
CurPos:=fSendStream.Position;
fSendStream.Position:=fSendStream.Size;
fSendStream.Position:=CurPos;
// !!!!!!!!!!!!!

// (1) здесь, если передается текущий FileStream (ссылка на который взята из поля тек. объекта-элемента очереди) и он не вычерпан полностью, то записываем в конец fSendStream не более 4к из этого FileStream

// уничтожаем текущий FileStream, если он вычерпан
// и читаем очер.эл-т очереди с созданием нового FileStream, если очередь непуста и того требует эл-т очереди, с переходом на (1).
// !!!!!!!!!!!!!

// если передаются иные данные, на которые указывает тек.эл-т очереди, то записываем очер. их порцию не более 4к в в конец fSendStream
//иначе читаем очер.эл-т очереди с переходом на (1).

// здесь повторяем отложенную попытку передачи потока

Socket.SendStream(fSendStream);

except
// произошла крит.ошибка транспорта - здесь безусловно очищаем (и при необходимости - уничтожаем) очередь передачи
fSendStream.Free; // и буферный объект-поток - тоже
raise; // регенерируем исключение, которое перехватим на уровне Execute()
end;
end;



 
Akni   (2003-06-24 13:36) [12]

получилcя такой код:

procedure TISServerThread.SendData(var Msg: TMessage);
var DatBlock: TISDataBlock; // TISDataBlock = class (TMemoryStream)
begin
DatBlock:=TISDataBlock.Create;
try
case msg.wParam of
cdREC:
DatBlock.Write((Pointer(msg.lParam))^,DatBlock.BytesReserved +SizeOf(ISRecDat));
...
cdFILE_TOCLIENT:
begin
fFileDat:=FileData;
DatBlock.WriteBlock(msg.wParam,SizeOf(ISFileDat),fFileDat);
end;
end;
fSendQueue.Push(DatBlock);

if not Assigned(fSendStream)
then SocketWrite(fServerSocket, fServerSocket.Socket.Connections[0]);
finally
DatBlock:=nil;
end;
end;


procedure TISServerThread.WriteBlockInStream;
var sz: integer;
fDat: ISFileDat;
Buf: array[0..BufSize] of Byte; //BufSize=4095
StreamSize: integer;
begin

StreamSize:=fSendStream.Size;
sz:=fSendDataBlock.Size-fSendDataBlock.Position;

if sz<BufSize-StreamSize then //записать остаток данных в поток
begin
fSendDataBlock.ReadBuffer(Buf, sz);
fSendStream.WriteBuffer(Buf, sz);

case fSendDataBlock.Code of //в зависиомсти от отправленных данных
cdFILE_TOCLIENT: // - создать файлстрим, из которого будет потом
begin // передаваться информация
fSendDataBlock.ReadData(fDat);
fFileStream:=TFileStream.Create(fDat.fServerName, fmOpenRead or fmShareCompat);
try
FreeAndNil(fSendDataBlock);
fSendDataBlock:=TISDataBlock.Create; //записать в поток идентиф. файла
fSendDataBlock.Code:=cdFILE; // и его размер
fSendDataBlock.DataSize:=fFileStream.Size;
except
raise;
end;
end;
cdFILE: //установить флаг отправки файла
begin
fFileToSend:=true;
FreeAndNil(fSendDataBlock);
end;
else //освободить память
FreeAndNil(fSendDataBlock);
end;
end
else //sz>BufSize-StreamSize - записать очередную порцию данных в поток
begin
fSendDataBlock.ReadBuffer(Buf, BufSize-StreamSize);
( Buf, BufSize-StreamSize)
получилcя такой код:

procedure TISServerThread.SendData(var Msg: TMessage);
var DatBlock: TISDataBlock; // TISDataBlock = class (TMemoryStream)
begin
DatBlock:=TISDataBlock.Create;
try
case msg.wParam of
cdREC:
DatBlock.Write((Pointer(msg.lParam))^,DatBlock.BytesReserved +SizeOf(ISRecDat));
...
cdFILE_TOCLIENT:
begin
fFileDat:=FileData;
DatBlock.WriteBlock(msg.wParam,SizeOf(ISFileDat),fFileDat);
end;
end;
fSendQueue.Push(DatBlock);

if not Assigned(fSendStream)
then SocketWrite(fServerSocket, fServerSocket.Socket.Connections[0]);
finally
DatBlock:=nil;
end;
end;


procedure TISServerThread.WriteBlockInStream;
var sz: integer;
fDat: ISFileDat;
Buf: array[0..BufSize] of Byte; //BufSize=4095
StreamSize: integer;
begin

StreamSize:=fSendStream.Size;
sz:=fSendDataBlock.Size-fSendDataBlock.Position;

if sz<BufSize-StreamSize then //записать остаток данных в поток
begin
fSendDataBlock.ReadBuffer(Buf, sz);
fSendStream.WriteBuffer(Buf, sz);

case fSendDataBlock.Code of //в зависиомсти от отправленных данных
cdFILE_TOCLIENT: // - создать файлстрим, из которого будет потом
begin // передаваться информация
fSendDataBlock.ReadData(fDat);
fFileStream:=TFileStream.Create(fDat.fServerName, fmOpenRead or fmShareCompat);
try
FreeAndNil(fSendDataBlock);
fSendDataBlock:=TISDataBlock.Create; //записать в поток идентиф. файла
fSendDataBlock.Code:=cdFILE; // и его размер
fSendDataBlock.DataSize:=fFileStream.Size;
except
raise;
end;
end;
cdFILE: //установить флаг отправки файла
begin
fFileToSend:=true;
FreeAndNil(fSendDataBlock);
end;
else //освободить память
FreeAndNil(fSendDataBlock);
end;
end
else //sz>BufSize-StreamSize - записать очередную порцию данных в поток
begin
fSendDataBlock.ReadBuffer(Buf, BufSize-StreamSize);
fSendStream.WriteBuffer(Buf, BufSize-StreamSize);
end;
end;



 
Akni   (2003-06-24 13:37) [13]

procedure TISServerThread.SocketWrite(Sender: TObject;
Socket: TCustomWinSocket);
var bool: boolean;
Buf: array[0..BufSize] of Byte;
Sz: integer;
CurPos: integer;
StreamSize: integer;
bRes: boolean;
ErrCode: integer;

begin
if not Assigned(fSendStream)
then fSendStream:=TISMemoryStream.Create(DestroyStream);

try
bool:=true;
while bool do
begin
if not Assigned(fSendStream)
then fSendStream:=TISMemoryStream.Create(DestroyStream);

StreamSize:=fSendStream.Size;
CurPos:=fSendStream.Position;
fSendStream.Position:=fSendStream.Size;

if Assigned(fFileStream) and (fFileToSend) then //файл должен быть отправлен
begin
sz:=fFileStream.Size-fFileStream.Position;
fLogStr:="FileStream.Size="+IntToStr(sz);
Synchronize(wrLog);
if sz<BufSize-StreamSize then
begin
fFileStream.ReadBuffer(Buf, sz);
fSendStream.WriteBuffer(Buf, sz);
FreeAndNil(fFileStream);
fFileToSend:=false;
end
else //sz>BufSize-StreamSize
begin
fFileStream.ReadBuffer(Buf, BufSize-StreamSize);
fSendStream.WriteBuffer(Buf, BufSize-StreamSize);
end;
end

else
if Assigned (fSendDataBlock) //данные на отправку
then WriteBlockInStream
else
if fSendQueue.Count>0 //если есть еще данные в очереди, то прочитать их
then // в fSendDataBlock и отправить
begin
fSendDataBlock:=TISDataBlock.Create;
fSendDataBlock:=fSendQueue.Pop;
fSendDataBlock.Position:=0;
WriteBlockInStream;
end
else //нет больше данных для отправки, выход из цикла
begin
bool:=false;
FreeAndNil(fSendStream);
end;

if Assigned(fSendStream) then //отправка потока
begin
fSendStream.Position:=CurPos;
bRes:=Socket.SendStream(fSendStream);
if not bRes then
begin
ErrCode:=WSAGetLastError;
fLogStr:="ErrorCode="+IntToStr(ErrCode);
Synchronize(WrLog);
if ErrCode<>WSAEWOULDBLOCK then raise ESendError.CreateRes(@SSendError);
end;
end;
end;
except
fSendQueue.Free;
fSendStream.Free;
( @SSendError) procedure TISServerThread.SocketWrite(Sender: TObject;
Socket: TCustomWinSocket);
var bool: boolean;
Buf: array[0..BufSize] of Byte;
Sz: integer;
CurPos: integer;
StreamSize: integer;
bRes: boolean;
ErrCode: integer;

begin
if not Assigned(fSendStream)
then fSendStream:=TISMemoryStream.Create(DestroyStream);

try
bool:=true;
while bool do
begin
if not Assigned(fSendStream)
then fSendStream:=TISMemoryStream.Create(DestroyStream);

StreamSize:=fSendStream.Size;
CurPos:=fSendStream.Position;
fSendStream.Position:=fSendStream.Size;

if Assigned(fFileStream) and (fFileToSend) then //файл должен быть отправлен
begin
sz:=fFileStream.Size-fFileStream.Position;
fLogStr:="FileStream.Size="+IntToStr(sz);
Synchronize(wrLog);
if sz<BufSize-StreamSize then
begin
fFileStream.ReadBuffer(Buf, sz);
fSendStream.WriteBuffer(Buf, sz);
FreeAndNil(fFileStream);
fFileToSend:=false;
end
else //sz>BufSize-StreamSize
begin
fFileStream.ReadBuffer(Buf, BufSize-StreamSize);
fSendStream.WriteBuffer(Buf, BufSize-StreamSize);
end;
end

else
if Assigned (fSendDataBlock) //данные на отправку
then WriteBlockInStream
else
if fSendQueue.Count>0 //если есть еще данные в очереди, то прочитать их
then // в fSendDataBlock и отправить
begin
fSendDataBlock:=TISDataBlock.Create;
fSendDataBlock:=fSendQueue.Pop;
fSendDataBlock.Position:=0;
WriteBlockInStream;
end
else //нет больше данных для отправки, выход из цикла
begin
bool:=false;
FreeAndNil(fSendStream);
end;

if Assigned(fSendStream) then //отправка потока
begin
fSendStream.Position:=CurPos;
bRes:=Socket.SendStream(fSendStream);
if not bRes then
begin
ErrCode:=WSAGetLastError;
fLogStr:="ErrorCode="+IntToStr(ErrCode);
Synchronize(WrLog);
if ErrCode<>WSAEWOULDBLOCK then raise ESendError.CreateRes(@SSendError);
end;
end;
end;
except
fSendQueue.Free;
fSendStream.Free;
raise ESendError.CreateRes(@SSendError);
end;
end;
Приведенный выше код является кодом на стороне сервера. Код на стороне клиента практически полностью аналогичен.

Так вот, при отправке данных хотя бы в несколько сотен Кб и больше (ну, например, тех же файлов), часть данных успешно отправляется, а потом все попытки отправить поток дают WSAEWOULDBLOCK(ErrCode=10035)

Из-за чего это происходит? Обмен данными тестируется или на одном компе, или на двух в ЛВС, так что неготовность/нестабильность соединения кажутся маловероятными.

Тем более что ранее отправка файла «в лоб» Socket.SendStream(TFileStream.Create(fDat.fServerName, fmOpenRead or fmShareCompat)) завершалась успешно


 
Digitman   (2003-06-24 15:09) [14]

...
if Assigned(fSendStream) then //отправка потока
begin
fSendStream.Position:=CurPos;
( fSendStream) ...
if Assigned(fSendStream) then //отправка потока
begin
fSendStream.Position:=CurPos;
Socket.SendStream(fSendStream);
end;
end;
...

так будет вернее.

сам объект fSendStream, как ты уже знаешь, будет уничтожен автом-ки, если все данные из него успешно отправлены на передачу

в противном случае событие OnWrite вновь возникнет позже


 
Akni   (2003-06-25 11:33) [15]


> сам объект fSendStream, как ты уже знаешь, будет уничтожен
> автом-ки, если все данные из него успешно отправлены на
> передачу


но ведь все равно нужно проводить какую-то проверку отправки потока и в случае, если он не отправлен полностью (не уничтожен), выходить из обработчика OnWrite?



 
Digitman   (2003-06-25 14:45) [16]

зачем ?? никакой проверки не нужно !!

вызвала там, в OnWrite() метод SendStream(fSendStream) - и всех делов !) даже без анализа результата вызова метода
если поток полностью "уйдет" в буфер передачи, fSendStream будеит автом-ки уничтожен, иначе OnWrite() через какое-то время возникнет снова

я все-таки тебе порекомендую внимательно изучить исходники методов SendStream() и SendStreamPiece() - сразу все станет понятно, что там и при каких условиях происходит


 
Akni   (2003-06-25 15:02) [17]


> я все-таки тебе порекомендую внимательно изучить исходники
> методов SendStream() и SendStreamPiece()
- да я их уже почти наизусть знаю :)

происходит у меня сейчас следующее:

после отправки потока

if Assigned(fSendStream) then //отправка потока
begin
fSendStream.Position:=CurPos;
Socket.SendStream(fSendStream);
end;
end;

он (поток) освобождается не сразу (точнее, не всегда сразу). И в этом случае зациливается у меня метод OnWrite до бесконечности :(

Помогает вставка после
Socket.SendStream(fSendStream);

строки Sleep(10)

Тогда поток успешно освобождается и дальше все идет нормально.
Но это, ИМХО, совсем нехорошее решение...


 
Digitman   (2003-06-25 15:31) [18]

правильно !)

у тебя же while bool do !)
а не нужен он здесь вовсе)

поскольку событие возникло, то это говорит том, что предыдущий вызов SendStream(fSendStream) (где бы он ни был произведен - в этом обработчике или вне его) завершился с WSAEWOULDBLOCK (буфер передачи был в этот момент занят)... при этом (см. исходники метода) метод вернет True. По этому самому True судить о текущем состоянии передачи потока нельзя, да и не нужно - следующее событие OnWrite() само скажет об имевшем место факте отложенной передачи.


т.о., все, что тебе требуется в этом событии :
- если fSendStream = nil, то создать его (если очередь передачи непуста)
- записать в хвост fSendStream очередной блок данных (очер фрагмент файла или еще чего-то - неважно), если очередь передачи требует то или иное, и в конце обработчтка вновь выполнить SendStream(fSendStream);


 
Akni   (2003-06-25 17:13) [19]

где-то я, наверное, очень сильно торможу...

Вариант 1.
1.1 пишем в fSendStream 4 кб информации (из, скажем, 25кб)
1.2 SendStream(fSendStream) не может отправить весь поток и завершается с WSAEWOULDBLOCK
1.3 OnWrite вызывается снова, повторяется п. 1.1. и т .д.

Вариант 2.
2.1 пишем в fSendStream 4 кб информации (из, скажем, 25кб)
2.2 SendStream(fSendStream) отправляет весь поток (4 кб) благополучно. Событие OnWrite как реакция на незаконченную передачу не возникает.
2.3 оставшиеся 19 кб виснут "в воздухе"


 
Digitman   (2003-06-26 08:25) [20]

см. Вариант 2

но ты же "научила" свой объект fSendStream извещать о своем разрушении !) ... которое происходит в SendStreamPiece() в случае успешной передачи всего его содержимого) ... так вот и используй этот факт для анализа, есль ли еще в твоей собственной очереди на передачу данные, требуемые к передаче !


 
Akni   (2003-06-26 11:14) [21]

Digitman, спасибо огромное за советы и терпение :) !
Все получилось.


 
Digitman   (2003-06-26 11:35) [22]


> Akni


Молодец !
Успехов тебе !



Страницы: 1 вся ветка

Форум: "Сети";
Текущий архив: 2003.09.01;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.57 MB
Время: 0.01 c
3-1335
explorer
2003-08-06 08:37
2003.09.01
Картинки в БД


14-1673
Knight
2003-08-12 07:36
2003.09.01
Что за глюк?


1-1426
Terrible
2003-08-18 14:08
2003.09.01
Теряются тесктовые ресурсы в проге на Delphi7


14-1562
RIMMER
2003-08-11 23:31
2003.09.01
Как запретить XP theme UI для моей программы


1-1404
PlaZZma
2003-08-16 02:12
2003.09.01
передача компонента в функцию





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский