Good usage of MailboxProcessor/Agent in class rather than mutable field?
Your code looks pretty good to me - I would only change some minor details.


I'd replace UdpClient seq with UdpClient list, because lazy sequences can be subtle. In your code, this doesn't seem to be a problem, but they can cause unexpected GC issues (by capturing a reference) and tricky performance behavior (by re-evaluating things).
I'd probably write the creation of clients differently - I think the following is easier to understand:

let uniAndMulticastUdpClients =
  [ for ip in ipAddresses do
      yield createUnicastClient(ip, port)
      yield createMulticastClient(ip) ]

You're calling closeRunningClients in both branches, so you can extract it and move it before the match construct (and, in fact, you can just use the Seq.iter call without defining a function for it).