Async नेटवर्क प्रोग्रामिंग रिएक्टिव एक्सटेंशन्स का उपयोग करते हुए


25

कुछ साल (अधिक-या-कम) "निम्न-स्तर" की async socketप्रोग्रामिंग करने के बाद सालों पहले (एक इवेंट-आधारित एसिंक्रोनस पैटर्न (EAP) फैशन में) और हाल ही में एक TcpListenerअतुल्यकालिक प्रोग्रामिंग मॉडल (APM) पर "ऊपर" जा रहा है और फिर async/await(टास्क-आधारित एसिंक्रोनस पैटर्न (टीएपी) ) को स्थानांतरित करने की कोशिश कर रहा हूं, मुझे बहुत खुशी हुई है कि यह सब 'निम्न स्तर की पाइपलाइन' से परेशान है। तो मुझे लगा था; क्यों नहीं RXएक जाने के बाद ( प्रतिक्रियात्मक एक्सटेंशन ) क्योंकि यह मेरी समस्या डोमेन के लिए अधिक snugly फिट हो सकता है।

बहुत सारे कोड जो मैंने लिखे हैं, उनमें कई क्लाइंट्स के साथ Tcp को मेरे एप्लिकेशन से कनेक्ट करना है जो तब टू-वे (async) कम्युनिकेशन शुरू करते हैं। क्लाइंट या सर्वर किसी भी बिंदु पर तय कर सकते हैं कि एक संदेश भेजने और ऐसा करने की आवश्यकता है, इसलिए यह आपका क्लासिक request/responseसेटअप नहीं है, बल्कि वास्तविक समय, दो-तरफ़ा, "लाइन" दोनों पक्षों के लिए खुला है जो वे चाहते हैं भेजने के लिए , जब चाहें। (यदि किसी के पास इसका वर्णन करने के लिए एक सभ्य नाम है तो मुझे यह सुनकर खुशी होगी!)।

"प्रोटोकॉल" प्रति एप्लिकेशन में भिन्न होता है (और वास्तव में मेरे प्रश्न के लिए प्रासंगिक नहीं है)। हालाँकि, मेरा प्रारंभिक प्रश्न है:

  1. यह देखते हुए कि केवल एक "सर्वर" चल रहा है, लेकिन इसे कई (आमतौर पर हजारों) कनेक्शनों का ट्रैक रखना पड़ता है (उदाहरण के लिए क्लाइंट) प्रत्येक के पास (बेहतर विवरण की कमी के लिए) अपने स्वयं के "स्टेट मशीन" को उनके आंतरिक का ट्रैक रखने के लिए राज्यों आदि, जो दृष्टिकोण आप पसंद करेंगे? ईएपी / नल / एपीएम? क्या आरएक्स को भी एक विकल्प माना जाएगा? यदि नहीं, तो क्यों?

इसलिए, मुझे Async काम करने की आवश्यकता है क्योंकि a) यह एक अनुरोध / प्रतिक्रिया प्रोटोकॉल नहीं है, इसलिए मेरे पास "संदेश के इंतजार में" / कॉल को कॉल करने या "संदेश भेजने" के लिए एक थ्रेड / क्लाइंट नहीं हो सकता है (हालांकि, अगर भेज है तो) उस ग्राहक के लिए अवरुद्ध करना केवल मैं उसके साथ रह सकता था) और बी) मुझे कई समवर्ती कनेक्शनों को संभालने की आवश्यकता है। मैं अवरुद्ध कॉल का उपयोग करके ऐसा करने का कोई तरीका नहीं देखता (मज़बूती से)।

मेरे अधिकांश एप्लिकेशन VoiP से संबंधित हैं; यह SIP के ग्राहक या पीबीएक्स (संबंधित) संदेश जैसे कि FreeSwitch / OpenSIPS इत्यादि से संदेश भेजना चाहते हैं, लेकिन आप सबसे सरल रूप में, एक "चैट" सर्वर की कल्पना करने की कोशिश कर सकते हैं जो कई "चैट" क्लाइंट को संभालने की कोशिश कर रहा है। अधिकांश प्रोटोकॉल पाठ आधारित (ASCII) हैं।

