Désolé pour le post long. Je voudrais utiliser TcpListener
pour écouter sur un port, gérer le levage lourd demandé par une connexion entrante dans un autre thread (arrière-plan), puis renvoyer la réponse au client quand il est prêt. J'ai lu beaucoup de code et d'exemples sur MSDN, et viens avec les implémentations suivantes pour le serveur.Quelle est la manière correcte de rendre un TcpListener gérer les connexions de manière asynchrone?
Pour toutes les implémentations ci-dessous, s'il vous plaît prendre les variables suivantes:
let sva = "127.0.0.1"
let dspt = 32000
let respondToQuery (ns_ : NetworkStream) (bta_ : byte array) : unit =
// DO HEAVY LIFTING
()
MISE EN ŒUVRE 1 (serveur synchrone ordinaire,; ma traduction de the code from this MSDN page)
let runSync() : unit =
printfn "Entering runSync()"
let (laddr : IPAddress) = IPAddress.Parse sva
let (svr : TcpListener) = new TcpListener (laddr, dspt)
try
svr.Start()
let (bta : byte array) = Array.zeroCreate<byte> imbs
while true do
printfn "Listening on port %d at %s" dspt sva
let (cl : TcpClient) = svr.AcceptTcpClient()
let (ns : NetworkStream) = cl.GetStream()
respondToQuery ns bta
cl.Close()
svr.Stop()
printfn "Exiting runSync() normally"
with
| excp ->
printfn "Error: %s" excp.Message
printfn "Exiting runSync() with error"
MISE EN ŒUVRE 2 (ma traduction du code on this MSDN page)
let runAsyncBE() : unit =
printfn "Entering runAsyncBE()"
let (tcc : ManualResetEvent) = new ManualResetEvent (false)
let (bta : byte array) = Array.zeroCreate<byte> imbs
let datcc (ar2_ : IAsyncResult) : unit =
let tcpl2 = ar2_.AsyncState :?> TcpListener
let tcpc2 = tcpl2.EndAcceptTcpClient ar2_
let (ns2 : NetworkStream) = tcpc2.GetStream()
respondToQuery ns2 bta
tcpc2.Close()
tcc.Set() |> ignore
let rec dbatc (tcpl2_ : TcpListener) : unit =
tcc.Reset() |> ignore
printfn "Listening on port %d at %s" dspt sva
tcpl2_.BeginAcceptTcpClient (new AsyncCallback (datcc), tcpl2_) |> ignore
tcc.WaitOne() |> ignore
dbatc tcpl2_
let (laddr : IPAddress) = IPAddress.Parse sva
let (tcpl : TcpListener) = new TcpListener (laddr, dspt)
try
tcpl.Start()
dbatc tcpl
printfn "Exiting try block"
printfn "Exiting runAsyncBE() normally"
with
| excp ->
printfn "Error: %s" excp.Message
printfn "Exiting runAsyncBE() with error"
MISE EN ŒUVRE 3 (ma mise en œuvre basée sur the MSDN page for asynchronous workflows)
let runAsyncA() : unit =
printfn "Entering runAsyncA()"
let (laddr : IPAddress) = IPAddress.Parse sva
let (svr : TcpListener) = new TcpListener (laddr, dspt)
try
svr.Start()
let (bta : byte array) = Array.zeroCreate<byte> imbs
while true do
printfn "Listening on port %d at %s" dspt sva
let (cl : TcpClient) = svr.AcceptTcpClient()
let (ns : NetworkStream) = cl.GetStream()
async {respondToQuery ns bta} |> Async.RunSynchronously
cl.Close()
svr.Stop()
printfn "Exiting runAsyncA() normally"
with
| excp ->
printfn "Error: %s" excp.Message
printfn "Exiting runAsyncA() with error"
Maintenant, de ma lecture de la documentation MSDN, je l'aurais pensé que Implementation 3
aurait été le plus rapide. Mais quand je frappe le serveur avec plusieurs requêtes de plusieurs machines, elles fonctionnent toutes à peu près à la même vitesse. Cela m'amène à croire que je dois faire quelque chose de mal.
est soit Implementation 2
ou Implementation 3
la « bonne » façon de mettre en œuvre un TcpListener
qui fait son levage de charges lourdes en arrière-plan, et renvoie la réponse au client quand il est terminé, tout en permettant un autre client peut également se connecter et commencer une autre tâche dans un autre thread d'arrière-plan? Si non, pourriez-vous me dire quelles classes (ou tutoriels) devrais-je lire?
Merci beaucoup! – Shredderroy
Pouvez-vous utiliser 'use client = client' au lieu de' try..finally'? –