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.