इसलिए, उपरोक्त तकनीकों के कई अलग-अलग क्रमों को लागू करने के बाद, मैं एक ऐसी वस्तु बनाकर अपने काम को आसान बनाना चाहूंगा जिसे मैं तुरंत कर सकता हूं, यह बताएं कि किस IPEndpointपर सुनना है और क्या मुझे यह बताना है कि जब भी ब्याज की कोई चीज चल रही हो (जो आमतौर पर होती है) के लिए घटनाओं का उपयोग करें, इसलिए कुछ ईएपी को आमतौर पर अन्य दो तकनीकों के साथ मिलाया जाता है)। वर्ग को प्रोटोकॉल को 'समझने' की कोशिश करने से परेशान नहीं होना चाहिए; इसे केवल इनकमिंग / आउटगोइंग स्ट्रिंग्स को हैंडल करना चाहिए। और इस प्रकार, आरएक्स पर मेरी आंख होने की उम्मीद है कि (अंत में) काम को सरल करेगा, मैंने नियंत्रण से एक नया "फिडल" बनाया:

using System;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.Reactive.Linq;
using System.Text;

class Program
{
    static void Main(string[] args)
    {
        var f = new FiddleServer(new IPEndPoint(IPAddress.Any, 8084));
        f.Start();
        Console.ReadKey();
        f.Stop();
        Console.ReadKey();
    }
}

public class FiddleServer
{
    private TcpListener _listener;
    private ConcurrentDictionary<ulong, FiddleClient> _clients;
    private ulong _currentid = 0;

    public IPEndPoint LocalEP { get; private set; }

    public FiddleServer(IPEndPoint localEP)
    {
        this.LocalEP = localEP;
        _clients = new ConcurrentDictionary<ulong, FiddleClient>();
    }

    public void Start()
    {
        _listener = new TcpListener(this.LocalEP);
        _listener.Start();
        Observable.While(() => true, Observable.FromAsync(_listener.AcceptTcpClientAsync)).Subscribe(
            //OnNext
            tcpclient =>
            {
                //Create new FSClient with unique ID
                var fsclient = new FiddleClient(_currentid++, tcpclient);
                //Keep track of clients
                _clients.TryAdd(fsclient.ClientId, fsclient);
                //Initialize connection
                fsclient.Send("connect\n\n");

                Console.WriteLine("Client {0} accepted", fsclient.ClientId);
            },
            //OnError
            ex =>
            {

            },
            //OnComplete
            () =>
            {
                Console.WriteLine("Client connection initialized");
                //Accept new connections
                _listener.AcceptTcpClientAsync();
            }
        );
        Console.WriteLine("Started");
    }

    public void Stop()
    {
        _listener.Stop();
        Console.WriteLine("Stopped");
    }

    public void Send(ulong clientid, string rawmessage)
    {
        FiddleClient fsclient;
        if (_clients.TryGetValue(clientid, out fsclient))
        {
            fsclient.Send(rawmessage);
        }
    }
}

public class FiddleClient
{
    private TcpClient _tcpclient;

    public ulong ClientId { get; private set; }

    public FiddleClient(ulong id, TcpClient tcpclient)
    {
        this.ClientId = id;
        _tcpclient = tcpclient;
    }

    public void Send(string rawmessage)
    {
        Console.WriteLine("Sending {0}", rawmessage);
        var data = Encoding.ASCII.GetBytes(rawmessage);
        _tcpclient.GetStream().WriteAsync(data, 0, data.Length);    //Write vs WriteAsync?
    }
}

