Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I receive and send over UDP asynchronously with F#?

I am trying to make a messaging application over UDP, and get it to be able to continuously send and receive at the same time. I don't know how to achieve this and have tried a few things. Below is the code I have so far, could somebody point out what I'm doing wrong or what I need to add? Thank you.

open System
open System.Net
open System.Net.Sockets
open System.Text

printfn "Receive port: "
let receivePort = Console.ReadLine() |> int

let receivingClient = new UdpClient(receivePort)

let ReceivingIpEndPoint = new IPEndPoint(IPAddress.Any, 0)

printfn "Send address: "
let sendAddress = IPAddress.Parse(Console.ReadLine())

printfn "Send port: "
let sendPort = Console.ReadLine() |> int

let sendingClient = new UdpClient()

let sendingIpEndPoint = new IPEndPoint(sendAddress, sendPort)

let rec loop() =
    let receive = async {
        try
            let! receiveResult = receivingClient.ReceiveAsync() |> Async.AwaitTask
            let receiveBytes = receiveResult.Buffer
            let returnData = Encoding.ASCII.GetString(receiveBytes)
            printfn "%s" returnData
        with
            | error -> printfn "%s" error.Message
    }

    receive |> ignore

    printfn "Send message: "
    let (sendBytes: byte array) = Encoding.ASCII.GetBytes(Console.ReadLine())

    try
        sendingClient.Send(sendBytes, sendBytes.Length, sendingIpEndPoint) |> ignore
    with
        | error -> printfn "%s" error.Message

    loop()

loop()

Console.Read() |> ignore

1 Answers

One obvious issue with your code is that you create an asynchronous computation receive and then ignore it, without ever starting it. This means that your current version is only sending.

I assume that you intended to start the receiving process in the background. To do that, let's first define receive and send as two separate asynchronous functions:

let receive () = async {
    try
        let! receiveResult = receivingClient.ReceiveAsync() |> Async.AwaitTask
        let receiveBytes = receiveResult.Buffer
        let returnData = Encoding.ASCII.GetString(receiveBytes)
        printfn "%s" returnData
    with
        | error -> printfn "%s" error.Message }

let send () = async {
    printfn "Send message: "
    let (sendBytes: byte array) = Encoding.ASCII.GetBytes(Console.ReadLine())
    try
        sendingClient.Send(sendBytes, sendBytes.Length, sendingIpEndPoint) |> ignore
    with
        | error -> printfn "%s" error.Message }

Now, there are different ways to send and receive "at the same time" depending on what exactly you mean by "at the same time". You can start receiving in the background, then send at the same time and then wait until both sending and receiving completes before looping:

let rec loop() = async {
    let! wait = Async.StartChild (receive ())
    do! send () 
    do! wait
    return! loop() }

loop() |> Async.Start

Alternatively, you could also start two loops, one that keeps sending and another one that keeps receiving as fast as they can:

let rec loop1() = async {
    do! receive ()
    return! loop1() }

let rec loop2() = async {
    do! send ()
    return! loop2() }

loop1() |> Async.Start
loop2() |> Async.Start
like image 59
Tomas Petricek Avatar answered Dec 07 '25 22:12

Tomas Petricek



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!