2012-09-27 42 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,我不知道如何。如果你可以给我一个接受连接的例子,使用这些方法从他们那里接收数据,我将非常感激它 –