मुझे पता है कि, इस "फिडेल" में, थोड़ा कार्यान्वयन विशिष्ट विवरण है; इस मामले में मैं फ्रीस्विच ईएसएल के साथ काम कर रहा हूं, इसलिए फिडेल"connect\n\n" में, जब अधिक सामान्य दृष्टिकोण के लिए रिफैक्टरिंग किया जाना चाहिए, तो हटा दिया जाना चाहिए।

मुझे यह भी पता है कि मुझे सर्वर क्लास पर निजी तरीकों के लिए अनाम तरीकों को फिर से भरने की आवश्यकता है; मुझे यकीन नहीं है कि OnSomethingउनके विधि-नामों के लिए उपयोग करने के लिए कौन सा सम्मेलन (उदाहरण के लिए " ")?

यह मेरा आधार / आरंभ-बिंदु / आधार है (जिसे कुछ "ट्विकिंग" की आवश्यकता है)। इस बारे में मेरे कुछ सवाल हैं:

  1. उपरोक्त प्रश्न "1" देखें
  2. क्या मैं सही रास्ते पर हूं? या मेरे "डिजाइन" निर्णय अन्यायपूर्ण हैं?
  3. Concurrency-wise: यह हजारों ग्राहकों के साथ सामना करेगा (वास्तविक संदेशों को एक तरफ पार्स / संभालना)
  4. अपवादों पर: मुझे यकीन नहीं है कि ग्राहकों को "सर्वर" ("आरएक्स-वार") के भीतर उठाए गए अपवादों को कैसे प्राप्त किया जाए; क्या एक अच्छा तरीका होगा?
  5. अब मैं अपने सर्वर वर्ग (इसका उपयोग करके ClientId) से कोई भी जुड़ा हुआ क्लाइंट प्राप्त कर सकता हूं , यह मानते हुए कि मैं ग्राहकों को एक या दूसरे तरीके से उजागर करता हूं, और सीधे उन पर कॉल के तरीके। मैं सर्वर वर्ग के माध्यम से भी तरीकों को कॉल कर सकता हूं (उदाहरण के लिए, Send(clientId, rawmessage)विधि (जबकि बाद वाला दृष्टिकोण दूसरी तरफ एक संदेश प्राप्त करने के लिए "सुविधा" विधि होगी)।
  6. मुझे यकीन नहीं है कि यहाँ से कहाँ (और कैसे) जाऊंगा:
    • क) मुझे आने वाले संदेशों को संभालने की जरूरत है; मैं इसे कैसे सेट करूंगा? मैं धारा बिल्कुल प्राप्त कर सकते हैं, लेकिन जहां मैं प्राप्त बाइट्स पुन: प्राप्त करने संभाल होगा? मुझे लगता है कि मुझे किसी प्रकार के "ऑब्जर्वेबलस्ट्रीम" की आवश्यकता है, जो कि मैं सदस्यता ले सकता हूं? क्या मैं इसे डालूंगा FiddleClientया FiddleServer?
    • ख) यह मानते हुए कि मैं इवेंट का उपयोग करने से बचना चाहता हूं जब तक कि इन FiddleClient/ FiddleServerवर्गों को विशेष रूप से अपने एप्लिकेशन विशिष्ट प्रोटोकॉल हैंडलिंग आदि को अधिक विशिष्ट FooClient/ FooServerकक्षाओं का उपयोग करने के लिए कार्यान्वित नहीं किया जाता है : मैं अंतर्निहित 'फिडेल-क्लास' में डेटा प्राप्त करने से उनके पास कैसे जाऊंगा अधिक विशिष्ट समकक्षों?

लेख / लिंक मैं पहले से ही पढ़ा / स्किम्ड / संदर्भ के लिए इस्तेमाल किया:


मौजूदा ReactiveSockets पुस्तकालय
फ्लैगबग

