views:

237

answers:

2

In C# I am using the asynchronous versions of TcpListener/TcpClient, and I am chaining these via the callback method so that another Accept/Read is posted when the callback completes. Here is an example (untested):

    public void Start()
    {
        TcpListener listener = new TcpListener(IPAddress.Any, 3000);
        listener.Start();

        PostAccept(listener);
    }

    private void PostAccept(TcpListener listener)
    {
        listener.BeginAcceptTcpClient(AcceptCallback, listener);
    }

    private void AcceptCallback(IAsyncResult ar)
    {
        var listener = ar.AsyncState as TcpListener;

        if (listener == null)
        {
            return;
        }

        // get the TcpClient and begin a read chain in a similar manner

        PostAccept(listener);
    }

My question is how do I model something similar in F#? Would I use the async keyword? Is the code after BeginAcceptTcpClient, BeginRead, etc.. essentially the code that would be executed in the callback function? For example, is this correct?

let accept(listener:TcpListener) = async {      
  let! client = listener.AsyncAcceptTcpClient()    
  // do something with the TcpClient such as beginning a Read chain
  accept(listener)
  return()
}

The above code doesn't work because accept isn't defined, and marking it recursive technically isn't true as it isn't a recursive method?

+2  A: 

I'm not sure what you mean by "it isn't a recusive method"; if you refer to a function from within its own definition, then it is a recursive function. I don't have much experience with the Sockets classes, but perhaps something along these lines is what you're looking for?

let rec accept(t : TcpListener) =
  async {
    let! client = t.AsyncAcceptTcpClient()

    // do some stuff with client here

    do! accept(t)
  }
kvb
I guess my question is more about semantics. Recursive methods in languages such as C# will eventually result in a stack overflow error. In my C# code example, it is not recursive, and will not result in a stack overflow. In F# does the 'rec' keyword mean that it will somehow be called recursively, or that it just provides that methods signature to the method itself? I guess I can just ildasm and find out, but I was curious if anyone knew.
esac
@esac: The `rec` keyword allows the function's definition to refer to the function itself. As far as compilation strategy, this may or may not result in a recursive method being generated (for instance, a single tail recursive function may be compiled into a loop rather than a recursive method). Any tail recursive method should be compiled in such a way that it will not lead to a stack overflow (even if it is compiled to a recursive method, it will use the .tail instruction in IL to prevent overflow). Note that this is true for Release mode in VS2010, but may not apply to Debug mode.
kvb
+2  A: 

@kvb's answer is correct and idiomatic.

Just wanted to also point out you can use (gasp) a loop:

let accept(t:TcpListener) =
    let completed = ref false 
    async {
        while not(!completed) do 
            let! client = t.AsyncAcceptTcpClient()
            if client <> null then
                Blah(client)
            else
                completed := true
    }

(that was typing code into my browser, hopefully it compiles). Which is nice, since you certainly can't use a loop in the C# code (that has to span multiple methods with the begin/end callbacks).

Brian
That is actually really cool, although as you refer to a little scary :)
esac