2012-09-27 11 views
32

प्रोग्रामिंग मैं पहले BeginAccept() और BeginRead(), का उपयोग किया है, लेकिन दृश्य स्टूडियो 2012 के साथ मैं नए अतुल्यकालिक (async, await) का उपयोग करना चाहते हैं मेरी सॉकेट सर्वर कार्यक्रम में पेश करता है।सॉकेट के लिए नेट 4.5 Async फ़ीचर का उपयोग

मैं AcceptAsync और 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() 
     {  
     } 
    } 
} 

उत्तर

52

... क्योंकि आप इतने दृढ़ हैं, मैंने आपके रास्ते पर जाने के लिए एक गूंज सर्वर लिखने का एक बहुत ही सरल उदाहरण रखा है। प्राप्त कुछ भी ग्राहक को वापस गूंज जाता है। सर्वर 60 के लिए चल रहा रहेगा। स्थानीयहोस्ट पोर्ट 6666 पर टेलनेटिंग करने का प्रयास करें। यह समझने के लिए समय लें कि वास्तव में क्या हो रहा है।

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

लेकिन अब के लिए एक आखिरी सवाल: डी, ​​क्या पुराने BeginReceive, और प्रदर्शन में नया ReceiveAsync का उपयोग करने के बीच कोई अंतर है? या यह थोड़े समान है? –

12

आप TaskFactory.FromAsync का उपयोग async -ready संचालन में Begin/End जोड़े लपेट कर सकते हैं।

स्टीफन टब के पास उनके ब्लॉग पर awaitable Socket है जो अधिक कुशल *Async एंडपॉइंट्स को लपेटता है। मैं TPL Dataflow के साथ async-संगत Socket घटक बनाने के लिए इसे संयोजन करने की अनुशंसा करता हूं।

+2

मैं शुरुआत और अंत को लपेटना नहीं चाहता (अगर मैं आपको सही ढंग से समझता हूं)। मैं जो करना चाहता हूं उसका उपयोग करना है। के बजाय .cceptAsync .BeginAccept, और इसके बजाय। ResiveAsync .BeginReceive –

+3

'AcceptAsync' और' ReceiveAsync' एक [विशेष रूप से एसिंक्रोनस एपीआई] का उपयोग करें (http://msdn.microsoft.com/ en-us/library/system.net.sockets.socketasynceventargs.aspx) जो केवल 'सॉकेट' वर्ग के लिए मौजूद है। उनके पास 'async' और' प्रतीक्षा 'के साथ कुछ लेना देना नहीं है। –

+2

: डी हाँ जो मैं चाहता था लेकिन मैं सॉकेटएसिंसेन्ट आर्ट्स का उपयोग करके हासिल नहीं कर सकता, मुझे नहीं पता कि कैसे। यदि आप मुझे कनेक्शन स्वीकार करने का एक उदाहरण प्रदान कर सकते हैं, तो उन तरीकों का उपयोग करके उनसे डेटा प्राप्त करना मैं इसकी समीक्षा करूंगा –