How can Indy's TIdTCPClient and TIdTCPServer be used in the following scenario:
Client ---------- initate connection -----------> Server
...
Client <---------------command------------------- Server
Client ----------------response-----------------> Server
...
Client <---------------command------------------- Server
Client ----------------response-----------------> Server
The client initiates the connection, but acts as a "server" (waiting for commands and executing them).
The OnExecute approach of TIdTCPServer does not work well in this case (at least I am not getting it to work well). How could I do this?
I hope the question is clear enough.
There is nothing preventing you from doing this with Indy's TIdTCPServer component.
A TIdTCPServer only sets up the connection. You'll need to implement the rest. So the sequence of the actual sending and receiving can be whatever you want.
Put this code in your TIdTCPServer component's OnExecute event:
var
sName: String;
begin
// Send command to client immediately after connection
AContext.Connection.Socket.WriteLn('What is your name?');
// Receive response from client
sName := AContext.Connection.Socket.ReadLn;
// Send a response to the client
AContext.Connection.Socket.WriteLn('Hello, ' + sName + '.');
AContext.Connection.Socket.WriteLn('Would you like to play a game?');
// We're done with our session
AContext.Connection.Disconnect;
end;
Here's how you can setup the TIdTCPServer really simply:
IdTCPServer1.Bindings.Clear;
IdTCPServer1.Bindings.Add.SetBinding('127.0.0.1', 8080);
IdTCPServer1.Active := True;
This tells the server to listen on the loopback address only, at port 8080. This prevents anyone outside of your computer from connecting to it.
Then, to connect your client, you can go to a Windows command prompt and type the following:
telnet 127.0.0.1 8080
Here's the output:
What is your name?
Marcus
Hello, Marcus.
Would you like to play a game?
Connection to host lost.
Don't have telnet? Here's how to install telnet client on Vista and 7.
Or with a TIdTCP Client, you can do this:
var
sPrompt: String;
sResponse: String;
begin
// Set port to connect to
IdTCPClient1.Port := 8080;
// Set host to connect to
IdTCPClient1.Host := '127.0.0.1';
// Now actually connect
IdTCPClient1.Connect;
// Read the prompt text from the server
sPrompt := IdTCPClient1.Socket.ReadLn;
// Show it to the user and ask the user to respond
sResponse := InputBox('Prompt', sPrompt, '');
// Send user's response back to server
IdTCPClient1.Socket.WriteLn(sResponse);
// Show the user the server's final message
ShowMessage(IdTCPClient1.Socket.AllData);
end;
An important thing to note here is that the ReadLn statements wait until there is data. That's the magic behind it all.
If your commands are textual in nature, then have a look at the TIdCmdTCPClient component, it is specifically designed for situations when the server is sending commands instead of the client. The server can use TIdContext.Connection.IOHandler.WriteLn() or TIdContext.Connection.IOHandler.SendCmd() to send the commands.
When the client connects to the server, the server has an OnConnect event with an AContext: TIdContext parameter.
A property of this is AContext.Connection, which you can store outside of that event (say, in an Array). If you pair it with the IP or better yet a generated Session ID, then reference that Connection by that criteria, you can then have the server send adhoc commands or messages to the client.
Hope this helps!
normally the client and the server side have a thread that is reading incoming telegrams, and sending pending telegrams...but this kind of protocols (send/receive, when and what) depend of the application.
A very good starting point how the client side can be implemented using a thread, listening for messages from the server, is the Indy Telnet client component (TIdTelnet in the Protocols folder).
The Indy telnet client connects to the telnet server and uses only one socket to write and read data. Reading happens in a listener thread.
This design can easily be adapted to build distributed messaging software like chat etc., and also shows how easy the protocol can be decoupled from the network layer using blocking sockets.
With Indy this is not possible by design:
Indy supports only Client-initiated communication, what means the server can only send a response to requests by the client.
The easiest way (but not the smartest) to get what you want is to use a pull-process. Controlled by a timer the clients ask the server if there is a new command. Of course this will cause a lot of traffic-overhead and depending on your pull-intervall there is a delay.
Alternatively you could use another library like ICS (http://www.overbyte.be/eng/products/ics.html)
Related
Hi is it possible to create intermediary application which will act as tcp proxy server? It would be made of two components. IdTCPServer and IdTCPClient. The idea is:
Application > connects to IdTCPServer > all data is redirected to > IdTCPClient which is connected to the destination.
And the data received by TCPClient will be redirected to the IdTCPServer and Application.
I made such simple app, and it works in HTTP requests, but it fails when I tried to use it for RDP Client ( I got error that Protocol is wrong). Is it even possible? I use ReadByte method, and each single byte is sent to the other IOHandler.
I would like to create some sort of virtual tcp channel which would allow connections over NAT.
I wasn't clear enough. What I want to achieve is something like this:
RDP Server < IdTCPClient <> IdTCPclient > NAT > IdTCPServer < RDPClient.
Application > connects to IdTCPServer > all data is redirected to > IdTCPClient which is connected to the destination.
And the data received by TCPClient will be redirected to the IdTCPServer and Application.
In fact, Indy has a component specifically for that very purpose - TIdMappedPortTCP.
Set its MappedHost and MappedPort properties to point at the intended destination, then activate it, and all inbound connections will automatically be directed to the destination, and data passed back and forth in both directions.
I was going to add just a comment, but don't have enough points for that.
Your request says you want to "redirect", for which Remy supplied the answer.
However, your description sounds like you want to pass the data yourself (man-in-the-middle). RDP contains some guards against that, though earlier versions may have been more open to it.
You may want to specify the question more tightly if actual redirect is not what you are looking for.
I've written a small application using delphi to update my twitter status.
I use Indy 10 and OpenSSL and everything works fine, which means that i can both authenticate my app and update my status.
The thing is that if i use a program like "http analyzer" i can see the request's headers and so i can see sensitive information like the consumer_key.
Is that normal or is it a sign that i have not set properly the iohandler (TIdSSLIOHandlerSocketOpenSSL)?
mSslIoHandler.SSLOptions.Method := sslvSSLv3;
mSslIoHandler.SSLOptions.Mode := sslmBoth;
mSslIoHandler.SSLOptions.VerifyMode := [];
mSslIoHandler.SSLOptions.VerifyDepth := 0;
By providing their own SSL certificates, HTTP analyzers are able to monitor the HTTP traffic as if it was unencrypted. I guess you have to set the analyzer IP address and port as proxy only, and leave the destination server address and port unchanged in the client. Then the analyzer will be able to decrypt your client data with its own key, and forwards it to the destination server. (This is the same way a 'man in the middle'-attack would work)
So yes, it is normall with this type of HTTP analyzers (such as Fiddler)
When I connect to 80 port, plain text web site using TIdTCPClient component, all works fine, data is received without a problem, but, when I connect to 443 port, SSL web site, data not always came.
Maybe something with my receive data block ? Need advice.
while not Terminated do
begin
SetLength(data, 0);
ws.IOHandler.ReadBytes(data, -1);
if Length(data) = 0 then
break;
// processing_my(data);
end;
Thanks
Opening the connection to an other port to have it secure is not enough. To connect to anything SSL, you'll need an IOHandler that supports it, such as TIdSSLIOHandlerSocketOpenSSL
I have a datasnap server with a vcl forms client. From the client, how can I handle if the server has been say shutdown and restarted with existing client connections? This scenario raises a 10053 EIdSocketError exception.
To replicate, I run up both Server and Client, make a call to the server (I use methods exposed via the DataSnap proxy generator) which succeeeds. I then shut down the server (eg Close the application) and restart it. I then attempt to make a call to the server again.
eg:
CLIENT call
sm := TsvrPolicySearchClient.Create(datClientDB.SQLConnection1.DBXConnection);
try
ds := sm.SearchPolicyByPolicy(40, WCRef, '', 3);
dspPolicyGroup.DataSet := ds;
if cdsPolicyGroup.Active then
cdsPolicyGroup.Refresh
else
cdsPolicyGroup.Open;
finally
sm.Free;
end;
dspPolicyGroup is a TDataSetProvider and cdsPolicyGroup is a TClientDataSet (I just use it locally to "store" my TDataSet result).
SERVER
function TsvrPolicySearch.SearchPolicyByPolicy(AClientId: Integer; WCRefNum, ClientRef: string; SearchMethod: Integer): TDataSet;
begin
spPolicyByWCRef.Close;
spPolicyByWCRef.ParamByName('p_client').AsInteger := AClientId;
spPolicyByWCRef.ParamByName('p_search_method').AsInteger := SearchMethod;
spPolicyByWCRef.ParamByName('p_wc_refno').AsString := WCRefNum;
spPolicyByWCRef.Open;
Result := spPolicyByWCRef;
end;
I would think this is regularly encountered by people seeing that it's quite easy to replicate. Should I place a "Test Connection" call or something first (such as a method TestConnection) before each call to check for EIdSocketError (and equivalent) and handle? Or is it more a design flaw perhaps?
Thanks
My scenario:
Client application connect to DataSnap Server (TCP/IP, remote server)
Client request a DataSet (using DataSnap server methods). TClientDataSet
Client downloaded dataset
Server shutdown (taskkill, close application no matters)
Client request a DataSet againt (ehhrrr Socket Error)
Solution:
On client I catch exception (AppEvents.OnException)
Recognize that is Socket error from connection to DataSnap server
I show dialog window with information that connection is lost. User can click "RETRY" - if so,
Free and re-create DataSnap client module and initialize connection
Try to request dataset (special dataset for connection test) if application catch exception entire process works from the beginning.
After this operation my Client re-connect to DataSnap server and can request dataset using new TCP/IP connection. Of course user can close dialog, but then application is turn off.
I think this can help you. I tried many others solutions but this proved to be the best. Moreover algorithm also supports the loss of connection with the client's fault.
The easiest workaround for this problem is to change the TDSServerClass LifeCycle attribute to Invocation.
This will cause you to have a stateless server, and the server will create a new session per request. But, you will be able to close and reconnect the server without interrupting the clients connection.
How can a desktop application communicate with a Windows service under Vista/Windows2008/Windows7? The application needs to send small strings to the service and receive string responses back. Both are written in Delphi 2009. (Please provide sample code also)
The way to go is named pipes, you'll probably have to take a look at the communication across different Integrity levels.
This article explores how to do this in vista. Although it's written in c++ it's just basic Windows API calls, so it should translate fast enough to Delphi.
If you want to search for more on this subject, this communication is called Inter Process Communication, but a better search term is IPC.
Using Indy you can relatively easy create a TCP connection between your apps. Especially if you only need to send string messages. For the client (in your case the desktop application) it's basically
var
Client : TIdTCPClient;
...
Client.Host := 'localhost';
Client.Port := AnyFreePortNumber;
Client.Connect;
Client.IOHandler.Writeln (SomeString);
Response := Client.Readln;
...
Client.Disconnect;
For the server (would be the service in your case)
var
Server : TIdTCPServer;
Binding : TIdSocketHandle;
...
Server.DefaultPort := SameFreePortNumberAsInClient;
Binding := Server.Bindings.Add;
Binding.IP := '127.0.0.1';
Binding.Port := Server.DefaultPort;
Server.OnConnect := HandleConnection;
Server.OnDisconnect := HandleDisconnection;
Server.OnExecute := HandleCommunication;
Server.Active := True;
Just implement the HandleCommunication method. It is called whenever the client decides to send something. Example:
procedure MyClass.HandleCommunication (AContext : TIdContext);
var
Request : String;
begin
Request := AContext.Connection.IOHandler.Readln;
if (Request = Command1) then
HandleCommand1
else if (Request = Command2) then
HandleCommand2
...
end;
IIRC a service is only allowed to have a graphical user interface OR have network access, so this might be a problem if your service needs a GUI (which you should avoid anyway, see this question). I don't know how this is handled in Windwos Vista and later though.
Have a look at the answers in Exchange Data between two apps across PC on LAN which is pretty much the same question nowadays. Local comms via TCP is standard. As I said in my response there, solutions that use "Remote Procedure Call" type interfaces work well. I use RemObjects SDK for this sort of thing, and it makes it easy to expand to control across the network if you wish to later.
Both of these allow you to create a connection that for most of your code is "transparent", and you just call an interface which sends the data over the wire and gets results back. You can then program how you usually do, and forget the details of sockets etc.
You have to change the service user from localsystem to networkservice and then the service can use TCPIP fine. I have several services which use TCPIP for an external control hook. Just make sure your service port is configurable so you can handle any collisions.
A few of my control interfaces are based on XML pages served from an internal HTTP server. This allows me to remotely check on status of the service using any web browser which can reach the port on that machine. The advantage of using HTTP over other methods is that it works well when you need to work over existing network hardware.
If your ONLY going to be communicating locally, then named pipes, mail slots or a memory mapped file might be the best method.
I haven't tried it, but I think you could use named pipes.
I use in my service applications a component set, freeware with sourcecode called Simple IPC.
Search torry.net. It has worked very well in all of my service apps when communicating with a desktop app.
John