Board index » delphi » Socket Error # 0 in RETR handler

Socket Error # 0 in RETR handler


2003-10-30 10:08:15 AM
delphi84
The following code is killing me. I keep getting Socket Error # 0 during
the WriteStream call. And even if I try to handle/ignore it, I still lose
the connection. I'd be willing to pay anyone who could help me solve
this one. Argh.
Thanks
--------------------------------
procedure TMainForm.PopServerRETR(ASender: TIdCommand;
AMessageNum: Integer);
var
s, sMsgFile: string;
AStream: TMemoryStream;
AMsg : TIdMessage;
begin
with TVsUserData(ASender.Thread.Data) do begin
AddToLog( 'C: RETR ' + IntToStr(AMessageNum) );
s := '+OK ' + IntToStr(TVsMsgInfo(MsgList.Items[AMessageNum-1]).Size) +
' octets';
ASender.thread.Connection.WriteLn(s);
sMsgFile := MsgPath + TVsMsgInfo(MsgList.Items[AMessageNum-1]).FileName;
AddToLog( 'About to send contents of ' + sMsgFile );
try
try
AMsg := TIdMessage.create(nil);
except
on e: exception do begin
AddToLog('TIdMessage.create exception: ' + e.Message );
exit;
end;
end;
AMsg.NoEncode := true;
AMsg.NoDecode := true;
try
AMsg.LoadFromFile(sMsgFile,false);
except
on e: exception do begin
AddToLog('TIdMessage.LoadFromFile exception: ' + e.Message );
exit;
end;
end;
try
AStream := TmemoryStream.Create;
except
on e: exception do begin
AddToLog('TMemoryStream.create exception: ' + e.Message );
exit;
end;
end;
try
AMsg.SaveToStream(AStream,false);
except
on e: exception do begin
AddToLog('TIdMessage.SaveToStream exception: ' + e.Message );
exit;
end;
end;
try
ASender.Thread.Connection.WriteStream(AStream); // always
blows an error here!
except
on e: exception do begin
AddToLog('ASender.Thread.Connection.WriteStream exception: ' +
e.Message );
if ( lowercase(trim(e.Message)) <>'socket error # 0' ) then begin
exit;
end;
end;
end;
AddToLog( 'S: <contents of ' + sMsgFile + ' sent successfully>' );
finally
if assigned(AStream) then freeandnil(AStream);
if assigned(AMsg) then freeandnil(AMsg);
end;
end;
end; // RETR
 
 

Re:Socket Error # 0 in RETR handler

