![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
OnClientWrite [D6, Win2k]
Dmitriy Polskoy © (2002-11-25 14:22:00)Ув. Мастера помогите разобраться. Я никак не могу до конца понять когда возникает сабж. Насколько я понял это событие возникает в момент когда в сокет можно передавать данные. Например, вызвав Send-метод, я анализирую Result и если кол-во байт реально переданных меньше отправляемого значения, то жду события ClientWrite, в котором допередаю оставшиеся байты.
Привожу пример, в котором передаю через сокет вайл потоком. Результат передачи пишу в листбокс. В случае неудачи, когда сокет освободиться по-идее должно сработать OnClienWrite. Но что-то не так. Еще раз прошу - помогите разобраться.
Digitman (2002-11-26 13:40:00) [1]Оно мне надо, ерундой этой заниматься с твоими ОК и No?) Логика, набросанная в этом коде, есть шаблон, по которому давно и успешно работают некоторые мои задачи, использующие конкретно SendStream() в кач-ве транспортного метода.
Основная идея в шаблоне - объект-поток должен сам известить о своем разрушении заинтересованный в этом факте код. При таком подходе вообще нет нужды анализировать результат SendStream(), достаточно просто "обернуть" каждый такой вызов в try/finally/exept, дабы не упустить исключения, связанного с неожиданным разрушением транспортной петли между передатчиком и приемником (равно как и иных исключений Winsock, не позволяющих штатное функционирование/восстановление транспорта )
Dmitriy Polskoy (2002-11-26 13:29:00) [2]А ты попробуй запусти свой код, только с проверкой возникновения события Write - если сработал в нем Send_методот, то (ShowMessage, Label.Caption и т.д) "Ok" иначе "No". Так мне интересно после "No" когда у тебя выскочит "Ok".
Digitman (2002-11-26 12:58:00) [3]
> Может надо просто Socket или ServerSocket.Socket
Что значит "Может надо просто" ? А на кой черт, по-твоему, параметры события существуют ? В дан.случае ClientSocket ? Для красоты что-ли ? Этот параметр кака раз и сообщает, для какого конкретно гнезда возникло это событие. Вот я св-во Data этого гнездаи инициализирую перед дальнейшей работой.
> А и еще по поводу слов в листбоксе
Я не знаю, какая там у тебя проблема с каким-то "листбоксом", но событие OnWrite() просто ОБЯЗАНО возникать в этом режиме через некоторый момент времени при соблюдении условия, что в некий предыдущий момент времени в буфер передачи гнезда производится попытка записать БОЛЬШЕ данных, чем позволяет размер свободной части буфера (и, разумеется, коннект существует в этот момент)
Dmitriy Polskoy (2002-11-26 12:49:00) [4]Ok, тогда у меня вопрос по твоему коду - что это такое
> procedure TMyForm.ServerSocketClientConnect(Sender: TObject;
> ClientSocket: TCustomWinSocket);
> begin
> ClientSocket.Data := nil;
> end;
Может надо просто Socket или ServerSocket.Socket? А и еще по поводу слов в листбоксе - таже проблема и с твоим кодом.
Digitman (2002-11-26 12:41:00) [5]
> я тут твой кусок чуть переделал (упростил для проведения
> теста).
Не занимайся ерундой. Фрагмент, что я привел, работает только в комплексе. Ты же его исказил, не вникнув, доведя до изначального примитива !
В этом фрагменте прооверка результата SensStream() вообще не нужна, ибо контроль за обнулением ссылки в Socket.Data на поток, с которым работает SensStreamPiece(), возлагается на обработчик OnStreamDestroy().
Dmitriy Polskoy (2002-11-26 11:20:00) [6]Digitman ©, я тут твой кусок чуть переделал (упростил для проведения теста). Вот глянь
procedure TMainForm.Button1Click(Sender: TObject);
var
Stream: TMemoryStream;
begin
Stream := ServerSocket.Socket.Connections[0].Data;
if not Assigned(Stream) then
Stream := TMemoryStream.Create;
Stream.LoadFromFile("c:\222.zip");
Stream.Position := 0;
if not Assigned(ServerSocket.Socket.Connections[0].Data) then
begin
ServerSocket.Socket.Connections[0].Data := Stream;
ServerSocketClientWrite(ServerSocket, ServerSocket.Socket.Connections[0]);
end;
end;
procedure TMainForm.ServerSocketClientWrite(Sender: TObject;
Socket: TCustomWinSocket);
var
Stream: TMemoryStream;
begin
Stream := Socket.Data;
if Assigned(Stream) then
if ServerSocket.Socket.Connections[0].SendStream(Stream) then
begin
ListBox1.Items.Add("True");
Socket.Data := nil;
end
else ListBox1.Items.Add("False");
end;
Поправь меня если я что-то скажу не так: получается, что если SendStream в обработчике OnClientWrite вернет False, то экземпляр потока не будет автоматически уничтожен в SendStreamPiece() и ассоциация с экземпляром также не будет разорвана. В результате когда сокет освободиться, то он известит об этом событием ClientWrite, поэтому рано или поздно после нажатия на кнопку и появления слова "False" в листбоксе без последующего нажатия в нем же должно появиться слово "True", но оно так и не появляется. Почему так происходит?
Digitman (2002-11-26 08:17:00) [7]я привел тебе набросок работающкго кода
причесывай его и вникай-пользуй
единственное, чего там не хватает - обработки исключений send-вызовов
Morfein (2002-11-25 20:48:00) [8]А ты проверь свойство Connected... мож из-за какой-то внутренней ошибки у тебя сокет давно уничтожен, а ты ждёшь от него события OnWrite()!
Dmitriy Polskoy (2002-11-25 18:05:00) [9]Но почему тогда
procedure TMainForm.Button1Click(Sender: TObject);
var
X: TMemoryStream;
begin
X := TMemoryStream.Create;
X.LoadFromFile("c:\222.zip");
X.Position := 0;
ServerSocket.Socket.Connections[0].SendStream(X);
end;
procedure TMainForm.ServerSocketClientWrite(Sender: TObject;
Socket: TCustomWinSocket);
begin
ShowMessage("OnWrite");
end;
не срабатывает ShowMessage, когда Send_Метод_Result = False (только не говори, что такого не может быть)
Digitman (2002-11-25 17:43:00) [10]в 1-й же строчке и сбрасывает.
SendStreamPiece() же будет вызвана здесь же автоматически и ТОЛЬКО при условии, что ссылка на ранее переданный параметром объект-поток запомнена в приватном поле FSendStream;
А SendStream() следует вызывать в событии OnWrite() ТОЛЬКО в случае,
Иными словами, если текущий поток находится, так сказать, "во внутреннем производстве" у лок.процедуры SendStreamPiece()
(т.е. в рез-те предыдущего вызова SendStreamPiece() содержимое объекта-потока не было поставлено в очередь полностью, объект-поток не разрушен и ссылка на него временно зафиксирована в приватном поле FSendStream)
то рано или поздно возникнет OnWrite(), в теле которого можно попытаться вновь вызвать SendStream(), передав ему при этом любую ссылку (например, nil), ибо SendStreamPiece() реально возьмет ее из поля FSendStream
TMyStream = class(TMemoryStream)
private
FSocket: TCustomWinSocket;
FOnDestroy: TSocketNotifyEvent;
protected
procedure AfterConstruction; override;
public
constructor Create(Socket: TCustomWinSocket);
destructor Destroy; override;
property OnDestroy: TNotifyEvent read FOnDestroy write FOnDestroy;
end;
constructor TMyStream.Create(Socket: TCustomWinSocket);
begin
inherited Create;
FSocket := Socket;
end;
destructor TMyStream.Destroy;
begin
if Assigned(FOnDestroy) then
FOnDestroy(Self, Socket);
inherited;
end;
procedure TMyStream.AfterConstruction;
begin
FOnDestroy := nil;
end;
procedure TMyForm.ServerSocketClientConnect(Sender: TObject; ClientSocket: TCustomWinSocket);
begin
ClientSocket.Data := nil;
end;
procedure TMyForm.ServerSocketClientDisconnect(Sender: TObject; ClientSocket: TCustomWinSocket);
begin
try
TMyStream(ClientSocket.Data).Free;
except
end;
end;
procedure TMyForm.ServerSocketClientWrite(Sender: TObject; ClientSocket: TCustomWinSocket);
begin
if Assigned(ClientSocket.Data) then
ClientSocket.SendStream(TMyStream(ClientSocket.Data));
end;
procedure TMyForm.OnStreamDestroy(Sender: TObject; Socket: TCustomWinSocket);
begin
if Sender = Socket.Data then
Socket.Data := nil;
end;
....
var
Target : TCustomWinSocket;
MyStream: TMyStream;
CurStreamPos: Integer;
...
Target := ServerSocket.Socket.Connections[..];
MyStream := TMyStream(Target.Data);
if not Assigned(MyStream) then
begin
MyStream := TMyStream.Create(Target);
MyStream.OnDestroy := OnStreamDestroy;
end;
CurStreamPos := MyStream.Position;
MyStream.Position := Size;
MyStream.Write(чего-то там);
MyStream.Position := CurStreamPos;
ServerSocketClientWrite(ServerSocket1, Target);
...
/CODE>
Dmitriy Polskoy (2002-11-25 16:45:00) [11]Не понятно следующие:
в SendStreamPiece во всех случаях, кроме
if (FSocket = INVALID_SOCKET) or (not FConnected) then exit;
иend else if AmountInBuf > AmountSent thenсрабатывет Break, в результате чего Result = True, вот у меня и возникает вопрос - когда Result станет False. Ты сказал смотреть
FSendStream.Position := StartPos + AmountSent
> Неправильно. Еще раз смотри вот сюда :
>
>
> > function TCustomWinSocket.SendStream(AStream: TStream):
>
> > Boolean;
> > begin
> > Result := False;
> > if FSendStream = nil then
> > begin
> > FSendStream := AStream;
> > Result := SendStreamPiece;
> > end;
> > end;
но в этой функции я не вижу, где резалт збрасывается в False, она ведь вызывает SendStreamPiece, понимание исхода которой я изложил выше
Digitman (2002-11-25 16:36:00) [12]что непонятно ? конкретно ?
Dmitriy Polskoy (2002-11-25 16:33:00) [13]
> Неправильно. Еще раз смотри вот сюда :
>
>
> > function TCustomWinSocket.SendStream(AStream: TStream):
>
> > Boolean;
> > begin
> > Result := False;
> > if FSendStream = nil then
> > begin
> > FSendStream := AStream;
> > Result := SendStreamPiece;
> > end;
> > end;
Прости, но сам понять я не могу. Объясни пожалуйста.
Digitman (2002-11-25 16:11:00) [14]
> У меня по приведенному коду один вопрос:
> Правильно ли я понял, что только
> if (FSocket = INVALID_SOCKET) or (not FConnected) then exit
> - является результатом возврата False
Неправильно. Еще раз смотри вот сюда :
> function TCustomWinSocket.SendStream(AStream: TStream):
> Boolean;
> begin
> Result := False;
> if FSendStream = nil then
> begin
> FSendStream := AStream;
> Result := SendStreamPiece;
> end;
> end;
> как узнать какой объем я передал
Вот ведь велика проблема !)
Если объект-поток на момент проверки уже разрушен в контексте SensStream(), то - все, что в нем было, уже передано приемнику либо
Иначе если Position < Size, то - жди очередного OnWrite()
Dmitriy Polskoy (2002-11-25 16:00:00) [15]Вот еще фраза из хелпа, подтолкнувшая меня к вышевысказанному мнению
The value returned by SendStream indicates whether anyinformation was successfully written to the connection.
Если мое предположение правильно, то уменя возникает вопрос - как узнать какой объем я передал.
Dmitriy Polskoy (2002-11-25 15:43:00) [16]У меня по приведенному коду один вопрос:
Правильно ли я понял, что только
if (FSocket = INVALID_SOCKET) or (not FConnected) then exit- является результатом возврата False
Digitman (2002-11-25 15:07:00) [17]ты вообще в состоянии анализировать Паскаль-текст ?
я уже в который раз (и для тебя персонально - в т.ч.) привожу текст из scktcomp.pas, реализующий метод SendStream()
вникни в него и разберись, при каких условиях метод возвращает False и что происходит с потоком, если метод вернул True
сопоставь эти условия с теми, которые у тебя возникают всякий раз на момент вызова SendStream().
function TCustomWinSocket.SendStreamPiece: Boolean;
var
Buffer: array[0..4095] of Byte;
StartPos: Integer;
AmountInBuf: Integer;
AmountSent: Integer;
ErrorCode: Integer;
procedure DropStream;
begin
if FDropAfterSend then Disconnect(FSocket);
FDropAfterSend := False;
FSendStream.Free;
FSendStream := nil;
end;
begin
Lock;
try
Result := False;
if FSendStream <> nil then
begin
if (FSocket = INVALID_SOCKET) or (not FConnected) then exit;
while True do
begin
StartPos := FSendStream.Position;
AmountInBuf := FSendStream.Read(Buffer, SizeOf(Buffer));
if AmountInBuf > 0 then
begin
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;
end else if AmountInBuf > AmountSent then
FSendStream.Position := StartPos + AmountSent
else if FSendStream.Position = FSendStream.Size then
begin
DropStream;
Break;
end;
end else
begin
DropStream;
Break;
end;
end;
Result := True;
end;
finally
Unlock;
end;
end;
function TCustomWinSocket.SendStream(AStream: TStream): Boolean;
begin
Result := False;
if FSendStream = nil then
begin
FSendStream := AStream;
Result := SendStreamPiece;
end;
end;
Dmitriy Polskoy (2002-11-25 15:06:00) [18]Да нет - может. Только что пробовал.
Digitman (2002-11-25 15:01:00) [19]Быть того не может.
Dmitriy Polskoy (2002-11-25 14:55:00) [20]Но почему тогда ShowMessage не срабатывает, когда SendStream возвращает false.