Handling different clients in indy lazarus / 38-Networking and Web Programming ---------------------------------------------------------------------- torbente · 2014-08-28 07:26 Is there a way to send messages to one defined client from the server? My server is working ok until now, since it only process request, but now i need add the functionality to send messages to one client, so it is necessary to link every connected user to their TidContext when they connect (or they soccket, or something) to make everyone reachable if necessary. Since my server is already able to only allow connections from registered users, im trying to link their username to their TidContext, but i dont know how i could convert it to something usable. Thanks in advance ---------------------------------------------------------------------- marcov · 2014-08-28 07:59 What Indy server are you using? Usually there is some serverexecute event that is parametrized with Peer context of the user. However this will only work for protocols that maintain connections. In a connectionless scheme like http it is harder to push events from server to client if the client is not asking. ---------------------------------------------------------------------- torbente · 2014-08-28 08:33 Im using IDTCPServer. I know i can get the user IP ... var ClientIP : string := acontext.Connection.Socket.Binding.peerip; But how i send something (in this case, a simple string) to that IP if the client do not ask before? ---------------------------------------------------------------------- marcov · 2014-08-28 09:01 You don't need the IP, but the socket. It depends on Indy version, but probably simply there are acontext.Connection.Socket.write*() to write to the client socket. Did you look at the examples? That's a separate download but iirc it had some chat client-server app. ---------------------------------------------------------------------- ChrisF · 2014-08-28 14:44 There is sample for an Indy server push action here, for instance: http://mikejustin.wordpress.com/2014/04/19/indy-10-tidtcpserver- server-side-message-push-example/ It must adapted a little bit in order to work with FPC/Lazarus, as TThread.Queue is not implemented in FPC 2.6.x branches. Attached a quick conversion of this sample for FPC (it's not really a "full" conversion, but I guess it's enough for a demonstration purpose). ---------------------------------------------------------------------- torbente · 2014-08-28 20:15 That example already knows the TidContext to send the information. How i could store the socket then? Or Indy servers for many clients only works for answers? ---------------------------------------------------------------------- ChrisF · 2014-08-29 00:05 You can use your own list of user data, using the onconnect and ondisconnect events, and use the TIdContext.data field to point on them. or you can derive the TIdcontext class to add your own data. That's what I've done in this modified sample: I've added a UserName field. For simplification, this UserName field is filled during the IdContext creation, but of course it should be done instead when analyzing the data received from the client. ---------------------------------------------------------------------- torbente · 2014-08-30 09:10 chrisf, i solved this creating an array of a record: Code: [Select]Userdata = Record             Name : string[16];             IdData : TIdContext;             end;  Online : array[1..MaxConnectionsAllowed] of UserData; Then i just need link one name to each connection established and then use the array to manage all connections. Since my server only accepts registered users, i just linked their login to their IdContext. Ok, now i can send a string to any user using this: Code: [Select]Procedure Tform1.SendMessageTo(Userconnected: usertada; message: String); begin UserConnnected.IdData.Connection.IOJandler.WriteLn(message); end; Example: SendMessageTo(Online[4],'Hello '+Online[4].Name+', you have the virtual socket number 4'); GREAT! But how i can process the message in the client? IdTCPClient do not have an OnExecute event. ---------------------------------------------------------------------- ChrisF · 2014-08-30 13:38 One response is already present in the sample code: use a thread on the client side to wait for incoming data. Or you can use a timer to periodically check for incoming data. See Remy Lebeau's answer in this topic: http://stackoverflow.com/questions/7989133/how-can-i-wait-for-a- string-from-a-server-with-idtcpclient ---------------------------------------------------------------------- JD · 2014-08-30 14:23 Indy's IdTCPServer OnExecute method is multi-threaded. Ther server creates a unique thread for every client connected to it. For your application to be thread safe, the client should also communicate with the server via a thread created client side BUT you can get away with not doing this for small applications. So communication between server & client is as simple as a) client sends message to server (using one of the many Write methods) & waits for a response (using one of the many Read methods) b) server receives message in client's dedicated thread c) server processes message & sends reply in client's dedicated thread (IdContext is useful here) d) client's Read method already set up in (a) above receives the server's response & acts upon it e) if your client is thread-safe, the reading in (d) above is done in the client side thread and you then Synchronize the thread with your main application GUI to used the response received from the server. See the very basic example below. You can get away with the thread safe client side restriction for very small applications though. Code: [Select]procedure TClientThread.Execute; (* The main client thread that is executed EVERY TIME a client is connected to the server    All communication (messages/data operations) with the server MUST be done    INSIDE/THROUGH this thread *) var AString: string;  begin   {$IFNDEF FPC}   inherited;   {$ENDIF}   // while the thread is not terminated and the client is connected   while NOT Terminated and FTCPClient.Connected do   begin     try       // Say hello to the server       FTCPClient.IOHandler.WriteLn("Hello! Are you there?");       // read server response       AString := FTCPClient.IOHandler.ReadLn;       // sync with GUI     Synchronize(AString);     finally       //       AString := '';     end; // tryf     // we need to call Sleep so that this thread will not eat too much CPU     // 50 miliseconds should be perfect     // NOTE: we do NOT lose data, we just create a small latency     Sleep(50);   end; end; You do not need to know each client's IP address. The server handles it internally. I only use client's IP addresses to know whether the client is on the LAN or if the client is external (say in another country for example). Hope it helps. JD ---------------------------------------------------------------------- torbente · 2014-08-30 14:50 Meanwhile im trying to implement all this, i want add that i store the clients IP to avoid multiple connections from the same address (my server do not receive connections from my local LAN never) ---------------------------------------------------------------------- JD · 2014-08-30 15:03 Quote from: torbente on August 30, 2014, 04:50:22 pmMeanwhile im trying to implement all this, i want add that i store the clients IP to avoid multiple connections from the same address (my server do not receive connections from my local LAN never) You can easily avoid this by disabling the "Connect" button client- side once a connection with the server has been established. No need for complicated code server side to bounce off multiple same-IP connections. In addition, since a computer can only have a single IP address at any given moment, disabling the a "Connect" button is the easiest & fastest means to do it. Put the code below in a button's OnClick event (or some other procedure that manages the state of the GUI controls) & it's done. Code: [Select]  btnConnect.Enabled := not FTCPClient.Connected; In addition use the uniqueinstance component http://wiki.lazarus.freepascal.org/UniqueInstance to ensure that only ONE instance of your application can run on a machine at any given time. The uniqueinstance component is part of the Luipack http://code.google.com/p/luipack/. How to use uniqueinstance. http://forum.lazarus.freepascal.org/index.php?topic=16470.0%3bwap2 JD ---------------------------------------------------------------------- torbente · 2014-09-02 06:19 I really love the idea of uniqueinstance. I already downloaded the code and will probably add it to my project. I just figured out that when i need that my server send one message to one client, it is never necessary to receive an answer, but when it is the client who sent the message, there must be always an anserw. But sometimes, the server could send a message to a client that already sent one request and is waiting for his answer yet. In those situations, the client will receive the server message and will think that is the answer... or not? Example: client.toServer.Writeln('What day is today?'); Client,Fromserver.Readln(); But due to connection delays, the server sends this to the client: Server.ToClient.Writeln('Some rain expected for today'); and then answer the previous request... Server.ToClient.Writeln('Tuesday'); If the client receives the line A before his answer it will crash. Any fixes for this? PS I will include a timer in the client to check if there are messages from the server. ---------------------------------------------------------------------- JD · 2014-09-03 07:12 Quote from: torbente on September 02, 2014, 08:19:48 amI really love the idea of uniqueinstance. I already downloaded the code and will probably add it to my project. I just figured out that when i need that my server send one message to one client, it is never necessary to receive an answer, but when it is the client who sent the message, there must be always an anserw. But sometimes, the server could send a message to a client that already sent one request and is waiting for his answer yet. In those situations, the client will receive the server message and will think that is the answer... or not? Example: client.toServer.Writeln('What day is today?'); Client,Fromserver.Readln(); But due to connection delays, the server sends this to the client: Server.ToClient.Writeln('Some rain expected for today'); and then answer the previous request... Server.ToClient.Writeln('Tuesday'); If the client receives the line A before his answer it will crash. Any fixes for this? PS I will include a timer in the client to check if there are messages from the server. Why is your server sending messages to clients when it has not been asked to? You have to take a consistent structured approach to your application's architecture here. In my opinion, the server should remain silent at all times until a client sends it a message. In my applications, the only exceptions to this are a) a client is either connecting or disconnecting AND I need to notify other connected clients about this b) the server is performing a database backup AND clients need to be notified that the server is busy (this backup is triggered by a server-side timer) In those instances, the message is received client side by a popup tray message using the TPopupNotifier NEVER in the main client GUI. So I keep them separate i) responses to client messages will be shown in the client side GUI ii) server initiated messages will be shown in a TPopupNotifier so there is no confusion between messages on the client side. JD ---------------------------------------------------------------------- taazz · 2014-09-03 07:38 how about an application for real time monitoring of new york stick exchange?  Should the clients keep on requesting new data or is it easier to start a communication and let the server send data as they become available? ---------------------------------------------------------------------- torbente · 2014-09-03 18:58 I thinked to use this workaround to fix this: Client code: Connecting := true; ToServer.Writeln; FromServer.Readln(); Connecting := false; So, if a unexpected message from the server arrives between the write and the read lines, I just need check the variable connecting. If connecting is false, then just process the message, but if connecting is true, what i could do with it???? ---------------------------------------------------------------------- JD · 2014-09-03 20:26 Quote from: taazz on September 03, 2014, 09:38:46 amhow about an application for real time monitoring of new york stick exchange?  Should the clients keep on requesting new data or is it easier to start a communication and let the server send data as they become available? That is the broadcast model & it is different from what torbente is talking about here if I understand him correctly. Your example is similar to an internet radio application that receives a continuous stream of data once it is connected to a station. ---------------------------------------------------------------------- taazz · 2014-09-04 02:38 Quote from: JD on September 03, 2014, 10:26:31 pmQuote from: taazz on September 03, 2014, 09:38:46 amhow about an application for real time monitoring of new york stick exchange?  Should the clients keep on requesting new data or is it easier to start a communication and let the server send data as they become available? That is the broadcast model & it is different from what torbente is talking about here if I understand him correctly. Your example is similar to an internet radio application that receives a continuous stream of data once it is connected to a station. Well that contradicts your previous statement that says Quote from: JD on September 03, 2014, 09:12:30 amIn my opinion, the server should remain silent at all times until a client sends it a message. In my applications, the only exceptions to this are How about a chat application should the clients request new data every few seconds or should the server push the messages to the subscribed client for the room? I do not see the difference between those small examples and the OP's needs then again that might only meen that I do not have good imagination that is why I'm asking. ---------------------------------------------------------------------- JD · 2014-09-04 11:26 Quote from: taazz on September 04, 2014, 04:38:05 amQuote from: JD on September 03, 2014, 10:26:31 pmQuote from: taazz on September 03, 2014, 09:38:46 amhow about an application for real time monitoring of new york stick exchange?  Should the clients keep on requesting new data or is it easier to start a communication and let the server send data as they become available? That is the broadcast model & it is different from what torbente is talking about here if I understand him correctly. Your example is similar to an internet radio application that receives a continuous stream of data once it is connected to a station. Well that contradicts your previous statement that says Quote from: JD on September 03, 2014, 09:12:30 amIn my opinion, the server should remain silent at all times until a client sends it a message. In my applications, the only exceptions to this are How about a chat application should the clients request new data every few seconds or should the server push the messages to the subscribed client for the room? I do not see the difference between those small examples and the OP's needs then again that might only meen that I do not have good imagination that is why I'm asking. I see no contradiction in what I said earlier & I'm not trying to make a point. Just adding my own 2 cents to the discussion to try & help. In the case of the chat application, that is actually easier. I use a record structure that contains the Sender ID & the Receiver ID. Once the sender sends the message to the server, the server processes the message & forwards it to the receiver (if the receiver is still connected). The server is just a middleman & the architecture is similar to a relay model. None of the connected clients request data from the server but they have dedicated threads listening & waiting for messages from the server. A message only comes in if it was sent by someone otherwise the client just sits & waits. Remember the old "You Got Mail" or audible Yahoo alerts when new mail comes in? I believe it works like that. JD