Rav Posted June 15, 2015 Posted June 15, 2015 Hello, I have two apps, a client VCL app and a server none-VCL app which work together exchanging data using TidTCPClient and TidTCPServer components on both sides. If one side wants to change the other it sends data in a way like this: with TidTCPClient.Create do try try Host := 'localhost'; Port := 1234; ReadTimeout := 2000; Connect; with Socket do begin write(integer(1)); end; Disconnect; except end; finally Free; end; It works simple and reliable. How to properly organize this two way communication using UniGui? Which components should I use? Quote
Administrators Farshad Mohajeri Posted June 16, 2015 Administrators Posted June 16, 2015 How this event is triggered? Quote
Rav Posted June 16, 2015 Author Posted June 16, 2015 On the client side the events are triggered by interacting with controls. On the server side the events are triggered internally, without human intervention. In other words the clients display some server info and can modify some server parameters remotely. When the server parameter is changed (it could be also done due to other internal reasons) the server sends a message (delphi record) to all clients, they receive it and update the display info accordingly. Now this scheme works using TidTCPClient+TidTCPServer components bundle on the clients and the server (TidTCPClient sends a message, TidTCPServer handles received messages). I want to move from VCL to UniGui and use http for exchanging messages. Quote
Oliver Morsch Posted June 16, 2015 Posted June 16, 2015 In other words the clients display some server info and can modify some server parameters remotely. When the server parameter is changed (it could be also done due to other internal reasons) the server sends a message (delphi record) to all clients, they receive it and update the display info accordingly. By default web servers can't send messages to (all) clients. Servers can only answer a request. So you can do one of the following; - using TUniTimer (client (browser) sends every x seconds a request for updates to the server) - using "web sockets" (HTML5) - using "long polling" (see here) Quote
Rav Posted June 16, 2015 Author Posted June 16, 2015 - using TUniTimer (client (browser) sends every x seconds a request for updates to the server) I think it's the best way - using "web sockets" (HTML5) It's too modern way - using "long polling" (see here) It's too complicated way. Suppose I use TUniTimer. Sorry for the following stupid questions, but there is no UniGui documentation and "all features demo" app didn't clear up the situation either. 1) How to send a message (Delphi record) from a client to the server? I found a code sample: UniSession.SendResponse('New Text', False) Is it the way how the client sends a text message to the server? 2) How should the server handle it? If found OnHTTPCommand event handler. Is that it? 3) How to send a Delphi record from a client to the server? UniSession.SendStream? 4)How should the server handle incoming stream? There is no OnStream event handler. 5)How should the client retrieve new data from the server? I don't use databases. All data are in the server's memory. As I said I could not find a simple example which shows how to organize this client-server functionality properly. Quote
docjones Posted June 17, 2015 Posted June 17, 2015 you must think, that each unigui session, is like a one vcl running application. Each unigui session , ( main module or your unigui main form ), can have her custom tcp client, and you can comunicate it like a vcl application, with your custom server. Quote
rullomare Posted June 17, 2015 Posted June 17, 2015 Hi Rav, honestly do not understand the problem. If you want to communicate with a TCP server from a Unigui Application , just add the Indy component IdTcpClient to Unigui Form. In my example, I talk to a TCP server that processes a PDF file into a text file and returns to the Tcp_Client a return code. Function Pdftotext(PdfFilename,TextFilename,pdftotextpgm:string) : integer ; begin fuserform.Idtcpclient.Host := tcpserver ; fuserform.Idtcpclient.Port := TcpPort ; Fuserform.IdTCPClient.Connect; parms := pdftotextpgm + ';' + pdffilename + ';' + textfilename ; Fuserform.IdTCPClient.IOHandler.WriteLn(parms, TIdTextEncoding.Default); result := strtoint( Fuserform.IdTCPClient.IOHandler.ReadLn()); end; Let me know if you need an example of the code for the Server-TCP Regards Quote
Rav Posted June 17, 2015 Author Posted June 17, 2015 Each unigui session , ( main module or your unigui main form ), can have her custom tcp client, and you can comunicate it like a vcl application, with your custom server. If you want to communicate with a TCP server from a Unigui Application , just add the Indy component IdTcpClient to Unigui Form. Yes I tested idTCPClient, it works fine, thank you! Then I decided to try idTCPServer on the client side and tested the code: procedure TMainForm.UniFormCreate(Sender: TObject); begin idServer := TIdTCPServer.Create; idServer.OnExecute := idServerExecute; idServer.DefaultPort := 12345; idServer.Active := True; UniLabel2.Caption := 'Test';//Works fine end; procedure TMainForm.idServerExecute(AContext: TIdContext); var i: integer; begin with AContext.Connection.Socket do try i := ReadLongint; UniLabel2.Caption := inttostr(i);//Exception except end; end; This code gives an exception "Attempt to access nil session reference" while referencing UniLabel2. Why is that? I guess this relates to the fact that idServerExecute is executed by Indy in a thread, but why "session is nil"? And another question, VCL components are not thread safe, is the situation the same with UniGui controls? Quote
docjones Posted June 18, 2015 Posted June 18, 2015 don't use a tcpserver in unigui , becouse each user that open new web session, need tcp a server diferent port. with tcpclient you cant send and recive data. and yes, tcpserver.onexecute, is multithreaded, you can not access unigui components in the execute method, or in a thread when you recive data, add data to temporaly buffer, and check and read the buffer content it in unitimer. Procedure TmainForm.Oncreate(....) Begin Buffer:=TmemoryStream.create; End; procedure TMainForm.idServerExecute(AContext: TIdContext); var i: integer; begin with AContext.Connection.Socket do try ReadStream(buffer,-1); except end; end; Procedure on unitimer(.....) Begin Buffer.Position:=0; i:=buffer.readint; buffer.clear; UniLabel2.Caption := inttostr(i); End; Quote
Rav Posted July 2, 2015 Author Posted July 2, 2015 The TUniTimer works fine, thank you. But I got another question. As far as I understand the TUniTimer being placed on the main form is instantiated in every session, so with the 100 open sessions we'll have 100 timers on the server side. Is this the right way of constructing the application? Quote
Oliver Morsch Posted July 2, 2015 Posted July 2, 2015 As far as I understand the TUniTimer being placed on the main form is instantiated in every session, Yes. so with the 100 open sessions we'll have 100 timers on the server side. No. TUniTimer runs/fires on client side (browser). But for every "onTimer" the browser sends a request to ther server. So you have very much network trafffic and the server must handle all these requests. Is this the right way of constructing the application? In my opinion: No. See option 2 and 3 in Post #4. Quote
Rav Posted July 2, 2015 Author Posted July 2, 2015 See option 2 and 3 in Post #4. I looked through your long polling application, very informative, thank you! I think that's a little bit complicated scheme, does it have flaws? Are you planning to add WebSockets example to it? Quote
Oliver Morsch Posted July 3, 2015 Posted July 3, 2015 Rav, on 02 Jul 2015 - 11:33 PM, said: I looked through your long polling application, very informative, thank you! I think that's a little bit complicated scheme, does it have flaws? There are 347 downloads - and no bug reports till now... Rav, on 02 Jul 2015 - 11:33 PM, said: Are you planning to add WebSockets example to it? No. Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.