I am testing IndyFTP to upload a file to a server. The file is uploaded but has 0 bytes because there is a EIdAccessTimeout exception - 'Accept timed out". How can I prevent the exception? Is my code incorrect? The code is shown below:
procedure TForm1.FTPUpload1Click(Sender: TObject);
{ Indy FTP Upload. }
var
iHost: string;
iUsername: string;
iPassword: string;
iFolder: string;
iSourceFile: string;
iDestFile: string;
iAppend: boolean;
iStartPos: Int64;
begin
iHost := FTPHost1.Text;
iUsername := Username1.Text;
iPassword := Password1.Text;
iFolder := ServerFolder1.Text;
if FileExists(SourceFile1.Text) then
iSourceFile := SourceFile1.Text
else
Exit;
if FileExists(SourceFile1.Text) then
iDestFile := ExtractFileName(SourceFile1.Text)
else
Exit;
iAppend := False;
iStartPos := -1;
IdFTP1.Host := iHost;
IdFTP1.Username := iUsername;
IdFTP1.Password := iPassword;
IdFTP1.Connect;
IdFTP1.TransferType := ftBinary;
IdFTP1.Put(iSourceFile);
IdFTP1.Disconnect;
end;
There are some unused vars listed because I am just learning and have not used some of the parameters yet.
Most likely, your FTP client is set to ACTIVE mode, so this error means that after a successful login to the FTP server, the "reverse" connection couldn't be established (the file transfer).
In active mode FTP the client connects from a random unprivileged port
(N > 1023) to the FTP server's command port, port 21. Then, the client
starts listening to port N+1 and sends the FTP command PORT N+1 to the
FTP server. The server will then connect back to the client's
specified data port from its local data port, which is port 20.
Active FTP vs. Passive FTP, a Definitive Explanation
You can set to passive mode this way:
IdFTP1.Passive := True;
EDIT
In addition, use try-except-finally blocks, so you can do some error handling. Something like:
try
IdFTP1.Connect;
try
IdFTP1.Put(...);
finally
IdFTP1.Disconnect;
end;
except
// couldn't connect
end;
Related
The Environment
I've created a web server in Delphi using Indy component TidHTTPServer. I'm using Delphi XE2 which came with Indy version 10.5.8. The
server is running as a desktop app with a form that displays a log of the connections and their requests. It is running on Windows 7
Professional. Requests are for SQL data from a Firebird database. The response is JSON. All traffic is HTTP.
The Challenge
When I was testing it with a small number of users everything worked great. Now that I have rolled it out to about 400 users there are
communication problems. The server stops responding to requests and the only way I can get it to respond again is to reboot the machine it is running on and then restart it. The need to reboot occurs more frequently during
high volume times.
The Symptoms
Using Windows netstat I have noticed that whenever a TCP connection of type CLOSE_WAIT occurs the server stops responding to requests and I have to reboot again
The Test Procedure
I have been able to simulate this hanging even when there is no traffic on the server. I created a web page that sends multiple requests with
a delay between each request.
The web page let's me specify the number of requests to make, how long to wait between each request, and how long to wait before timing out. Even at one millisecond between requests the server seems to respond without issue.
The Test Results
If I set the time out period of each request to a very small number, like 1 msec, I can make my Delphi HTTP Server hang. At a 1 msec timeout requests to my server fail every time, as I would expect. The time out is so short my server can't possibly respond quickly enough.
What I don't understand is that after I force this timeout at the client side, even a relatively small number of requests (fewer than 50), my Delphi web server no longer responds to any requests. When I run netstat on the server machine there are a number of CLOSE_WAIT socket connections. Even after an hour and after closing my server the CLOSE_WAIT socket connections persist.
The Questions
What is going on? Why does my Delphi Indy idHTTPServer stop responding when there are (even just one) CLOSE_WAIT socket connection? The CLOSE_WAITs don't go away and the server does not start responding again. I have to reboot.
What am I not doing?
Here is the results of netstat command showing CLOSE_WAITs:
C:\Windows\system32>netstat -abn | findstr 62000
TCP 0.0.0.0:62000 0.0.0.0:0 LISTENING
TCP 10.1.1.13:62000 9.49.1.3:57036 TIME_WAIT
TCP 10.1.1.13:62000 9.49.1.3:57162 CLOSE_WAIT
TCP 10.1.1.13:62000 9.49.1.3:57215 CLOSE_WAIT
TCP 10.1.1.13:62000 9.49.1.3:57244 CLOSE_WAIT
TCP 10.1.1.13:62000 9.49.1.3:57263 CLOSE_WAIT
TCP 10.1.1.13:62000 9.49.1.3:57279 ESTABLISHED
TCP 10.1.1.13:62000 104.236.216.73:59051 ESTABLISHED
Here is the essence of my web server:
unit MyWebServer;
interface
Uses
...
Type
TfrmWebServer = class(TForm)
...
IdHTTPServer: TIdHTTPServer;
...
procedure IdHTTPServerCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
procedure IdHTTPServerDisconnect(AContext: TIdContext);
procedure btnStartClick(Sender: TObject);
...
dbFirebird : TIBDatabase;
txFireird : TIBTransaction;
...
private
function CreateSomeResponseStringData: string;
end;
implementation
procedure TfrmWebServer.btnStartClick(Sender: TObject);
begin
{set the IP's and proit to listen on}
IdHTTPServer.Bindings.Clear;
IdHTTPServer.Bindings.Add.IP := GetSetting(OPTION_TCPIP_ADDRESS);
IdHTTPServer.Bindings.Add.Port := Str2Int(GetSetting(OPTION_TCPIP_PORT));
{start the web server}
IdHTTPServer.Active := TRUE;
...
dbFirebird.Transactrion := txFirebird;
...
end;
procedure TfrmWebServer.IdHTTPServerCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
qryFirebird : TIBSql;
function CreateSomeResponseStringData: string;
begin
qryFirebird := NIL;
qryFirebird := TIBSql.Create(IdHTTPServer);
qryFirebird.Database := dbFirebird;
dbFirebird.Connected := FALSE;
dbFirebird.Connected := TRUE;
qryFirebird.Active := TRUE;
Result := {...whatever string will be returned}
end;
function CreateAnErrorResponse: string;
begin
Result := {...whatever string will be returned}
end;
begin
try
AResponseInfo.ContentText := CreateSomeResponseStringData;
{Clean up: What do I do here to make sure that the connection that was served is:
- properly closed so that I don't run out of resourses?
- anything that needs to be cleaned up is freed so no memory leaks
- TIME_WAIT, CLOSE_WAIT, any other kind of _WAITs are not accumulating?}
except;
AResponseInfo.ContentText := CreateAnErrorResponse;
end;
qryFirebird.Free;
end;
procedure TfrmWebServer.IdHTTPServerDisconnect(AContext: TIdContext);
begin
{Maybe I do the "Clean Up" here? I tried Disconnect as shown but still lots of
TIME_WAIT tcp/ip connections accumulate. even after the app is closed}
AContext.Connection.Disconnect;
end;
end.
There are at least two major issues with this code that could cause the crashing:
The database and transaction objects are global to all threads created by IdHTTPServer. When you disconnect the database it would disconnect for all threads.
If there is a run time error assigning content text this line AResponseInfo.ContentText := CreateAnErrorResponse; is not in an exception block.
Here is how I would fix this:
...
procedure TfrmWebServer.btnStartClick(Sender: TObject);
begin
{set the IP's and port to listen on}
IdHTTPServer.Bindings.Clear;
IdHTTPServer.Default.Port := Str2Int(GetSetting(OPTION_TCPIP_PORT));
IdHTTPServer.Bindings.Add.IP := GetSetting(OPTION_TCPIP_ADDRESS);
{start the web server}
IdHTTPServer.Active := TRUE;
...
end;
procedure TfrmWebServer.IdHTTPServerCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
{make these local to each thread}
qryFirebird : TIBSql;
dbFirebird : TIBDatabase;
txFirebird : TIBTransaction;
function CreateSomeResponseStringData: string;
begin
dbFirebird := TIBDatbase.Create(IdHTTPServer);
txFirebird := TIBTransaction.Create(IdHTTPServer);
qryFirebird := TIBSql.Create(IdHTTPServer);
dbFirebird.Transaction := txFirebird;
qryFirebird.Database := dbFirebird;
...Add params that do the log in to database
dbFirebird.Connected := TRUE;
qryFirebird.Active := TRUE;
Result := {...whatever string will be returned}
end;
function CreateAnErrorResponse: string;
begin
Result := {...whatever string will be returned}
end;
begin
try
try
...
AResponseInfo.ContentText := CreateSomeResponseStringData;
...
except;
try
AResponseInfo.ContentText := CreateAnErrorResponse;
except
{give up}
end;
end;
finaly
qryFirebird.Free;
dbFirebird.Free;
txFirebird.Free;
end;
end;
end.
On the below piece of code i have some related questions
var
UseProxy : Boolean = True;
....
var
IdUDPClient : TIdUDPClient;
sText : string;
begin
IdUDPClient := TIdUDPClient.Create(nil);
try
IdUDPClient.Host := '10.10.10.10';
IdUDPClient.Port := 5555;
if UseProxy then begin
IdUDPClient.TransparentProxy.Enabled := True;
IdUDPClient.TransparentProxy.Host := '20.20.20.20';
IdUDPClient.TransparentProxy.Port := 8080;
IdUDPClient.OpenProxy;
end;
try
IdUDPClient.Connect;
except
Writeln('Connect Error.');
end;
if IdUDPClient.Connected then
Writeln('Connected')
else begin
Writeln('Not Connected');
Exit;
end;
try
IdUDPClient.Send('Foo');
try
sText := IdUDPClient.ReceiveString(1000);
Writeln('Received: ', sText);
except
Writeln('Receive Error.');
end;
except
Writeln('Send Error.');
end;
if UseProxy then
IdUDPClient.CloseProxy;
finally
IdUDPClient.Free;
end;
Readln;
end.
Why the try...except block is not catching errors on the UDP
client even if the Host is not reachable nor the port is closed e.g. IdUDPClient.Connected always True?
When using the proxy i'm not sure if my implementation is correct above
because if UseProxy is True the IdUDPClient try to directly send
the request to 10.10.10.10not thru the proxy server. How i can fix this? what i'm doing wrong?
My test scenario for the proxy was as follow:
My PC IP 30.30.30.30
UDP Server 10.10.10.10
Proxy Sever (Socks 5) 20.20.20.20
30.30.30.0/24 cant reach 10.10.10.10 but 20.20.20.20 does.
if my PC can access the UDP server directly and i put fake proxy server (unused IP with random port) to the client. the client can reach the UDP server which is not supposed to be reached because the proxy is down. How i cant prevent such scenario ?
UDP is connection-less, so Connected cannot tell you whether the remote peer is reachable or not.
"Connecting" a UDP socket simply assigns a local association with the peer IP/Port. That way, outbound packets are sent only to that peer, and only packets received from that peer are accepted. Nothing more. There is no actual connection, like in TCP.
And unlike TCP, sending a UDP packet just dumps the packet onto the network, there is no acknowledgement that the packet ever reaches the peer. Acks have to be implemented at the application layer.
As for exceptions, they are only raised when an actual error occurs. In UDP, the only way an unreachable Host would cause socket errors is if the network sends back an ICMP packet to indicate the Host is not reachable. A "connected" UDP socket will receive such packets internally and start reporting failures on subsequent reads/sends with the same peer. So, in your example, the call to Send() will never raise an error about not being about to reach the Host, because it simply does not know. ReceiveString() has a better chance of reporting such errors, however the way it is currently implemented, it would likely just ignore them because it checks if the socket is readable (has a pending UDP packet) before actually reading it. A ICMP packet may not make the socket enter a readable state.
Since you are specifying a timeout on ReceiveString(), you will just have to assume the peer is gone if the timeout elapses without receiving an actual string.
Your code is bypassing the proxy because you are not setting up the TransparentProxy correctly.
When you access the TransparentProxy property, if no proxy component is currently assigned then the property getter creates an internal TIdSocksInfo object. TIdSocksInfo has a Version property that defaults to svNoSocks. Since you are trying to connect through a SOCKS v5 proxy, you need to set the Version to svSocks5 instead:
if UseProxy then begin
(IdUDPClient.TransparentProxy as TIdSocksInfo).Version := svSocks5; // <--
IdUDPClient.TransparentProxy.Host := '20.20.20.20';
IdUDPClient.TransparentProxy.Port := 8080;
end;
The TransparentProxy.Enabled property setter is not used with TIdSocksInfo at all, it is a no-op. However, the Enabled property getter returns True/False depending on the value of the TIdSocksInfo.Version property.
And you don't need to call OpenProxy() and CloseProxy() manually, TIdUDPClient.Connect() and TIdUDPClient.Disconnect() will handle that for you when the TransparentProxy is enabled.
Now, with all of that said, try this:
var
UseProxy : Boolean = True;
...
var
IdUDPClient : TIdUDPClient;
IdSocksInfo : TIdSocksInfo;
sText : string;
begin
IdUDPClient := TIdUDPClient.Create(nil);
try
IdUDPClient.Host := '10.10.10.10';
IdUDPClient.Port := 5555;
if UseProxy then begin
IdSocksInfo := TIdSocksInfo.Create(IdUDPClient);
IdSocksInfo.Version := svSocks5;
IdSocksInfo.Host := '20.20.20.20';
IdSocksInfo.Port := 8080;
IdUDPClient.TransparentProxy := IdSocksInfo;
end;
try
IdUDPClient.Connect;
except
on E: Exception do begin
Writeln('Connect Error: ', E.Message);
Exit;
end;
end;
try
Writeln('Connected.');
try
IdUDPClient.Send('Foo');
except
on E: Exception do begin
Writeln('Send Error: ', E.Message);
Exit;
end;
end;
try
sText := IdUDPClient.ReceiveString(1000);
if sText <> '' then
Writeln('Received: ', sText)
else
Writeln('Nothing Received after 1 second.');
except
on E: Exception do begin
Writeln('Receive Error: ', E.Message);
end;
end;
finally
IdUDPClient.Disconnect;
end;
finally
IdUDPClient.Free;
end;
end.
The following code always returns True on my system:
uses
WinInet;
function CheckInternetConnection() : Boolean;
var
dwConnectionTypes: Integer;
begin
dwConnectionTypes := (
INTERNET_CONNECTION_MODEM +
INTERNET_CONNECTION_LAN +
INTERNET_CONNECTION_PROXY); // "dwConnectionTypes" now "7"
if (InternetGetConnectedState(#dwConnectionTypes, 0)) then
Result := True // Always hit, "dwConnectionTypes" now "18"
else
Result := False; // Never reaches here!
end;
I've tried:
* unplugging the network cable
* stopped "Wireless Zero Configuration" service
* disabled all connections in Control Panel > Network Connections
* definitely confirmed no internet connection in a web browser
What am I missing?
UPDATE
I've confirmed that dynamically loading wininet.dll and using GetProcAddress to find the method "InternetGetConnectedState" gives exactly the same result with the internet disconnected (returns True and the parameter is set to "18").
If you want to know if you are connected to the Internet, there is no other way that contacting a host on the internet.
Correct technically then you only know if that host is online, but that's often good enough, since if your program requires internet access it's because you need to cantact a host on the internet.
One way of doing that is using a TIdHTTP from Indy:
uses
IdHTTP;
uses
IdHTTP;
function HasInternet: Boolean;
begin
with TIdHTTP.Create(nil) do
try
try
HandleRedirects := True;
Result := Get('http://www.Google.com/') <> '';
except
Result := false;
end;
finally
free;
end;
end;
And then use it :
procedure TForm1.FormCreate(Sender: TObject);
begin
Caption := BoolToStr(HasInternet, True);
end;
But it would be bettet to try to contact you host.
I have a TIdPop3Server in one application that has a IdServerIOHandlerSSLOpenSSL1 attached to it and retrieves emails and sends them to a TIdPop3 client in another application (having TIdSSLIOHandlerSocketOpenSSL attached to it). Everything's fine when the connections are made insecure using port 110. But when I try to use SSL connection through port 995 I get error Connection Closed Gracefully after connect attemp from the client fails. This is my Pop3SeverOnConnect event :
procedure TMainForm.Pop3ServerConnect(AContext: TIdContext);
begin
if (AContext.Connection.IOHandler is TIdSSLIOHandlerSocketBase) then
TIdSSLIOHandlerSocketBase(AContext.Connection.IOHandler).PassThrough :=
(AContext.Binding.Port <> 995);
showmessage('SSL connection made!');
end;
And this is the client-side :
procedure TMainForm.btnCheckMailBoxClick(Sender: TObject);
begin
IdSSLIOHandlerSocketOpenSSL1.PassThrough := False;
POP3Client.IOHandler := IdSSLIOHandlerSocketOpenSSL1;
with POP3Client do begin
AuthType := patUserPass;
Host := myHost;
UserName := myUserName;
Password := myPass;
Port := myPort;
end;
try
POP3Client.Connect;
Except on e : Exception do
showmessage('error=' + e.Message);
end;
// code for retrieving message data
end;
And I always get an exception from Pop3Client.Connect like I've already mentioned above (The message SSL connection made! in the server application never shows up). If I use however another mail client like for example Mozilla Thunderbird I achieve a successful SSL connection for port 995. So the problem should be somewhere in the client's procedure but who knows - that's why I'm asking you guys for help.
In your client code, you need to set the TIdPOP3.UseTLS property instead of the TIdSSLIOHandlerSocketOpenSSL.PassThrough property directly, eg:
procedure TMainForm.btnCheckMailBoxClick(Sender: TObject);
begin
with POP3Client do
begin
IOHandler := IdSSLIOHandlerSocketOpenSSL1;
AuthType := patUserPass;
UseTLS := utUseImplicitTLS; // <-- here
Host := myHost;
UserName := myUserName;
Password := myPass;
Port := myPort;
end;
try
POP3Client.Connect;
try
// code for retrieving message data
finally
POP3Client.Disconnect;
end;
except
on e : Exception do
ShowMessage('error=' + e.Message);
end;
end;
In your server code, you need to get rid of the ShowMessage(). TIdPOP3Server is multi-threaded, the OnConnect event is fired in the context of a worker thread, and ShowMessage() is not thread-safe. If you must display a popup message, use Windows.MessageBox() instead.
Using Delphi XE2, Indy 10 that ships with it and OpenSSL dll's version 1.0.1c.
We are in the final stages of converting our Delphi 6 project to Delphi XE2.
We have solved every show stopper so far but I am running into one trying to connect to Gmail smtp server.
Our Delphi 6 app works fine using SSL v2.3 and SSL v3 and Indy 9 components connecting to port 465 per Google's own documentation. Google Support
Our Delphi XE2 app does not connect at all. The call to connect goes into the ether and nothing happens for some 7 minutes until I get bored waiting for the thing to timeout and kill it off. I actually traced the program execution all the way to the IdWinsock2 funcion here:
function Stub_select(nfds: Integer; readfds, writefds, exceptfds: PFDSet; timeout: PTimeVal): Integer; stdcall;
begin
#select := FixupStub(hWinSockDll, 'select'); {Do not Localize}
Result := select(nfds, readfds, writefds, exceptfds, timeout);//<-this line never returns and timeout is always nil
end;
I need to maintain the old SSL connectivity for existing users' configurations.
New users will be able to connect using TLS (to port 587) and that DOES work!?!?
I am at a loss as to why the non-TLS SSL options do not work and do not report any error and do not time out but just go off to Never-Never Land. Please help!
TJ
Here is the code:
SMTPClient.Host := trim(EditSMTPServer.Text);
SMTPClient.Port := EditSMTPServerPort.AsInteger;//<- value from custom edit box
SMTPClient.Username := trim(EditSMTPLogon.Text);
SMTPClient.Password := trim(EditSMTPPassword.Text);
SMTPClient.AuthType := satDefault;
if CheckBoxUseSSL.Checked then begin
SMTPClient.IOHandler := IdSSLIOHandlerSocket1;
IdSSLIOHandlerSocket1.ReadTimeout := 30000;//30 second timeout which does not appear to work
case ComboBoxSSLMode.ItemIndex of
0 : IdSSLIOHandlerSocket1.SSLOptions.Method := sslvSSLv2;
1 : IdSSLIOHandlerSocket1.SSLOptions.Method := sslvSSLv23;
2 : IdSSLIOHandlerSocket1.SSLOptions.Method := sslvSSLv3;
3 : begin
IdSSLIOHandlerSocket1.SSLOptions.Method := sslvTLsv1;
SMTPClient.UseTLS := utUseImplicitTLS;
end;
end;//case
IdSSLIOHandlerSocket2.SSLOptions.Method := IdSSLIOHandlerSocket1.SSLOptions.Method;
IdSSLIOHandlerSocket2.PassThrough := False;
end
else begin
SMTPClient.IOHandler := nil;
end;
try
SMTPClient.Connect;
if SMTPClient.Connected then begin
if SMTPClient.Authenticate then begin
... do some work ...
end;
end;
except
on e:Exception do begin
showmessage(e.message);
end;
end;
EDIT:
As usual, after I post a question I have stumbled across a workaround.
If I set the UsetTLS property to utUseImplicitTLS for all NON-SSL transactions and set it to utUseExplicitTLS for TLS transactions my connections appear to work in a timely manner.
Hopefully this helps someone out.
updated code:
if CheckBoxUseSSL.Checked then begin
SMTPClient.IOHandler := IdSSLIOHandlerSocket1;
SMTPClient.UseTLS := utUseImplicitTLS;
case ComboBoxSSLMode.ItemIndex of
0 : IdSSLIOHandlerSocket1.SSLOptions.Method := sslvSSLv2;
1 : IdSSLIOHandlerSocket1.SSLOptions.Method := sslvSSLv23;
2 : IdSSLIOHandlerSocket1.SSLOptions.Method := sslvSSLv3;
3 : begin
IdSSLIOHandlerSocket1.SSLOptions.Method := sslvTLsv1;
SMTPClient.UseTLS := utUseExplicitTLS;
end;
end;//case
IdSSLIOHandlerSocket2.SSLOptions.Method := IdSSLIOHandlerSocket1.SSLOptions.Method;
IdSSLIOHandlerSocket2.PassThrough := False;
end
else begin
SMTPClient.UseTLS := utNoTLSSupport;//reset TLS support flag
SMTPClient.IOHandler := nil;
end;
What you discovered is what you are supposed to be doing. SSL and TLS have different semantics, so you have to set UseTLS accordingly.
For SSL on port 465, the server expects your client to initiate an SSL handshake immediately upon connecting, before the server then sends an encrypted Greeting. UseTLS=utUseImplicitTLS does that, but UseTLS=utUseExplicitTLS does not. When UseTLS=utUseExplicitTLS, TIdSMTP will expect the server to send an unencrypted Greeting immediately, which would explain the hang in select() - the server is not sending any data!
For TLS on port 587, the server is expecting the client to connect initially unencrypted and then send an explicit STARTTLS command when it is ready to initiate encryption. UseTLS=utUseExplicitTLS does that, but UseTLS=utUseImplicitTLS does not.