F#和UdpClient接收器

问题描述

我正在使用.Net UdpCLient类在f#中创建UDP接收器,它看起来很简单:

let Start (ip: IPAddress,port : int32) : Async<unit> = 
    async {
        try
            let endpoint = IPEndPoint(ip,port)
            use receivingClient = new UdpClient();
            receivingClient.Client.Bind(endpoint)
            let! receiveResult = receivingClient.ReceiveAsync() |> Async.AwaitTask
            let receiveBytes = receiveResult.Buffer
            printfn "%A" receiveBytes 
        with | ex -> raise (ex)
    }

为了保持它的生命,我正在使用另一个使用rec函数属性,它看起来像:

let Watcher (ip: IPAddress,port : int32) : unit =
    let rec listenerWatcher () = 
        async {
            try
                do! Start (ip,port)
                return! listenerWatcher() 
            with | :? UdpClientdisposedException ->
                return ()
        }        
    listenerWatcher() |> Async.Start

并且使用类型进行调用很简单:

UdpReceiver.Watcher (ip,port) (* where UdpReceiver is module name *)

我的问题是我只收到第一个传入的软件包,就像侦听器在收到第一个传入的软件包后关闭一样,这可能是什么问题?

解决方法

也许您的问题是发送包裹的速度太快。收到第一个程序包后,需要一些时间才能再次启动接收程序,但与此同时,发送方仍在发送下一个程序包。

不确定您的确切意图是什么,但是我认为您应该只启动(设置)接收程序一次,然后重复接收传入的程序包,并且仅在发生错误(引发异常)时重新启动接收程序。

顺便说一句,您的代码在F#中并不是真正惯用的,应该:

  • 在元组中优先使用分隔的参数,这增加了使用curring的机会。
  • 仅在需要时使用类型注释,这会使代码更短。
  • 使用名称函数,使它们成为动词而不是名词,并使用camelCase样式。

我将如下重写您的代码:

let start (ip: IPAddress) port =
    let endpoint = IPEndPoint (ip,port)
    let receivingClient = new UdpClient ()
    receivingClient.Client.Bind endpoint
    let rec loop () = async {
        printfn "Waiting..."
        let! receiveResult = receivingClient.ReceiveAsync () |> Async.AwaitTask
        let receiveBytes = receiveResult.Buffer
        printfn "Receive: %A" receiveBytes
        return! loop ()
    }
    loop ()

let watch ip port =
    let rec loop () =  async {
        try
            return! start ip port
        with ex ->
            printfn "Error: %s" ex.Message
            return! loop ()
    }        
    loop ()

// in main function or somewhere:
watch ... ... |> Async.Start...