2
मैं पुस्तकालयों या लिंक (हालांकि संदर्भ के लिए उनकी सराहना की जाती है) के लिए नहीं देख रहा हूं, लेकिन मेरे सवालों और सामान्य सेटअप पर इनपुट / सलाह / सहायता के लिए। मैं अपना खुद का कोड सुधारना सीखना चाहता हूं और इस दिशा में कदम बढ़ाने के लिए अपने दिमाग को बेहतर बनाने में सक्षम होना चाहता हूं, समर्थक और विपक्ष का वजन करना आदि। कुछ पुस्तकालय का संदर्भ नहीं देना, इसे छोड़ना और आगे बढ़ना। मैं इस अनुभव से सीखना चाहता हूं और आरएक्स / नेटवर्क प्रोग्रामिंग के साथ अधिक अनुभव प्राप्त करना चाहता हूं।
रॉबथ

ज़रूर, लेकिन चूंकि पुस्तकालय खुला स्रोत है, आप देख सकते हैं कि वहाँ कैसे लागू किया गया
फ्लैगबग

1
ज़रूर, लेकिन स्रोत कोड को देखकर यह नहीं बताया गया है कि कुछ डिज़ाइन निर्णय क्यों किए गए / किए गए हैं। और क्योंकि मैं नेटवर्क प्रोग्रामिंग के साथ संयोजन में Rx के लिए अपेक्षाकृत नया हूं, मुझे यह बताने के लिए पर्याप्त अनुभव नहीं है कि क्या यह पुस्तकालय कोई अच्छा है, अगर डिजाइन समझ में आता है, अगर सही निर्णय किए गए थे और भले ही यह मेरे लिए सही हो।
राबेट आठ

मुझे लगता है कि एक हैंडशेक के सक्रिय सदस्य के रूप में सर्वर पर पुनर्विचार करना अच्छा होगा, इसलिए, यह सर्वर कनेक्शन के लिए सुनने के बजाय कनेक्शन शुरू करता है। उदाहरण के लिए, यहाँ: codeproject.com/Articles/20250/Reverse-Connection-Shell

जवाबों:


1

... मैं एक ऐसी वस्तु बनाकर अपने काम को आसान बनाना चाहूंगा जिसे मैं तुरंत कर सकता हूं, यह बताएं कि जिस पर IPEndpoint को सुनना है और मुझे यह बताना है कि जब भी ब्याज की कोई चीज चल रही हो ...

उस कथन को पढ़ने के बाद मैंने तुरंत "अभिनेताओं" के बारे में सोचा। अभिनेता वस्तुओं के समान हैं, सिवाय इसके कि उनके पास केवल एक ही इनपुट है जहां आप इसे संदेश (ऑब्जेक्ट के तरीकों को सीधे कॉल करने के बजाय) पास करते हैं और वे अतुल्यकालिक रूप से संचालित होते हैं। एक बहुत ही सरल उदाहरण में ... आप अभिनेता को बनाएंगे और उसे IPEndpoint और अभिनेता के पते पर संदेश भेजकर परिणाम भेजेंगे। यह बंद हो जाता है और यह पृष्ठभूमि में काम करता है। आप केवल तभी इसे सुनते हैं जब "रुचि का कुछ" होता है। आप जितने एक्टर्स को लोड करने के लिए जरूरत पड़ती है, उतने समय के लिए इंस्टेंट कर सकते हैं।

मैं। नेट में किसी भी एक्टर्स लाइब्रेरी से परिचित नहीं हूं, हालांकि मुझे पता है कि कुछ हैं। मैं टीपीएल डेटाफ्लो लाइब्रेरी से परिचित हूं (मेरी किताब http://DataflowBook.com में इसे कवर करने वाला एक सेक्शन होगा ) और उस लाइब्रेरी के साथ एक साधारण एक्टर मॉडल को लागू करना आसान होना चाहिए।


यह दिलचस्प लग रहा है। मैं यह देखने के लिए कि क्या यह मेरे लिए फिट है, मैं एक खिलौना परियोजना स्थापित करूँगा। सलाह के लिये धन्यवाद।
रॉबथ
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.