"Vern Six" <XXXX@XXXXX.COM>writes
Quote
s := '+OK ' + IntToStr(TVsMsgInfo(MsgList.Items[AMessageNum-1]).Size)
+
' octets';
ASender.thread.Connection.WriteLn(s);
I would not recommend you call WriteLn() until after you have already loaded
and prepared the TIdMessage for sending. Otherwise you are going to be
sending an incomplete response to the client. Also, whenever you are
catching exceptions, you are simply calling 'Exit' without letting the
exception make its way back to TIdPOP3Server. You should let exceptions
make their way back so that it can know that something wrong happened and
return an error response to the client. In back, to make sure that you do
not send an incomplete respnse, you should buffer the response using
OpenWriteBuffer() and CloseWriteBuffer(), with CancelWriteBuffer() in case
an error occurs.
Quote
AMsg.NoEncode := true;
AMsg.NoDecode := true;
Since you are essentially loading and message file without any extra
processing whatever, then why not just load the file into a stream, such as
TFileStream, and send it directly without using TIdMessage at all?
Quote
if ( lowercase(trim(e.Message)) <>'socket error # 0' ) then
begin
An easier way to test the error would be to cast the exception to
EIdSocketError (or just catch EIdSocketError to begin with) and then use its
ErrorCode property:
except
on e: EIdSocketError do begin
AddToLog('ASender.Thread.Connection.WriteStream exception: ' +
e.Message );
if ( e.ErrorCode <>0 ) then exit;
end;
on e: exception do begin
AddToLog('ASender.Thread.Connection.WriteStream exception: ' +
e.Message );
exit;
end;
end;
With all that said, try this instead (untested):
procedure TMainForm.PopServerRETR(ASender: TIdCommand; AMessageNum:
Integer);
var
sMsgFile: string;
AStream: TMemoryStream;
AMsg : TIdMessage;
begin
with TVsUserData(ASender.Thread.Data) do
begin
AddToLog( 'C: RETR ' + IntToStr(AMessageNum) );
sMsgFile := MsgPath +
TVsMsgInfo(MsgList.Items[AMessageNum-1]).FileName;
AddToLog( 'About to send contents of ' + sMsgFile );
try
try
AMsg := TIdMessage.create(nil);
AMsg.NoEncode := true;
AMsg.NoDecode := true;
except
on e: Exception do AddToLog('TIdMessage.create
exception: ' + e.Message);
Raise; // so TIdPop3Server knows something went wrong
end;
try
AMsg.LoadFromFile(sMsgFile, false);
except
on e: exception do AddToLog('TIdMessage.LoadFromFile
exception: ' + e.Message );
Raise; // so TIdPop3Server knows something went wrong
end;
try
AStream := TMemoryStream.Create;
except
on e: Exception do AddToLog('TMemoryStream.create
exception: ' + e.Message );
Raise; // so TIdPop3Server knows something went wrong
end;
try
AMsg.SaveToStream(AStream, false);
AStream.Seek(0, soFromBeginning);
except
on e: Exception do AddToLog('TIdMessage.SaveToStream
exception: ' + e.Message );
Raise; // so TIdPop3Server knows something went wrong
end;
ASender.Thread.Connection.OpenWriteBuffer;
try
ASender.Thread.Connection.WriteLn('+OK ' +
IntToStr(AStream.Size) + ' octets');
ASender.Thread.Connection.WriteStream(AStream);
ASender.Thread.Connection.CloseWriteBuffer;
except
on e: Exception do begin
ASender.Thread.Connection.ClearWriteBuffer;
AddToLog('ASender.Thread.Connection exception: ' +
e.Message );
Raise; // so TIdPop3Server knows something went
wrong
end;
AddToLog( 'S: <contents of ' + sMsgFile + ' sent
successfully>' );
finally
if Assigned(AStream) then FreeAndNil(AStream);
if Assigned(AMsg) then FreeAndNil(AMsg);
end;
end;
end; // RETR
Or simply:
procedure TMainForm.PopServerRETR(ASender: TIdCommand; AMessageNum:
Integer);
var
sMsgFile: string;
AStream: TFileStream;
begin
with TVsUserData(ASender.Thread.Data) do
begin
AddToLog( 'C: RETR ' + IntToStr(AMessageNum) );
sMsgFile := MsgPath +
TVsMsgInfo(MsgList.Items[AMessageNum-1]).FileName;
AddToLog( 'About to send contents of ' + sMsgFile );
try
AStream := TFileStream.Create(sMsgFile, fmOpenRead or
fmShareDenyWrite);
try
ASender.thread.Connection.OpenWriteBuffer;
try
ASender.thread.Connection.WriteLn('+OK ' +
IntToStr(AStream.Size) + ' octets');
ASender.Thread.Connection.WriteStream(AStream);
ASender.thread.Connection.CloseWriteBuffer;
AddToLog( 'S: <contents of ' + sMsgFile + ' sent
successfully>' );
except
ASender.thread.Connection.ClearWriteBuffer;
Raise;
end;
finally
FreeAndNil(AStream);
end;
except
on e: Exception do begin
AddToLog('Exception: ' + e.Message );
Raise; // so TIdPop3Server knows something went wrong
end;
end;
end;
end; // RETR
Gambit
 

Re:Socket Error # 0 in RETR handler

You, Sir... are my hero!!! It appears to have worked. I guess I need a
little help with the try...except blocks huh? <grin>
The problem seems to have been fixed by the placement of the +OK statement.
Go figure.
"Remy Lebeau (TeamB)" <XXXX@XXXXX.COM>writes
Quote

"Vern Six" <XXXX@XXXXX.COM>writes
news:3fa0728f$XXXX@XXXXX.COM...

>s := '+OK ' +
IntToStr(TVsMsgInfo(MsgList.Items[AMessageNum-1]).Size)
+
>' octets';
>ASender.thread.Connection.WriteLn(s);

I would not recommend you call WriteLn() until after you have already
loaded
and prepared the TIdMessage for sending. Otherwise you are going to be
sending an incomplete response to the client. Also, whenever you are
catching exceptions, you are simply calling 'Exit' without letting the
exception make its way back to TIdPOP3Server. You should let exceptions
make their way back so that it can know that something wrong happened and
return an error response to the client. In back, to make sure that you do
not send an incomplete respnse, you should buffer the response using
OpenWriteBuffer() and CloseWriteBuffer(), with CancelWriteBuffer() in case
an error occurs.

>AMsg.NoEncode := true;
>AMsg.NoDecode := true;

Since you are essentially loading and message file without any extra
processing whatever, then why not just load the file into a stream, such
as
TFileStream, and send it directly without using TIdMessage at all?

>if ( lowercase(trim(e.Message)) <>'socket error # 0' ) then
begin

An easier way to test the error would be to cast the exception to
EIdSocketError (or just catch EIdSocketError to begin with) and then use
its
ErrorCode property:

except
on e: EIdSocketError do begin
AddToLog('ASender.Thread.Connection.WriteStream exception: ' +
e.Message );
if ( e.ErrorCode <>0 ) then exit;
end;
on e: exception do begin
AddToLog('ASender.Thread.Connection.WriteStream exception: ' +
e.Message );
exit;
end;
end;


With all that said, try this instead (untested):

procedure TMainForm.PopServerRETR(ASender: TIdCommand; AMessageNum:
Integer);
var
sMsgFile: string;
AStream: TMemoryStream;
AMsg : TIdMessage;
begin
with TVsUserData(ASender.Thread.Data) do
begin
AddToLog( 'C: RETR ' + IntToStr(AMessageNum) );

sMsgFile := MsgPath +
TVsMsgInfo(MsgList.Items[AMessageNum-1]).FileName;
AddToLog( 'About to send contents of ' + sMsgFile );

try
try
AMsg := TIdMessage.create(nil);
AMsg.NoEncode := true;
AMsg.NoDecode := true;
except
on e: Exception do AddToLog('TIdMessage.create
exception: ' + e.Message);
Raise; // so TIdPop3Server knows something went
wrong
end;

try
AMsg.LoadFromFile(sMsgFile, false);
except
on e: exception do AddToLog('TIdMessage.LoadFromFile
exception: ' + e.Message );
Raise; // so TIdPop3Server knows something went
wrong
end;

try
AStream := TMemoryStream.Create;
except
on e: Exception do AddToLog('TMemoryStream.create
exception: ' + e.Message );
Raise; // so TIdPop3Server knows something went
wrong
end;

try
AMsg.SaveToStream(AStream, false);
AStream.Seek(0, soFromBeginning);
except
on e: Exception do AddToLog('TIdMessage.SaveToStream
exception: ' + e.Message );
Raise; // so TIdPop3Server knows something went
wrong
end;

ASender.Thread.Connection.OpenWriteBuffer;
try
ASender.Thread.Connection.WriteLn('+OK ' +
IntToStr(AStream.Size) + ' octets');
ASender.Thread.Connection.WriteStream(AStream);
ASender.Thread.Connection.CloseWriteBuffer;
except
on e: Exception do begin
ASender.Thread.Connection.ClearWriteBuffer;
AddToLog('ASender.Thread.Connection exception: ' +
e.Message );
Raise; // so TIdPop3Server knows something went
wrong
end;

AddToLog( 'S: <contents of ' + sMsgFile + ' sent
successfully>' );
finally
if Assigned(AStream) then FreeAndNil(AStream);
if Assigned(AMsg) then FreeAndNil(AMsg);
end;
end;
end; // RETR

Or simply:

procedure TMainForm.PopServerRETR(ASender: TIdCommand; AMessageNum:
Integer);
var
sMsgFile: string;
AStream: TFileStream;
begin
with TVsUserData(ASender.Thread.Data) do
begin
AddToLog( 'C: RETR ' + IntToStr(AMessageNum) );

sMsgFile := MsgPath +
TVsMsgInfo(MsgList.Items[AMessageNum-1]).FileName;
AddToLog( 'About to send contents of ' + sMsgFile );

try
AStream := TFileStream.Create(sMsgFile, fmOpenRead or
fmShareDenyWrite);
try
ASender.thread.Connection.OpenWriteBuffer;
try
ASender.thread.Connection.WriteLn('+OK ' +
IntToStr(AStream.Size) + ' octets');
ASender.Thread.Connection.WriteStream(AStream);
ASender.thread.Connection.CloseWriteBuffer;
AddToLog( 'S: <contents of ' + sMsgFile + ' sent
successfully>' );
except
ASender.thread.Connection.ClearWriteBuffer;
Raise;
end;
finally
FreeAndNil(AStream);
end;
except
on e: Exception do begin
AddToLog('Exception: ' + e.Message );
Raise; // so TIdPop3Server knows something went
wrong
end;
end;
end;
end; // RETR


Gambit