Thanks Maarten and Gabriele!

Paul Tretter

----- Original Message ----- 
From: "Maarten Koopmans" <[EMAIL PROTECTED]>
To: <[EMAIL PROTECTED]>
Sent: Wednesday, March 03, 2004 8:48 AM
Subject: [REBOL] The REBOL async:// tutorial - take 1


>
> See below. Enjoy it. Republish it.
> ==================================================
>
> The REBOL async:// tutorial - Take 1
>
> Maarten Koopmans, code samples and root protocol by Gabriele Santilli
>
> What is async:// anyway? It is an asynchronous TCP root protocol that can
> be used to drive REBOLs hidden async TCP features. This allows network
code
> to be processed automagically upon certain network events, such as getting
> a connection, ready to read/write, and closing a conection.
>
> Once you have set the code to be called upon these events, things will go
> by themself if you're in an event loop. This is much like GUI events are
> processed automagically when you call 'view.
>
> The first thing you need to do is get the async:// root protocol by
> Gabriele. Download it at http://www.rebol.it/giesse/async-protocol.r
>
> Note that this version supports binary transfers only, /lines support is
> being worked on.
>
> Now let's start with a simple client script, downloading images in the
> background in REBOL/View. First the code:
>
>   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:
> www.3dwallpaper.com^M^J^M^J}
>               false
>           ]
>           read    [false]
>           write   [false]
>           close   [
>               ; get data
>               data: copy port
>               close port
>               ;print copy/part data find data "^M^J^M^J"
>               data: to binary! find/tail data "^M^J^M^J"
>               other/image: attempt [load data]
>               other/text: ""
>               show other
>               false
>           ]
>       ]
>   ]
>
>   port: open async://www.3dwallpaper.com:80
>   port/awake: :handler
>
>   view layout [
>       across me: box 100x100 random 255.255.255 0:00:00.5 feel [
>           engage: func [f a e] [
>               if a = 'time [
>                   me/color: random 255.255.255
>                   show me
>               ]
>           ]
>       ]
>       other: box 100x100 255.255.255 "Downloading image..." Return
>       Area 208x100 "You can type here while downloading."
>   ]
>
>
> Here is how it works: you open an async port using normal URI syntax in
the
> middle of the script. You can see this in the lines:
>
>   port: open async://www.3dwallpaper.com:80
>   port/awake: :handler
>
> The second line sets the callback function. This function is the crucial
> part for async:// based implementations, as it provides code that can be
> executed for all the events. So, let's take a look a the handler function!
>
> It starts with the following piece of code:
>
>   handler: func [port [port!] state [word! error!] /local tmp cmd] [
>       if error? :state [print mold disarm state return true]
>
> As you can see, we get two parameters: The port that we can read/write to
> and the state. The state can be an error! or a word!. If it is an error we
> need to clean up and the above code shows briefly one way how this can
> be done on
> the second line. If it is a word it can have any of four values:
>
> - 'connect, signalling that we have just acquired a connection
(client-side
>   only)
> - 'read , signalling that we can have data to read
> - 'write , signalling that we can write data (again)
> - and 'close , signalling that the other side has closed the connection.
>
> So what we do next in our handler is a switch based on the state,
executing
> different code for each possible state.
>
>   switch state [
>       connect [
>           ; do HTTP request
>           insert port {GET /fg/anen.jpg HTTP/1.0^M^JHost:
> www.3dwallpaper.com^M^J^M^J}
>           false
>       ]
>       read    [false]
>       write   [false]
>       close   [
>           ; get data
>           data: copy port
>           close port
>           ;print copy/part data find data "^M^J^M^J"
>           data: to binary! find/tail data "^M^J^M^J"
>           other/image: attempt [load data]
>           other/text: ""
>           show other
>           true
>       ]
>   ]
>
>
> As you see, we simply do a HTTP GET for an image, and write it to file and
> the view it. Upon 'connect we immediately make our request. Why not in the
> 'write event? The 'write event is triggered everything we have something
> written and we canw rite more. So the first time we always have to write
> from another event!!!
>
> Now, once the data is inserted we return false. This indicates that we
> shouldn't be removed from the system/ports/wait-list list, where all ports
> are stored (at least async:// and event:// ones). As you can see we did
> return true on error.
>
> The read and write states COULD be used to do some reading from data or
> write more, but as this is a very simple request-response, that's not
> necessary, so they just return false. So... the other side returns the
data
> and closes the connection. Upon close we simple read it ALL (again this
> could have been done in 'read using a temporary buffer) and close the
port.
> We update the image and.... done!
>
> Now for a simple server:
>
> First we add a listening server port to the system/ports/wait-list, like:
>
>   either error? try [listen: open/no-wait tcp://:8000] [
>       port: open async://localhost:8000
>       port/awake: do handler
>   ] [
>       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]
>           open port
>           port/awake: do handler
>           false
>       ]
>       insert tail system/ports/wait-list listen
>       port: none
>   ]
>
>
> 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.
>
> Now, on to the events. 'Connect does nothing, as we are already connected.
> This happened when the awake function of our TCP server port was called.
We
> chose 'write and 'close also to do nothing. Why? We only want to receive a
> command and then do it. The reading is done in 'read, we append what we
> have read to the buffer. If we see a newline, we execute it and clear the
> buffer until the newline. And then we return false. The fact that we
return
> false implies that we may read more than one connection.
>
> Also note that 'close does nothing, but *if* a port is closed, it also
> closes. Of course you can use 'close for all sorts of clean-up. Now if you
> this code
>
>   do-cmd: func [cmd] [
>       other/color: attempt [load cmd]
>       show other
>   ]
>
>   view layout [
>       across me: box 100x100 random 255.255.255 0:00:01 feel [
>           engage: func [f a e] [
>               if a = 'time [
>                   me/color: random 255.255.255
>                   if port [insert port join me/color newline]
>                   show me
>               ]
>           ]
>       ]
>       other: box 100x100 random 255.255.255 Return
>       Area 208x100
>   ]
>   halt
>
> to the above, you have a simple GUI with a command server.
>
> You are now ready to play with async:// and enhance the code adding
> substates to 'read or 'write to implement advanced protocols. Its just
more
> of the same. I hope you keep the use [ ] [  ] trick in mind for server
> handlers, I found it a very easy way to add extra substates this way.
>
> Now.... start coding!
>
> -- 
> To unsubscribe from this list, just send an email to
> [EMAIL PROTECTED] with unsubscribe as the subject.
>


---
Outgoing mail is certified Virus Free.
Checked by AVG anti-virus system (http://www.grisoft.com).
Version: 6.0.577 / Virus Database: 366 - Release Date: 2/3/2004

-- 
To unsubscribe from this list, just send an email to
[EMAIL PROTECTED] with unsubscribe as the subject.

Reply via email to