Форум: "Сети";
Текущий архив: 2003.01.23;
Скачать: [xml.tar.bz2];
ВнизOnClientWrite Найти похожие ветки
← →
Dmitriy Polskoy (2002-11-25 14:22) [0]Ув. Мастера помогите разобраться. Я никак не могу до конца понять когда возникает сабж. Насколько я понял это событие возникает в момент когда в сокет можно передавать данные. Например, вызвав Send-метод, я анализирую Result и если кол-во байт реально переданных меньше отправляемого значения, то жду события ClientWrite, в котором допередаю оставшиеся байты.
Привожу пример, в котором передаю через сокет вайл потоком. Результат передачи пишу в листбокс. В случае неудачи, когда сокет освободиться по-идее должно сработать OnClienWrite. Но что-то не так. Еще раз прошу - помогите разобраться.
← →
Dmitriy Polskoy (2002-11-25 14:24) [1]Забыл дописать код
procedure TMainForm.Button1Click(Sender: TObject);
var
X: TMemoryStream;
S: Integer;
Ok:Boolean;
begin
X := TMemoryStream.Create;
X.LoadFromFile("c:\222.zip");
S := X.Size;
ListBox1.Items.Add(IntToSTr(S));
X.Position := 0;
Ok := ServerSocket.Socket.Connections[0].SendStream(X);
if Ok then
ListBox1.Items.Add("Ok")
else
ListBox1.Items.Add("No");
ListBox1.Items.Add("-------");
end;
procedure TMainForm.ServerSocketClientWrite(Sender: TObject;
Socket: TCustomWinSocket);
begin
ShowMessage("OnWrite");
end;
← →
Digitman (2002-11-25 14:41) [2]если SendStream(X), вызванный НЕВАЖНО ГДЕ , вернул False, просто следует сохранить где-нибудь ссылку на объект-поток X, иначе - очистить ссылку (ибо при SendStream(X) = True объект-поток будет уничтожен автоматически в теле метода SendStream как полностью и успешно переданный)
procedure TMainForm.ServerSocketClientWrite(Sender: TObject;
Socket: TCustomWinSocket);
begin
// это событие возникнет, если предыдущий вызов SendStream() возвратил False. Здесь следует повторить вызов SendStream() для того же объекта-потока, если он в этот момент еще/уже существует
...
end;
← →
Dmitriy Polskoy (2002-11-25 14:55) [3]Но почему тогда ShowMessage не срабатывает, когда SendStream возвращает false.
← →
Digitman (2002-11-25 15:01) [4]Быть того не может.
← →
Dmitriy Polskoy (2002-11-25 15:06) [5]Да нет - может. Только что пробовал.
← →
Digitman (2002-11-25 15:07) [6]ты вообще в состоянии анализировать Паскаль-текст ?
я уже в который раз (и для тебя персонально - в т.ч.) привожу текст из 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:43) [7]У меня по приведенному коду один вопрос:
Правильно ли я понял, что только
if (FSocket = INVALID_SOCKET) or (not FConnected) then exit
- является результатом возврата False
← →
Dmitriy Polskoy (2002-11-25 16:00) [8]Вот еще фраза из хелпа, подтолкнувшая меня к вышевысказанному мнению
The value returned by SendStream indicates whether any information was successfully written to the connection.
Если мое предположение правильно, то уменя возникает вопрос - как узнать какой объем я передал.
← →
Digitman (2002-11-25 16:11) [9]
> У меня по приведенному коду один вопрос:
> Правильно ли я понял, что только
> 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:33) [10]
> Неправильно. Еще раз смотри вот сюда :
>
>
> > 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:36) [11]что непонятно ? конкретно ?
← →
Dmitriy Polskoy (2002-11-25 16:45) [12]Не понятно следующие:
в 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 17:43) [13]в 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 18:05) [14]Но почему тогда
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 (только не говори, что такого не может быть)
← →
Morfein (2002-11-25 20:48) [15]А ты проверь свойство Connected... мож из-за какой-то внутренней ошибки у тебя сокет давно уничтожен, а ты ждёшь от него события OnWrite()!
← →
Digitman (2002-11-26 08:17) [16]я привел тебе набросок работающкго кода
причесывай его и вникай-пользуй
единственное, чего там не хватает - обработки исключений send-вызовов
← →
Dmitriy Polskoy (2002-11-26 11:20) [17]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 12:41) [18]
> я тут твой кусок чуть переделал (упростил для проведения
> теста).
Не занимайся ерундой. Фрагмент, что я привел, работает только в комплексе. Ты же его исказил, не вникнув, доведя до изначального примитива !
В этом фрагменте прооверка результата SensStream() вообще не нужна, ибо контроль за обнулением ссылки в Socket.Data на поток, с которым работает SensStreamPiece(), возлагается на обработчик OnStreamDestroy().
← →
Dmitriy Polskoy (2002-11-26 12:49) [19]Ok, тогда у меня вопрос по твоему коду - что это такое
> procedure TMyForm.ServerSocketClientConnect(Sender: TObject;
> ClientSocket: TCustomWinSocket);
> begin
> ClientSocket.Data := nil;
> end;
Может надо просто Socket или ServerSocket.Socket? А и еще по поводу слов в листбоксе - таже проблема и с твоим кодом.
← →
Digitman (2002-11-26 12:58) [20]
> Может надо просто Socket или ServerSocket.Socket
Что значит "Может надо просто" ? А на кой черт, по-твоему, параметры события существуют ? В дан.случае ClientSocket ? Для красоты что-ли ? Этот параметр кака раз и сообщает, для какого конкретно гнезда возникло это событие. Вот я св-во Data этого гнездаи инициализирую перед дальнейшей работой.
> А и еще по поводу слов в листбоксе
Я не знаю, какая там у тебя проблема с каким-то "листбоксом", но событие OnWrite() просто ОБЯЗАНО возникать в этом режиме через некоторый момент времени при соблюдении условия, что в некий предыдущий момент времени в буфер передачи гнезда производится попытка записать БОЛЬШЕ данных, чем позволяет размер свободной части буфера (и, разумеется, коннект существует в этот момент)
← →
Dmitriy Polskoy (2002-11-26 13:29) [21]А ты попробуй запусти свой код, только с проверкой возникновения события Write - если сработал в нем Send_методот, то (ShowMessage, Label.Caption и т.д) "Ok" иначе "No". Так мне интересно после "No" когда у тебя выскочит "Ok".
← →
Digitman (2002-11-26 13:40) [22]Оно мне надо, ерундой этой заниматься с твоими ОК и No?) Логика, набросанная в этом коде, есть шаблон, по которому давно и успешно работают некоторые мои задачи, использующие конкретно SendStream() в кач-ве транспортного метода.
Основная идея в шаблоне - объект-поток должен сам известить о своем разрушении заинтересованный в этом факте код. При таком подходе вообще нет нужды анализировать результат SendStream(), достаточно просто "обернуть" каждый такой вызов в try/finally/exept, дабы не упустить исключения, связанного с неожиданным разрушением транспортной петли между передатчиком и приемником (равно как и иных исключений Winsock, не позволяющих штатное функционирование/восстановление транспорта )
Страницы: 1 вся ветка
Форум: "Сети";
Текущий архив: 2003.01.23;
Скачать: [xml.tar.bz2];
Память: 0.52 MB
Время: 0.007 c