3

我正在使用 F# 做一些低级别的套接字工作,并使一切异步。我有一个套接字,用于使用异步工作流来监听连接以处理它们,因此它使用它来包装 Socket.BeginListen() 和 Socket.EndListen()。

member socket.AsyncAccept () =
    Async.FromBeginEnd( socket.BeginAccept, endOrDisposed socket.EndAccept null )

当我想停止收听时,目前我正在对其执行 Socket.Close() ,这将导致由 Socket.BeginAccept() 启动的异步操作完成。

最初我遇到了一个问题,因为无论 BeginXXX() 启动的操作如何完成,FromBeginEnd() 函数都会调用 EndXXX() 函数。在某些情况下,这会导致 EndXXX() 函数发生 ObjectDisposed 异常,因为在调用它时,Socket 已经关闭并被释放。我添加了一个小处理函数,可以过滤掉这些异常:

let endOrDisposed endFunc defaultResult iar = try endFunc iar with | _ -> defaultResult

这可以解决问题,但在调试中运行时不行。我很清楚 Just My Code 选项可用于隐藏异常,但这可能会更频繁地发生在其他一些 IO 操作中,因此我也不想将处理器时间浪费在引发和捕获不应该的异常上一开始就真的在那里。此外,我可能不想隐藏在我需要调试的区域中引发其他异常的位置。

我查看了 Async.FromBeginEnd 的代码,无论如何它总是调用 EndXXX() 函数,我不确定这是最好的行为,也许我应该写一个替换它?或者有人对优雅的解决方案有任何其他想法吗?


我刚刚在文档中发现(当然我以前看过)这个:

要取消对 BeginAccept() 方法的挂起调用,请关闭 Socket。当异步操作正在进行时调用 Close() 方法时,将调用提供给 BeginAccept() 方法的回调。对 EndAccept() 方法的后续调用将引发 ObjectDisposedException 以指示操作已被取消。

我仍然不喜欢这个例外,即使它是设计使然。我宁愿找到一种不在已处置对象上调用 EndXXX() 的方法。也许我可以以某种方式将一些 CancellationToken 魔法混入其中?

4

2 回答 2

1

我会在这里使用可选cancelAction参数Async.FromBeginEnd

type Socket with
  member this.AsyncAccept () =
    let canceled = ref false
    let endAccept iar = if not !canceled then this.EndAccept iar else null
    let cancel () = canceled := true; this.Close ()
    Async.FromBeginEnd (this.BeginAccept, endAccept, cancelAction = cancel)

这样您就可以使用Async' 的内置取消功能(当然基于CancellationToken),而无需直接触摸 a CancellationToken。(即,您Socket.Close应该出于取消目的而致电。)

于 2012-07-12T02:50:37.217 回答
0

需要一种方法来中断 Async.FromBeginEnd 进程并在套接字关闭的情况下阻止它尝试 EndAccept(这可能会扩展到 Accept 之外的其他操作)。

我没有调用 Socket.Close(),而是使用了 CancellationTokenSource 并使用它注册了一个处理程序,该处理程序将调用 Socket.Close():

cts.Token.Register (fun () -> listener.Close()) |> ignore

然后不是直接传递 Async.FromBeginEnd EndAccept 函数,而是现在通过一个函数,如果令牌已被取消,该函数将停止调用 EndAccept。

let endOrDisposed endFunc defaultResult iar = try endFunc iar with | _ -> defaultResult
let endIfNotCancelled endFunc defaultResult (token:CancellationToken) iar = if token.IsCancellationRequested then defaultResult else endOrDisposed endFunc defaultResult iar

type Socket with
  member socket.AsyncAccept (cancelToken) =
    Async.FromBeginEnd( socket.BeginAccept, endIfNotCancelled socket.EndAccept null cancelToken)

这仍然使用我的函数来过滤和忽略异常(我不认为异常在网络方面是非常特殊的,在这个应用程序的情况下,无论如何它都不会做任何响应)。

于 2012-07-11T16:45:52.300 回答