Right, you can build an F# Async version using Async.FromBeginEnd. (I have not done it, though.)

(Aside: If you take this to the extreme, you just end up inventing WCF. If I wanted a real server app, I'd probably go the WCF route. It would not be 'very simple', but still 'simple', and perhaps could do whatever your simple example might do in another 30 lines of code or so.)

By on 11/11/2009 11:43 PM ()

Thanks Brian. It was my work with WCF that actually got me interested in using HttpListener, since it uses it. The below code works, but I don't understand the threading aspects. Is it multithreaded? Who/what controls the # of threads? I'm looking for a code review of:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
open System
open System.IO
open System.Net

let main() =
  use listener = new HttpListener()
  listener.Prefixes.Add "http://+/";

  let getContext() = Async.FromBeginEnd (listener.BeginGetContext, listener.EndGetContext)

  

  let handleRequest = 
    async {
      let! context = getContext()
      let request = context.Request
      use response = context.Response
      let enc = Text.Encoding.UTF8
      response.ContentType <- "text/plain;charset=" + enc.WebName
      use writer = new BinaryWriter(response.OutputStream, enc)
      let bytes = enc.GetBytes (sprintf "Now: %A" DateTime.Now)
      response.ContentLength64 <- int64 bytes.Length
      writer.Write bytes
      ()
    }

  listener.Start()
  while true do
    Async.RunSynchronously handleRequest
  ()

main()

cheers,
Cameron

By on 11/12/2009 6:13 PM ()

No, that uses at most two threads and they block on each other and only handle one request at a time.

If you want e.g. 10 concurrent 'servers', you could put the 'while true' inside the async{} block around all the code there, and then do 10 copies of Async.Start on it. You'd then use at most 10 threadpool threads, as each async guy is only using a thread between when they get a context and when they finish writing the bytes and start the next loop (e.g. will not hold a thread while waiting for a new request at the getContext() call).

Does it make sense?

By on 11/12/2009 6:45 PM ()

Yes, that makes sense. Looking at Async.Start, it looks like I can pass in a CancellationToken if I want to be able to stop the 'request processor' programmatically. Really cool! Here is the adjusted code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
open System
open System.IO
open System.Net

let main() =
  use listener = new HttpListener()
  listener.Prefixes.Add "http://+/";

  let getContext() = Async.FromBeginEnd (listener.BeginGetContext, listener.EndGetContext)

  let requestProcessor = 
    async {
      while true do
        let! context = getContext()
        let request = context.Request
        use response = context.Response
        let enc = Text.Encoding.UTF8
        response.ContentType <- "text/plain;charset=" + enc.WebName
        use writer = new BinaryWriter(response.OutputStream, enc)
        let bytes = enc.GetBytes (sprintf "Now: %A" DateTime.Now)
        response.ContentLength64 <- int64 bytes.Length
        writer.Write bytes
    }

  listener.Start()

  let maxThreads = 10
  for n = 0 to maxThreads do
    Async.Start requestProcessor

  printfn "HttpListener started with %d maximum threads processing requests." maxThreads

  printfn "Press any key to quit."
  Console.ReadKey false |> ignore
  ()

main()

Thanks again!
Cameron

By on 11/13/2009 11:11 AM ()
IntelliFactory Offices Copyright (c) 2011-2012 IntelliFactory. All rights reserved.
Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us | Terms of Use | Privacy Policy | Cookie Policy
Built with WebSharper