Newsgroups : Borland : borland.public.delphi.internet.winsock : 2008 Jan : Re: TClientSocket write progress???
| Subject: | Re: TClientSocket write progress??? |
| Posted by: | "Remy Lebeau (TeamB)" (no.spam@no.spam.com) |
| Date: | Thu, 24 Jan 2008 11:38:44 |
"Bo Berglund" <bo.berglund@telia.com> wrote in message
news:2ebgp3lj3mjr0t7f8g7hsvuhpj80u762al@4ax.com...
> Something is not right here...
> I have written the send part of my program this way (the repeat loop
> was put there only yesterday after reading your responses):
You did not pay attention to everything I told you. If SendText()
returns -1, you MUST wait for the OnWrite event to be triggered before you
can write more data to the socket. You are not checking for that at all.
And since you are using a non-blocking socket, you MUST allow window
messages to be processed as well, or else the event will never be triggered.
Did you try looking at the newsgroup archives for examples, like I
suggested? For instance:
http://groups.google.com/group/borland.public.delphi.internet.winsock/msg/af50cfbfc40fb5e9
(you would have to adapt it to your STX-ETX model, but you should get
the gist of how to manage the OnWrite event in general)
> function TSSRemoteClient.SendTextData(Msg: string): boolean;
If you must wait for all of the data to be written before exiting, then you
have to code it more like this instead:
function TSSRemoteClient.SendTextData(const Msg: string): boolean;
var
TxLen: integer;
Tel: string;
begin
Result := False;
try
if FSocket.Socket.Connected then
begin
LogStd('Tx to ' + FSocket.Socket.RemoteAddress + ': ' +
Msg);
FPacketCount := 0;
Tel := STX + Msg + ETX;
repeat
TxLen := FSocket.Socket.SendText(Tel);
if TxLen < 0 then // socket would block
begin
Application.ProcessMessages; // allow OnWrite to be
processed
Continue;
end;
if TxLen = 0 then
begin
LogErr('Disconnected from host, cannot send data');
Exit;
end;
Delete(Tel, 1, TxLen);
until Length(Tel) = 0;
Result := true;
end
else
LogErr('Disconnected from host, cannot send data');
except
on E: Exception do
LogErr('Exception in SendTextData: ' + E.Message);
end;
end;
Otherwise, use blocking sockets instead.
> Before this I just used
> FSocket.Socket.SendText(Tel);
> and no repeat loop.
Which did not protect your data in any way from partial transmissions or
errors.
> When sending a file I load it fully into memory
I do not recommend you do that. It would be better to read it in smaller
chunks, sending each one separately, ie
open file
repeat
read a chunk
send it
update progress
until no more data
close file
> then make a hex conversion of every single byte in the file body
> (blowing it up by x2) to make the data ASCII.
Since hex conversion will bloat the data by 2x, you should consider using
Base64, yEnc, or other encoding scheme that has better compression in it.
Or, if you can avoid using STX and ETX then you could just send the raw
binary data as-is without encoding it at all.
> I have not ever gotten a truncated file sent.
That doesn't mean it couldn't happen, though. Large files, or slow
networks, would be susceptable to the conditions I described earlier. Your
earlier code was simply not up to the task of handling everything properly.
> And I have tested with files more than several megabytes in size.
> And I have also tested to see what happens in this area of the code
> while stepping through. The SendText method always returns
> *immediately* at the same time as network activity starts and
> continues for a long time.
SendText() does a single call to SendBuf() and returns whatever SendBuf()
returns:
function TCustomWinSocket.SendText(const s: string): Integer;
begin
Result := SendBuf(Pointer(S)^, Length(S));
end;
SendBuf() can only send however much the socket can physically hold at one
time. There is no looping inside of SendBuf(). You cannot pass megabytes
of data in a single call. It is not physically possible. And because of
the non-blocking nature of your sockets, SendBuf() is not guaranteed to even
be able to accept data at any arbitrary time, either. The Result will be -1
(SOCKET_ERROR) instead (if any error other than WSAEWOULDBLOCK is reported,
you will also get an OnError event).
> This wait takes maybe 20 s for a large file and here is where I want
> to have the progress info.
Simply put it inside the loop, ie:
function TSSRemoteClient.SendTextData(const Msg: string): boolean;
var
TxLen, TotalLen, LenSent: integer;
Tel: string;
begin
Result := False;
try
if FSocket.Socket.Connected then
begin
LogStd('Tx to ' + FSocket.Socket.RemoteAddress + ': ' +
Msg);
FPacketCount := 0;
Tel := STX + Msg + ETX;
TotalLen := Length(Tel);
LenSent := 0;
repeat
TxLen := FSocket.Socket.SendText(Tel);
if TxLen < 0 then // socket would block
begin
// must allow OnWrite to be processed before writing
more data...
Application.ProcessMessages;
Continue;
end;
if TxLen = 0 then
begin
// other party disconnected...
LogErr('Disconnected from host, cannot send data');
Exit;
end;
// at least 1 byte was sent...
Delete(Tel, 1, TxLen);
Inc(LenSent, TxLen);
// update/display progress as needed. You have the total
length being
// sent, and the length that has been sent so far. You
can calculate a
// percentage from those two values if desired.
Remember to call
// Application.ProcessMessages as well if you are
updating the UI...
until LenSent >= TotalLen;
Result := true;
end
else
LogErr('Disconnected from host, cannot send data');
except
on E: Exception do
LogErr('Exception in SendTextData: ' + E.Message);
end;
end;
> But your suggestion would mean having the progress go from 0 to 100%
> instantaneously when the data are still only starting to being
> transfered.
No, it would not.
> Remember that my post was not about how to stop losing data, it was
> on getting a transfer progress display while successfully sending the
> data.
I realize that. But you can't implement progress tracking in this situation
without looping, since TClientSocket does not have any progress
notifications of its own. Since you have to loop anyway, you may as well
also learn the proper way to manage the loop itself.
> As I said I have never once had a bad transfer
Then you got lucky. Your earlier code was still prone to errors. Even your
newer looping code still has some room for errors in it.
Gambit