I use the following code, results are correct, but gethostbyaddr takes around 30 seconds.
function IPAddrToName(IPAddr: string): string;
var
SockAddrIn: TSockAddrIn;
HostEnt: PHostEnt;
WSAData: TWSAData;
begin
WSAStartup($101, WSAData);
SockAddrIn.sin_addr.s_addr := inet_addr(PChar(IPAddr));
HostEnt := gethostbyaddr(#SockAddrIn.sin_addr.S_addr, 4, AF_INET);
if HostEnt <> nil then
Result := StrPas(Hostent^.h_name)
else
Result := '';
end;
That's unlikely to be an issue with your code (unless WSAStartup is particularly slow).
The first thing I would do is to output the time (preferably to the millisecond, with GetTickCount, I think) in between each of those lines in your code, to find out exactly where the time is being spent.
The gethostbyaddr may well have to go out to a remote DNS machine to resolve the IP address into a hostname.
If your network is set up badly, or the DNS server containing that address is in the remote regions of the Tibetan mountains for example, resolution will take some time.
From your command line, enter:
nslookup x.x.x.x
(where x.x.x.x is the IP address you're interested in) and see how long that takes.
Based on your comment between the ruler lines below:
I am working on LAN with just 3 machines. Also that network is not connected to the internet. It takes 16 secs (+/- some millisecs) for only the line:
HostEnt := gethostbyaddr(#SockAddrIn.sin_addr.S_addr, 4, AF_INET);
while:
GetHostByName(PChar(HostName));
is instantaneous. Below is the output of Ping (instant output) and nslookup:
c:\> ping 192.168.1.22
Reply from 192.168.1.22: bytes=32 time<1ms TTL=128 Packets:
Sent = 4, Received = 4, Lost = 0 (0% loss)
c:\> nslookup 192.168.1.22
DNS request timed out.
I think your problem is with that timeout. It appears your network is set up okay for DNS name resolution but not IP reverse resolution.
When you just type nslookup, it should show you your DNS server that it's trying to use and this will probably give you a clue.
c:\pax> nslookup
Default Server: pax01.neveryoumind.com
Address: 9.190.230.75
It may be that resolving names to IP addresses doesn't go out through DNS but is instead handled with local information.
That's about as much help as I can give you with the current information. Since this now seems very much a SuperUser question now rather than StackOverflow, I'll nudge it over to there.
Windows attempts different ways to perform host name resolution depending on the way your hosts and LAN are configured. See http://technet.microsoft.com/en-us/library/bb727005.aspx.
You should not test that code in a LAN which is not correctly configured, either using a DNS server (or at least a WINS one) or correct hosts files. Otherwise you could not get the result you'd expect in a properly configured LAN.
Related
I'm trying to connect to an RN42, module through python. When the RN42 pairs with W10 it creates two virtual COM ports(outgoing and incoming). I need to connect to the outgoing port.
I'm trying to do this automatically. I've tried:
import serial
import serial.tools.list_ports as port_lst
ports = list(port_lst.comports())
bluetooth_ports = []
for p in ports:
if 'Bluetooth' in p.description:
bluetooth_ports += [p.device]
bluetooth_com = serial.Serial(bluetooth_ports[0],115200)
I thought that the first port was usually the outgoing one, but I've paired the module to another computer, and this didn't apply (the second port was the outgoing one). Is there a way to find out the direction of the COM ports?
Thanks!!!
Although this is an antique question, I have been searching for the answer to this for some time myself and since I finally figured it out I wanted others to be able to find the answer. With help from a blog entry at in the hand and its accompanying gist:
The trick is to acquire the hwid using pySerial, then parse the address. The incoming port in a pair has an address of zero and the outgoing port has a nonzero address. Here is some ugly Python code that decodes it:
import serial.tools.list_ports
cp=serial.tools.list_ports.comports()
for p in cp:
if "BTHENUM" in p.hwid:
start_of_address=p.hwid.rfind("&")
end_of_address=p.hwid.rfind("_")
address=p.hwid[start_of_address+1:end_of_address]
if int(address,16)==0:
port_type="incoming"
else:
port_type="outgoing"
print(p.hwid)
print(p.name, address, port_type)
And the output:
BTHENUM\{00001101-0000-1000-8000-00805F9B34FB}_LOCALMFG&0000\7&CC47540&0&000000000000_000000A8
COM4 000000000000 incoming
BTHENUM\{00001101-0000-1000-8000-00805F9B34FB}_LOCALMFG&0002\7&CC47540&0&209BA5420081_C00000000
COM5 209BA5420081 outgoing
I would use the power of Firebird Events with delphi application with TIBEvents component.
The problem is the firewall, not every time have the correct role and when I try to register the events the application stops responding and I must wait...
How can I do?
I also try to call register function in a separated thread but with the same result.
function RegisterEvents(data : Pointer) : Integer;
begin
with Form1 do begin
DBOspitiEvent.Registered := true;
end;
end; //<-- AFTER THIS, APPLICATION IS BLOCKED (for a while)
procedure TForm1.Button2Click(Sender: TObject);
var
ThreadId : Cardinal;
ThreadHandle : Integer;
begin
ThreadHandle := BeginThread(nil,0,#RegisterEvents,nil,0,ThreadId);
if ThreadHandle = 0
then ShowMessage('Error');
end;
For events, a client needs to establish a separate connection, and by default Firebird uses a random port for this. In combination with firewalls, this leads to problems because the port is - for example - not allowed.
You can configure Firebird to use a fixed port, by editing firebird.conf and setting the RemoteAuxPort to a fixed value (eg 3051), and restarting Firebird. You can then configure your firewall to allow this port.
See also How to configure events with firewall?
We had many problems with TIBEvent components since XP SP3 too.
Here is what works perfectly fine for us since than, even with newest Win10 updates:
We use UIB for receiving events. TUIBEvents component. (You may keep IBX for everything else... but UIB is better and faster and FB3.0 compatible.)
Do not execute any long-lasting code inside OnEvent procedure, but rather set multithread-safe variables to mark, what event you received (last time)
Deal with those variable inside each window locally. (For example: running a timer that checks and compares last-event-time with local last-refresh-time.)
If you are running SQL queries in a separate Thread, always create new database + transaction component, DO NOT USE the one from the main thread!
Open 3050 and 3051 TCP ports on Firewall!
We also add "fbserver.exe" file to Firewall exception and to Defender exception.
Set fixed ports in "firebird.conf" file: RemoteServicePort=3050 and RemoteAuxPort=3051
You may create a single FirstInstallScrip.bat file to do all these firewall changes and copies a pre-edited .conf file to FB's directory, overwriting the original one.
And YES, you can create these simple text files easily from Delphi and run it from there. Or notify the user if not ran yet. (You can read the original config file and compare those settings.)
#ECHO program in
#NETSH advfirewall firewall add rule name="FirebirdSQL szerver" program="%programfiles%\Firebird\Firebird_2_5\bin\fbserver.exe" profile=public,private,domain dir=in action=allow edge=yes description="FirebirdSQL Database engine"
#ECHO program out
#NETSH advfirewall firewall add rule name="FirebirdSQL szerver" program="%programfiles%\Firebird\Firebird_2_5\bin\fbserver.exe" profile=public,private,domain dir=out action=allow description="FirebirdSQL Database engine"
#ECHO ports in
#NETSH advfirewall firewall add rule name="FirebirdSQL portok" localport=3050-3051 protocol=tcp profile=public,private,domain dir=in action=allow edge=yes description="FirebirdSQL Database engine ports"
#ECHO ports out
#NETSH advfirewall firewall add rule name="FirebirdSQL portok" localport=3050-3051 protocol=tcp profile=public,private,domain dir=out action=allow description="FirebirdSQL Database engine ports"
#pause
As #Victoria suggested: use better business logic!
You may store your data (red from DB) in PC's memory and show it from there.
Create triggers inside your FB database self-updating the last modification.
Do not delete rows, just mark them as "deleted".
check only "what changed" SELECT * from "Customers" c where c.MODIFIED > '2019...' and compare with data already downloaded before, to reduce SQL load.
use separate, short-time transaction to write data into DB.
Recently I made some very small, very light app that uses two UDP client and two UDP Server components. Apart from several functions, it has basically nothing else in it.
I was stuck for a while with "Cannot bind socket. Address and port already in use", but somehow solved that with changing the ports, using rsTrue property on Reuse Socket, but somehow, several users still got that error when they try to run the connection from the app.
The server and client has to use same port and same address, as the UDPClient needs to have BoundPort the same as UDPServer's binding port. That's because the responses are being sent to the same port the request came from. This was causing the primary error, which was solved with rsTrue property.
Now I wonder, is there any chance that system overrides this settings by something, I'm not familiar with? Because on 12 computers I have tested so far, it worked fine (on one I had to kill Bonjour service as it was using the exact port that I needed, but other than that...)
What could be causing errors otherwise?
These are settings which are being called on Connect button click:
Server.Binding.IP:=Interface.Text;
Server.Binding.Port:=StrToInt(Port.Text);
Server.DefaultPort:=StrToInt(Port.Text);
Client.Host:= DeviceIP.Text;
Client.Port:= 10023;
Client.BoundIP:= Interface.Text;
Client.BoundPort:= StrToInt(Port.Text);
LocalServer.Binding.IP:= '127.0.0.5';
LocalServer.Binding.Port:= 10023;
LocalServer.DefaultPort:= 10023;
LocalClient.BoundIP:= '127.0.0.1';
LocalClient.BoundPort:= 10024;
LocalClient.Host:= '127.0.0.1';
LocalClient.Port:= 10023;
try Client.Active:=True; finally end;
try Server.Active:=True; finally end;
try LocalClient.Active:=True; finally end;
try LocalServer.Active:=True; finally end;
Where Port is the TEdit field for user to enter port, DeviceIP the device's IP, and Interface the local network interface IP that he will use.
The only hardcoded thing that doesn't really need to be hardcoded is LocalClient.BoundPort:=10024, it's just to make GUI as light as possible. But changing this doesn't help either.
The app is kind of a proxy server between the original App on PC, and the device on network.
The 10023 must be set as is, since the device only listens on that port, and the original app only sends to that port!
Any help would be appreciated.
You do not need to use separate TIdUDPClient and TIdUDPServer on the same port. You can use TIdUDPServer by itself and let it handle both sending and receiving. No need to use a separate TIdUDPClient at all.
Also, you are not using the Binding property correctly. When you read a server's Binding property for the first time, the socket is bound using the settings from the server's Bindings collection. Your assignments to Binding.IP and Binding.Port are then ignored. You need to configure the server's Bindings collection first, then use the server's Binding property only for sending (the server will handle the reading for you).
Try this:
Server.Active := False;
Server.Bindings.Clear;
Server.DefaultPort := StrToInt(Port.Text);
Server.Bindings.Add.IP := Interface.Text;
Server.Active := True;
...
Server.Binding.Send(DeviceIP.Text, 10023, data here);
Replies will arrive in the server's OnUDPRead event.
BTW, since you are writing a proxy, have a look at the TIdMappedPortUDP component.
i need win XP service with TTcpServer.
application was created by "File->New->Other->ServiceApplication"
TTcpServer.localport := 33000
server registered with exename.exe /install
everything looks good, even netstat -a shows that port 33000 - LISTENING
but i can`t access that port from outside of this machine. only local.
and when i make the standard application with same params - all ok.
EDIT1
TTcpServe.OnAccept =
procedure TFlexorXL.tcpServerAccept(Sender: TObject;
ClientSocket: TCustomIpClient);
var str: string;
begin
if ClientSocket.Connect then
begin
str := ClientSocket.Receiveln;
ClientSocket.Sendln('test');
//ClientSocket.Disconnect;
end;
end;
TCP/IP works just fine in a service (I use it all the time), so you are likely just misusing the TTcpServer component (which is possible, because it is a horribly written component).
If the TTcpServer.LocalHost property is blank then the socket will bind to all available local IPv4 addresses, otherwise it will bind only to the particular IPv4 address that you specify (netstat will show you the actual IP that the server is actually bound to). That is the IP that you must have clients connect to. In the case of 0.0.0.0, you can connect to any IP that belongs to the server's machine.
With that said, in order to actually accept clients, you must either:
set the TTcpServer.BlockMode property to bmThreadBlocking. The server will then use an internal worker thread to accept connections, and each client will run in its own worker thread. However, you must perform all of your client-related logic inside of the TTcpServer.OnAccept event, because a client will be disconnected immediately after that event handler exits.
for any other value of BlockMode, you must call TTcpServer.Accept() yourself, such as in a timer or thread. If you call the overloaded version of Accept() that has no parameters, you must perform all of your client-related logic inside of the TTcpServer.OnAccept event, because the client will be disconnected immediately after that event handler exits. If you call the other overloaded version of Accept() that returns a TCustomIpClient object, then you control the lifetime of that object and can use it however you need.
With that said, if you are doing all of that, and still having problems, then you need to provide more information about your actual TTcpServer setup, show some actual TTcpServer code, etc. As it currently stands, you have not provides enough details to diagnose your problem.
i need trace the route of an ip, actually i am using the TIdTraceRoute indy component from the idTraceRoute unit.
IdTraceRoute1:= TIdTraceRoute.Create(Self);
IdTraceRoute1.ResolveHostNames:= True;
IdTraceRoute1.ReceiveTimeout:= 5000;
IdTraceRoute1.OnReply:= TraceRoute;
IdTraceRoute1.Host:= 'www.google.com';
IdTraceRoute1.Trace;
procedure TForm1.TraceRoute(ASender: TComponent;
const AReplyStatus: TReplyStatus);
begin
Memo1.Lines.Add(AReplyStatus.FromIpAddress);
end;
but always return.
0.0.0.0
0.0.0.0
0.0.0.0
0.0.0.0
0.0.0.0
0.0.0.0
exist another way of trace an ip maybe using windows api or using another indy component?
actually i am using delphi-xe and Windows 7.
I just wrote a entry on my blog, wich can help you.
Building a traceroute application with IP geolocation using delphi
Trace is basically based on sending ICMP packets starting with a TTL of 1 and increasing it until reaching the destination. Because each router decrease the TTL, and when it reaches 0 an error is returned to the caller, it can be used to track the "route" packets takes. Note that to work the ICMP protocol must not be stopped by a firewall. ICMP is a protocol that runs atop IP, like TCP does. It doesn't use TCP. You could code a traceroute utility just using ICMP. But does the Windows tracert utility work on your system?