2013-03-07 171 views
3

对不起,这个帖子很长。我想使用TcpListener来侦听端口,处理不同(后台)线程中传入连接请求的繁重提升,然后在准备就绪时将响应发送回客户端。我已经阅读了MSDN上的很多代码和示例,并为服务器提供了以下实现。使TcpListener异步处理连接的正确方法是什么?

对于所有下面的实施方式的,请假设以下变量:

let sva = "127.0.0.1" 
let dspt = 32000 

let respondToQuery (ns_ : NetworkStream) (bta_ : byte array) : unit = 
    // DO HEAVY LIFTING 
    () 

实施1(普通纸,同步服务器;我的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" 

实现2 (我的翻译代码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" 

实现3(我基于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" 

,从我的MSDN文档的阅读,我本来以为Implementation 3将是最快的。但是当我用多台机器的多个查询来访问服务器时,它们的运行速度大致相同。这使我相信我一定在做错事。

或者是Implementation 2Implementation 3“正确”的方式来实现一个TcpListener,做了繁重的背景下,当它完成,同时允许其他客户或许还连接并开始另一个返回到客户端的响应任务在另一个后台线程?如果没有,请告诉我应该阅读哪些类(或教程)?

回答

6

主回路正确的结构应该是这样的:

let respondToQuery (client:TcpClient) = async { 
    try 
    let stream = client.GetStream() 
    () // TODO: The actual processing goes here! 
    finally 
    client.Close() } 

async { 
    while true do 
    let! client = t.AcceptTcpClientAsync() |> Async.AwaitTask 
    respondToQuery client |> Async.Start } 

关键的事情需要注意的是:

  • 我包的主循环​​中async这样就可以等待客户端异步使用AcceptTcpClientAsync(无阻塞)

  • respondToQuery函数返回一个n表示在后台使用Async.Start开始,这样的处理可以并行继续使用(使用Async.RunSynchronously时,你会阻塞等待,直到respondToQuery完成)

  • 为了使这个完全异步等待下一个客户端异步计算, respondToQuery内部的代码也需要使用流的异步操作 - 查找AsyncReadAsyncWrite

你也使用Async.StartChild,在这种情况下,孩子的计算(的respondToQuery体),得到了相同的取消标记为母公司,所以当你取消主异步工作流程,将取消所有的孩子太:

while true do 
    let! client = t.AcceptTcpClientAsync() |> Async.AwaitTask 
    do! respondToQuery client |> Async.StartChild |> Async.Ignore } 

Async.StartChild方法返回一个异步计算(使用let!do!开始),我们需要忽略它返回(可用于等到孩子完成)令牌。

+0

非常感谢! – Shredderroy 2013-03-07 14:59:40

+0

你可以使用'use client = client'而不是'try..finally'吗? – 2013-03-10 12:10:16

相关问题