2012-09-27 45 views
32

我以前使用BeginAccept()BeginRead(),但使用Visual Studio 2012我想在我的套接字服務器程序中使用新的異步(async,await)功能。使用.Net 4.5異步功能進行套接字編程

如何完成AcceptAsyncReceiveAsync的功能?

using System.Net; 
using System.Net.Sockets; 

namespace OfficialServer.Core.Server 
{ 
    public abstract class CoreServer 
    { 
     private const int ListenLength = 500; 
     private const int ReceiveTimeOut = 30000; 
     private const int SendTimeOut = 30000; 
     private readonly Socket _socket; 

     protected CoreServer(int port, string ip = "0.0.0.0") 
     { 
      _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 
      _socket.Bind(new IPEndPoint(IPAddress.Parse(ip), port)); 
      _socket.Listen(ListenLength); 
      _socket.ReceiveTimeout = ReceiveTimeOut; 
      _socket.SendTimeout = SendTimeOut; 
      _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); 
      _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true); 
     } 

     public void Start() 
     {  
     } 
    } 
} 

回答

52

......因爲你是如此的堅定,所以我編寫了一個非常簡單的例子,說明如何編寫一個回顯服務器來讓你順利。收到的任何東西都會回傳給客戶端。服務器將保持運行60秒。嘗試在localhost端口6666上telnet到它。花點時間來理解這裏到底發生了什麼。

void Main() 
{ 
    CancellationTokenSource cts = new CancellationTokenSource(); 
    TcpListener listener = new TcpListener(IPAddress.Any, 6666); 
    try 
    { 
     listener.Start(); 
     //just fire and forget. We break from the "forgotten" async loops 
     //in AcceptClientsAsync using a CancellationToken from `cts` 
     AcceptClientsAsync(listener, cts.Token); 
     Thread.Sleep(60000); //block here to hold open the server 
    } 
    finally 
    { 
     cts.Cancel(); 
     listener.Stop(); 
    } 
} 

async Task AcceptClientsAsync(TcpListener listener, CancellationToken ct) 
{ 
    var clientCounter = 0; 
    while (!ct.IsCancellationRequested) 
    { 
     TcpClient client = await listener.AcceptTcpClientAsync() 
              .ConfigureAwait(false); 
     clientCounter++; 
     //once again, just fire and forget, and use the CancellationToken 
     //to signal to the "forgotten" async invocation. 
     EchoAsync(client, clientCounter, ct); 
    } 

} 
async Task EchoAsync(TcpClient client, 
        int clientIndex, 
        CancellationToken ct) 
{ 
    Console.WriteLine("New client ({0}) connected", clientIndex); 
    using (client) 
    { 
     var buf = new byte[4096]; 
     var stream = client.GetStream(); 
     while (!ct.IsCancellationRequested) 
     { 
      //under some circumstances, it's not possible to detect 
      //a client disconnecting if there's no data being sent 
      //so it's a good idea to give them a timeout to ensure that 
      //we clean them up. 
      var timeoutTask = Task.Delay(TimeSpan.FromSeconds(15)); 
      var amountReadTask = stream.ReadAsync(buf, 0, buf.Length, ct); 
      var completedTask = await Task.WhenAny(timeoutTask, amountReadTask) 
              .ConfigureAwait(false); 
      if (completedTask == timeoutTask) 
      { 
       var msg = Encoding.ASCII.GetBytes("Client timed out"); 
       await stream.WriteAsync(msg, 0, msg.Length); 
       break; 
      } 
      //now we know that the amountTask is complete so 
      //we can ask for its Result without blocking 
      var amountRead = amountReadTask.Result; 
      if (amountRead == 0) break; //end of stream. 
      await stream.WriteAsync(buf, 0, amountRead, ct) 
         .ConfigureAwait(false); 
     } 
    } 
    Console.WriteLine("Client ({0}) disconnected", clientIndex); 
} 
+0

非常感謝你幫助我們瞭解事情的進展。 –

+0

沒問題。你似乎對最好的方法有點困惑,所以希望這會稍微澄清一些事情。 – spender

+0

但是現在最後一個問題:D,在使用Old BeginReceive和新的ReceiveAsync之間有什麼區別?或者它有點相同?! –

12

您可以使用TaskFactory.FromAsyncBegin /End雙包成async就緒的操作。

Stephen Toub在他的博客上有一個awaitable Socket,其中包含更有效的*Async終結點​​。我建議將其與TPL Dataflow結合使用以創建完全兼容async的組件。Socket組件。

+2

我不想打包開始和結束(如果我正確理解你)。我想要做的就是使用.AcceptAsync代替.BeginAccept,而.ReceiveAsync代替.BeginReceive –

+3

'AcceptAsync'和'ReceiveAsync'使用[特殊形式的異步API](http://msdn.microsoft.com/ en-us/library/system.net.sockets.socketasynceventargs.aspx)只存在於'Socket'類中。它們與'async'和'await'無關。 –

+2

:D是這就是我想要的,但我無法實現使用SocketAsyncEventArgs,我不知道如何。如果你可以給我一個接受連接的例子,使用這些方法從他們那裏接收數據,我將非常感激它 –