2012-09-27 19 views
32

que he usado anteriormente BeginAccept() y BeginRead(), pero con Visual Studio 2012 Quiero hacer uso de la nueva asíncrono (async, await) presenta en mi programa de servidor de socket.El uso de .Net 4.5 asíncrono Función para un enchufe de programación

¿Cómo puedo completar las funciones AcceptAsync y ReceiveAsync?

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() 
     {  
     } 
    } 
} 

Respuesta

52

... porque eres tan decidido, he creado un ejemplo muy simple de cómo escribir un servidor de eco para que puedas seguir tu camino. Todo lo recibido se devuelve al cliente. El servidor seguirá funcionando por 60 años. Intente realizar una función de telnet en el puerto del localhost 6666. Tómese el tiempo para comprender exactamente qué está pasando aquí.

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

Muchas gracias, esto ayudó a entender mucho cómo van las cosas. –

+0

No hay problema. Parecías un poco confundido acerca del mejor enfoque, así que espero que esto aclare un poco las cosas. – spender

+0

Pero una última pregunta por ahora: D, ¿hay alguna diferencia entre usar el Old BeginReceive y el nuevo ReceiveAsync en el rendimiento? o es un poco lo mismo?! –

12

Se puede utilizar para envolver TaskFactory.FromAsyncBegin/End pares en async operaciones -Ready.

Stephen Toub tiene un awaitable Socket en su blog que ajusta los extremos más eficaces *Async. Recomiendo combinar esto con TPL Dataflow para crear un componente completamente async compatible con Socket.

+2

No quiero ajustar Begin y End (si lo entiendo correctamente). lo que quiero hacer es usar .AcceptAsync en lugar de .BeginAccept, y .ReceiveAsync en lugar de .BeginReceive –

+3

'AcceptAsync' y' ReceiveAsync' usan una [forma especial de API asincrónica] (http://msdn.microsoft.com/ en-us/library/system.net.sockets.socketasynceventargs.aspx) que solo existe para la clase 'Socket'. No tienen nada que ver con 'async' y' await'. –

+2

: D sí eso es lo que quería pero no puedo lograr usar SocketAsyncEventArgs, no sé cómo. si pudieras darme un ejemplo de aceptar conexiones, recibir datos de ellos usando esos métodos lo agradecería mucho –

Cuestiones relacionadas