Of course you have to do the async-protocol.r first before trying the sample code.....
Maarten Koopmans wrote: >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.
