Fr0sT.Brutal 900 Posted October 30, 2019 I wonder what's the correct way of using TWSocket.LineMode to read line by line. From what I see in sources and samples, this only works when receiving happens in OnDataAvailable handler. But I create socket descendant and would like to override TriggerDataAvailable instead (using handlers for catching parent events is weird + I want to leave the place vacant for client handler). Unfortunately, I can't make it work. If Receive is called from TriggerDataAvailable, there's no data at all and if inherited TriggerDataAvailable is called beforehand, Receive returns the whole piece of data. Share this post Link to post
Angus Robertson 574 Posted October 30, 2019 When using LineMode, in the onDataAvailable event you use ReceiveStrA to get a complete raw line without needing to check line endings, but that just calls Receive and does all the hard work for you. If you are creating descendent components, you need to ensure the correct ancestor, which is not easy due to the deep hierarchy of wsocket. LineMode is handled in TCustomLineWSocket. Angus Share this post Link to post
Fr0sT.Brutal 900 Posted October 30, 2019 Angus, thanks for reply. TCustomLineWSocket is in straight inheritance line so no problem here. I descend from TWSocket. I tried calling Receive which is almost the same as ReceiveStr from TriggerDataAvailable with no luck. procedure MemoLog(const msg: string); begin Form1.mLog.Lines.Add(msg); end; type TLineTestSocket = class(TWSocket) public Request: RawByteString; procedure TriggerSessionConnectedSpecial(ErrCode: Word); override; function TriggerDataAvailable(ErrCode : Word) : Boolean; override; end; { TLineTestSocket } procedure TLineTestSocket.TriggerSessionConnectedSpecial(ErrCode: Word); begin inherited; SendStr(Request); end; function TLineTestSocket.TriggerDataAvailable(ErrCode: Word): Boolean; var s: RawByteString; begin repeat s := ReceiveStrA; if s = '' then Break; MemoLog('Got line: '+ s); until True; end; procedure TForm1.Button1Click(Sender: TObject); var sock: TLineTestSocket; begin sock := TLineTestSocket.Create(Self); sock.LineMode := True; sock.Addr := 'google.com'; sock.Port := '80'; sock.Request := RawByteString('GET / HTTP/1.1'#13#10#13#10); sock.Connect; end; Here's an excerpt from test project. On execution I get: Quote Got line: HTTP/1.0 301 Moved Permanently Location: http://www.google.com/ Content-Type: text/html; charset=UTF-8 Date: Wed, 30 Oct 2019 13:07:33 GMT Expires: Fri, 29 Nov 2019 13:07:33 GMT Cache-Control: public, max-age=2592000 Server: gws Content-Length: 219Got line: X-XSS-Protection: 0 X-Frame-Options: SAMEORIGIN X-Cache: MISS from IAC-GW1.glonass-iac.ru X-Cache-Lookup: MISS from IAC-GW1.glonass-iac.ru:3128 Via: 1.0 IAC-GW1.glonass-iac.ru:3128 (squid/2.7.STABLE5) Connection: close <HTML><HEAD><meta http-equGot line: iv="content-type" content="text/html;charset=utf-8"> <TITLE>301 Moved</TITLE></HEAD><BODY> <H1>301 Moved</H1> The document has moved <A HREF="http://www.google.com/">here</A>. </BODY></HTML> So either I'm not cooking ReceiveStrA properly or it doesn't do what it supposed to do Share this post Link to post
Angus Robertson 574 Posted October 30, 2019 You are not using the Inherited code in other versions of TriggerDataAvailable so need to handle line mode yourself. Angus Share this post Link to post
Fr0sT.Brutal 900 Posted October 30, 2019 If I add a call to inherited function TLineTestSocket.TriggerDataAvailable(ErrCode: Word): Boolean; var s: RawByteString; begin Result := inherited; repeat s := ReceiveStrA; if s = '' then Break; MemoLog('Got line: '+ s); until True; end; I get nothing at all, ReceiveStrA returns empty string. Share this post Link to post
Angus Robertson 574 Posted October 30, 2019 (edited) I am not trying to write your code for you, just explaining why your function does not work. You have a conceptual issue trying to override library functionality. Angus Edited October 30, 2019 by Angus Robertson Share this post Link to post
Fr0sT.Brutal 900 Posted October 30, 2019 Well, actually to use library functionality is exactly what I'm trying to do! Alas, library doesn't seem to allow me this. Of course first calling inherited is logical - that's where bytes are received, lines are buffered where they will be then extracted from and so on. Okay. But after doing this I expect Receive to return line by line, right? But I get nothing. As far as I can understand the logic behind the code it relies heavily on the presence of OnDataAvailable handler and breaks if there's no any but of course I might be wrong. Share this post Link to post
Angus Robertson 574 Posted October 30, 2019 The ICS library is designed to be used through event handlers, not by overriding internal functions. That requires a high level understanding of the library structure and is beyond the support I can offer here. Angus Share this post Link to post
Fr0sT.Brutal 900 Posted October 31, 2019 I see. Interface is not too friendly for extending so I tend to create a wrapper class with socket as a field. Share this post Link to post
Angus Robertson 574 Posted October 31, 2019 TriggerDataAvailable was never designed to be extendable. Apart from the three version in wsocket no other ICS units override it. So while you can override it, you need to replicate all the functionality like parsing line endings. Angus Share this post Link to post
FPiette 383 Posted October 31, 2019 28 minutes ago, Fr0sT.Brutal said: I tend to create a wrapper class with socket as a field. This is probably the best thing to do if you want to spare learning the inner working of TWSocket. All ICS components does just like that: create a new class and have one or more field of TWSocket type. By the way, given the log you showed, you are playing with HTTP protocol. Why not use ICS HTTP component instaed of TWSocket. This is the best choice unless you plan to reimplement HTTP. ICS HTTP component if really powerfull. Rewriting it is probably a huge work. Share this post Link to post
Fr0sT.Brutal 900 Posted October 31, 2019 3 hours ago, Angus Robertson said: TriggerDataAvailable was never designed to be extendable. Apart from the three version in wsocket no other ICS units override it. So while you can override it, you need to replicate all the functionality like parsing line endings. Angus It is extendable in wsocket descendants so why not. I kinda had luck with SchannelSocket doing that so I decided to proceed with encapsulating protocol-specific stuff into socket class itself. 3 hours ago, FPiette said: By the way, given the log you showed, you are playing with HTTP protocol. Why not use ICS HTTP component instaed of TWSocket. This is the best choice unless you plan to reimplement HTTP. ICS HTTP component if really powerfull. Rewriting it is probably a huge work. Thanks Francois, I'm aware of it but that's something like HTTP that I want to implement. HTTP request was just the simplest one I found to use as a sample of line-based protocol. Share this post Link to post
FPiette 383 Posted October 31, 2019 8 hours ago, Fr0sT.Brutal said: I decided to proceed with encapsulating protocol-specific stuff into socket class itself. Loooong time ago, back in 1996 when I started ICS, a went to that way. I quickly stopped it because it makes things much more complex for both the component developer and the component user. Since then, I am still happy to use delegation instead of inheritance. Inheritance is largely used in ICS, see TWSocket itself as an example. But delegation has to be used when it has to! 1 Share this post Link to post