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?

esac
  • 24,099
  • 38
  • 122
  • 179
  • Just wanted to note that the C# code can StackOverflow; to avoid it you need to check ar.CompletedSynchronously and... ugh... it's a nightmare to do this in C#. Just be glad you have F#. :) – Brian Sep 23 '09 at 21:42
  • But for completeness, see http://blogs.msdn.com/mjm/archive/2005/05/04/414793.aspx for the fully correct C# code. – Brian Sep 23 '09 at 21:43

2 Answers2

5

@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
  • 117,631
  • 17
  • 236
  • 300
4

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
  • 54,864
  • 2
  • 91
  • 133
  • 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 Sep 23 '09 at 20:14
  • 1
    @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 Sep 23 '09 at 20:21