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.

Reply via email to