Mailing List Archive: 49091 messages
  • Home
  • Script library
  • AltME Archive
  • Mailing list
  • Articles Index
  • Site search

[REBOL] Re: The REBOL async:// tutorial - take 1

From: maarten:vrijheid at: 3-Mar-2004 19:35

First, realize that we didn't knock this off in two afternoons. So it is OK to hit your head against the wall.
>> handler: func [port [port!] state [word! error!] /local tmp cmd] [ >> if error? :state [print mold disarm state return true] >> switch state [ >> connect [ >> ; do HTTP request >> insert port {GET /fg/anen.jpg HTTP/1.0^M^JHost: >>^M^J^M^J} >> >> >> >> >That one - raw tcp stream, right? I wonder if some kind of dialect (set >of functions) could be produced to handle that ugly MJMJMJ and GET >commands etc., as e.g. read/custom allows. Just a theoretical question, >if it even would be worth it, nothing more >
You could use the builtin crlf as a replacement.
>> false >> ] >> read [false] >> write [false] >> close [ >> ; get data >> data: copy port >> >> >> >> >OK, so that one does not block right? And it is so just because we are >inside handler function, which is being called once some event happens >on port, so theoretically some data should be awaiting us. I just wonder >it comes in 'close switch part. We get here once other side closed >connection? So if I understand it correctly, we read it in parts, but >'read part does nothing, rebol is internally buffering data (how large >data is rebol able to buffer easily that way?) and once other side >closes connection, we can read it by copy (which will not block, even if >no-wait was not used), and whole data is being read out of the port >buffer at once? Well, I hope I am still on track :-) >
That copy doesn't block, because it gets diverted to the copy in the async:// handler IIRC. Also, async:// does all low-level input and output buffering. And async opening + async dns://
>>Now for a simple server: >> >>First we add a listening server port to the system/ports/wait-list, like: >> >> >> >> >Why? Is that needed? Am I right thinking it is because of View? Once >View starts, it adds event-port into wait-list and if we want to process >all various events properly, we have to go via wait-list? >
It can be done many ways indeed. Pick your own example.
>> either error? try [listen: open/no-wait tcp://:8000] [ >> port: open async://localhost:8000 >> port/awake: do handler >> >> >> >> >Above code somehow escapes my understanding :-) So if we are not able to >open listen port (because e.g. we are already listening), we open >connection on localhost to that port? What is that good for? >
Gabriele? I missed it, it looks like setting up a tunnel to me.
>> ] [ >> listen/awake: func [l /local p] [ >> print "Got connection." >> p: first listen >> remove find system/ports/wait-list listen >> port: make port! [scheme: 'async sub-port: p] >> >> >> >> >that is something I never understood. That is why I was not able to >further more deeply adapt Sterling's proxy.r script. It contained way >too much port subport and proxy (as a port :-) stuff for my brain to >swallow :-) 'p is assigned first connected client. It does not contain >any sub-port, yet it can communicate. IIRC someone said, that sub-port >contains real communication port. But I don't understand the difference, >even without sub-port, I am able to send data here and there and I can >see it buffered in port/state. What is sub-port then? >
The real connecttion :-) Anyway... this is the way to go. If you take a look at the renewed xml-rpc you will find an add-server function that abstract this away nicely.
>> open port >> port/awake: do handler >> false >> ] >> insert tail system/ports/wait-list listen >> port: none >> ] >> >> >> >> >> >so overall - it is clever - once first event happens on listening port, >we remove it from wait-list, reassign handler and insert it back into >wait-list. That sounds like nice constructor/init method in OOP :-) >
Go clean your mouth!
>>As you can see, its awake function convert the accepted port to an async >>one and sets the handler. So what is the handler then? >> >> handler: [ use [ buffer ][ >> >> buffer: copy [] >> >> func [port [port!] state [word! error!] /local tmp cmd] [ >> if error? :state [print mold disarm state return true] >> switch state [ >> connect [print "Connected." false] >> read [ >> append buffer copy port >> while [tmp: find buffer newline] [ >> cmd: copy/part buffer tmp >> remove/part buffer next tmp >> do-cmd cmd >> ] >> false >> ] >> write [false] >> close [print "Peer closed connection." close port true] >> ] >> ] >> ] >> ] >> >>The first thing to notice is the fact that we use 'use to create a context >>that returns a function! value. This function (and only this particluar >>value) has access to its buffer. By doing the handler block in the server >>part above, every accepted port gets a copy of theis function value with >>its own "static" buffer space. A very simple but effective trick. >> >> >> >> >cool! >
I know! That's why I thought I'd share it with you all.
>Thanks for your tutorial, very educative! >
Put it to work. async:// is so clever. Carl explained it Eastern 2003 in a private chat, then I started experimenting and Gabriele turned that into async:// It took some more experimenting, and finally we have a tutorial. --Maarten