Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Сети";
Текущий архив: 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
FSendStream.Position := StartPos + AmountSent
срабатывет Break, в результате чего Result = True, вот у меня и возникает вопрос - когда Result станет False. Ты сказал смотреть
> Неправильно. Еще раз смотри вот сюда :
>
>
> > 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.009 c
1-72267
Regent
2003-01-15 05:48
2003.01.23
Что не так?


1-72398
solo
2003-01-12 22:40
2003.01.23
Вопрос по пространству имен Windows.


1-72175
Inan61
2003-01-13 18:50
2003.01.23
Application.MessageBox. Обработка нажатия на «Справка»


3-72060
ASF
2003-01-03 01:01
2003.01.23
Редактируемая запись в DBgrid другим цветом


6-72442
Itap
2002-11-25 17:45
2003.01.23
Error (10057)





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
Английский Французский Немецкий Итальянский Португальский Русский Испанский