F# 和 UdpClient 接收器

F# and UdpClient reciver

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

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# 中并不是真正地道,您应该:

  • 比元组更喜欢分离参数,它增加了使用柯里化的机会。
  • 仅在需要时才使用类型注释,这样可以缩短代码。
  • 命名函数,使其成为动词而不是名词,并使用驼峰命名法。

我会重写你的代码如下:

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...