私は現在、F#で基本的にアクティブなTCP接続を格納している小さなサーバータイプをプログラミングしています。そのループでは、それは新しい接続をチェックし、それはまた次のコードを使用して、現在の接続がまだ生きていることを確認しますInvalidOperationExceptionコレクションが変更されましたが、反復中にコレクションを変更しませんでしたか?
これがうまく動作するかどうかを知るためにはtype TCPListenerServer(discoveryPort:int) =
let server = new TcpListener (IPAddress.Loopback, discoveryPort)
let activeConnections = new List<TcpClient>()
let cancellationToken = new CancellationTokenSource()
let connectionIsStillActive (client:TcpClient) =
let ipProperties = IPGlobalProperties.GetIPGlobalProperties()
let allTcpConnections = ipProperties.GetActiveTcpConnections()
let relevantTcpConnections = Array.filter (fun (connectionInfo:TcpConnectionInformation) ->
(connectionInfo.LocalEndPoint = (client.Client.LocalEndPoint :?> IPEndPoint)) && (connectionInfo.RemoteEndPoint = (client.Client.RemoteEndPoint :?> IPEndPoint))) allTcpConnections
try
let stateOfConnection = (Array.get relevantTcpConnections 0).State
match stateOfConnection with
| TcpState.Established ->
true
| _ ->
false
with
| :? System.IndexOutOfRangeException as ex ->
false
let rec loop (pendingConnection:Task<TcpClient>) = async {
let newPendingConnection, client =
match pendingConnection.Status with
| TaskStatus.Created | TaskStatus.WaitingForActivation | TaskStatus.WaitingToRun
| TaskStatus.WaitingForChildrenToComplete | TaskStatus.Running ->
(None, None)
| TaskStatus.Faulted ->
let result = pendingConnection.Exception
raise (new System.NotImplementedException())
| TaskStatus.Canceled ->
raise (new System.NotImplementedException())
| TaskStatus.RanToCompletion ->
let connectionTask = server.AcceptTcpClientAsync()
(Some connectionTask, Some pendingConnection.Result)
| _ ->
raise (new System.NotImplementedException())
// Add the new client to the list
Option.iter (fun c -> activeConnections.Add c) client
// Switch the new pending connection if there is one
let connectionAttempt = defaultArg newPendingConnection pendingConnection
// Check that the connections are still alive
let connectionsToRemove = Seq.filter (fun (connection:TcpClient) -> not (connectionIsStillActive connection)) activeConnections
Seq.iter (fun connection -> activeConnections.Remove connection |> ignore) connectionsToRemove // <-- Exception happens here
Async.Sleep 1000 |> Async.RunSynchronously
return! loop connectionAttempt
}
member x.Start() =
if not cancellationToken.Token.CanBeCanceled
then
raise (new System.Exception("Cancellation token cannot be used to cancel server loop task."))
try
server.Start()
let connectionTask = server.AcceptTcpClientAsync()
Async.Start (loop connectionTask, cancellationToken.Token)
with
| :? SocketException as ex ->
server.Stop()
raise ex
member x.Stop() =
cancellationToken.Cancel()
Async.Sleep 2000 |> Async.RunSynchronously
server.Stop()
activeConnections.Clear()
member x.ActiveConnections =
activeConnections
、私はこの単純なユニット・テストを実施しています
のSystem.InvalidOperationException:コレクションカ月だった残念ながら、私はこのテストを実行したときに、私は、クライアント上で
Close()
を呼び出した後、私は次の例外を取得し[<TestMethod>] [<TestCategory(Networking)>] member x.``Start Server, Client Connects, then Disconnects``() = let server = new TCPListenerServer(44000) server.Start() let client = createClientAndConnect 44000 Async.Sleep 5000 |> Async.RunSynchronously client.GetStream().Close() client.Close() Async.Sleep 5000 |> Async.RunSynchronously Assert.IsTrue(server.ActiveConnections.Count = 0, "There are still connections in the server's active connections list.") cleanupTest server (Some [client])
異なる。列挙操作が実行されないことがあります。 [email protected] [T](FSharpFunc
2 f, IEnumerator
1 EのSystem.Collections.Generic.List1.Enumerator.MoveNextRare() at System.Collections.Generic.List
1.Enumerator.MoveNext() でSystem.ThrowHelper.ThrowInvalidOperationException(ExceptionResourceリソース) において、 FSharpRef1 started, Unit unitVar0) at [email protected]ctions-IEnumerator-MoveNext() at Microsoft.FSharp.Collections.SeqModule.Iterate[T](FSharpFunc
2アクション、IEnumerableを1 source) at [email protected](Unit unitVar) in E:\Documents\Source Control Projects\WiDroid\Core.Networking\TCPListenerServer.fs:line 61 at [email protected](AsyncParams
1引数)
私はあなたがそれを反復しながら、コレクションを変更するときに、この例外が発生したことを理解しています。しかし私は私が見ることができるものから自分のコードで直接行っていません。なぜこの例外が発生するのだろうと思っていますか?
私はF#はわかりませんが、私の推測では、非アクティブなソケットクリーンアップコードです: 'let connectionsToRemove = Seq.filter(fun(connection:TcpClient) - > not(connectionIsStillActive connection))activeCo Seq.iter(接続を楽しむ - > activeConnections.接続を削除する|>無視する)connectionsToRemove'。私は 'connectionsToRemove'を反復処理することは実際には' activeConnections'を繰り返し処理すると思っています。これは反復処理中にアイテムを 'activeConnections'から削除すると問題を引き起こします。 – wablab
@wablab例外は、その場所で起こります: '' Seq.iter(fun connection - > activeConnections.Remove connection |> ignore)connectionsToRemove''実際に。私は2つのリストがどのように結びついているのかわかりませんし、なぜ1つを反復するのが他のものを反復するのでしょうか? – Choub890
あなたの問題を[最小限で完全であり、検証可能な例](http://stackoverflow.com/help/mcve)に絞り込むために、バイナリ検索の使用を検討してください。多くの場合、私の経験では、それを実行する単なる行為は、あなた自身の問題のトラブルシューティングを可能にするでしょう。 –