कुछ साल (अधिक-या-कम) "निम्न-स्तर" की async socket
प्रोग्रामिंग करने के बाद सालों पहले (एक इवेंट-आधारित एसिंक्रोनस पैटर्न (EAP) फैशन में) और हाल ही में एक TcpListener
अतुल्यकालिक प्रोग्रामिंग मॉडल (APM) पर "ऊपर" जा रहा है और फिर async/await
(टास्क-आधारित एसिंक्रोनस पैटर्न (टीएपी) ) को स्थानांतरित करने की कोशिश कर रहा हूं, मुझे बहुत खुशी हुई है कि यह सब 'निम्न स्तर की पाइपलाइन' से परेशान है। तो मुझे लगा था; क्यों नहीं RX
एक जाने के बाद ( प्रतिक्रियात्मक एक्सटेंशन ) क्योंकि यह मेरी समस्या डोमेन के लिए अधिक snugly फिट हो सकता है।
बहुत सारे कोड जो मैंने लिखे हैं, उनमें कई क्लाइंट्स के साथ Tcp को मेरे एप्लिकेशन से कनेक्ट करना है जो तब टू-वे (async) कम्युनिकेशन शुरू करते हैं। क्लाइंट या सर्वर किसी भी बिंदु पर तय कर सकते हैं कि एक संदेश भेजने और ऐसा करने की आवश्यकता है, इसलिए यह आपका क्लासिक request/response
सेटअप नहीं है, बल्कि वास्तविक समय, दो-तरफ़ा, "लाइन" दोनों पक्षों के लिए खुला है जो वे चाहते हैं भेजने के लिए , जब चाहें। (यदि किसी के पास इसका वर्णन करने के लिए एक सभ्य नाम है तो मुझे यह सुनकर खुशी होगी!)।
"प्रोटोकॉल" प्रति एप्लिकेशन में भिन्न होता है (और वास्तव में मेरे प्रश्न के लिए प्रासंगिक नहीं है)। हालाँकि, मेरा प्रारंभिक प्रश्न है:
- यह देखते हुए कि केवल एक "सर्वर" चल रहा है, लेकिन इसे कई (आमतौर पर हजारों) कनेक्शनों का ट्रैक रखना पड़ता है (उदाहरण के लिए क्लाइंट) प्रत्येक के पास (बेहतर विवरण की कमी के लिए) अपने स्वयं के "स्टेट मशीन" को उनके आंतरिक का ट्रैक रखने के लिए राज्यों आदि, जो दृष्टिकोण आप पसंद करेंगे? ईएपी / नल / एपीएम? क्या आरएक्स को भी एक विकल्प माना जाएगा? यदि नहीं, तो क्यों?
इसलिए, मुझे 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" देखें
- क्या मैं सही रास्ते पर हूं? या मेरे "डिजाइन" निर्णय अन्यायपूर्ण हैं?
- Concurrency-wise: यह हजारों ग्राहकों के साथ सामना करेगा (वास्तविक संदेशों को एक तरफ पार्स / संभालना)
- अपवादों पर: मुझे यकीन नहीं है कि ग्राहकों को "सर्वर" ("आरएक्स-वार") के भीतर उठाए गए अपवादों को कैसे प्राप्त किया जाए; क्या एक अच्छा तरीका होगा?
- अब मैं अपने सर्वर वर्ग (इसका उपयोग करके
ClientId
) से कोई भी जुड़ा हुआ क्लाइंट प्राप्त कर सकता हूं , यह मानते हुए कि मैं ग्राहकों को एक या दूसरे तरीके से उजागर करता हूं, और सीधे उन पर कॉल के तरीके। मैं सर्वर वर्ग के माध्यम से भी तरीकों को कॉल कर सकता हूं (उदाहरण के लिए,Send(clientId, rawmessage)
विधि (जबकि बाद वाला दृष्टिकोण दूसरी तरफ एक संदेश प्राप्त करने के लिए "सुविधा" विधि होगी)। - मुझे यकीन नहीं है कि यहाँ से कहाँ (और कैसे) जाऊंगा:
- क) मुझे आने वाले संदेशों को संभालने की जरूरत है; मैं इसे कैसे सेट करूंगा? मैं धारा बिल्कुल प्राप्त कर सकते हैं, लेकिन जहां मैं प्राप्त बाइट्स पुन: प्राप्त करने संभाल होगा? मुझे लगता है कि मुझे किसी प्रकार के "ऑब्जर्वेबलस्ट्रीम" की आवश्यकता है, जो कि मैं सदस्यता ले सकता हूं? क्या मैं इसे डालूंगा
FiddleClient
याFiddleServer
? - ख) यह मानते हुए कि मैं इवेंट का उपयोग करने से बचना चाहता हूं जब तक कि इन
FiddleClient
/FiddleServer
वर्गों को विशेष रूप से अपने एप्लिकेशन विशिष्ट प्रोटोकॉल हैंडलिंग आदि को अधिक विशिष्टFooClient
/FooServer
कक्षाओं का उपयोग करने के लिए कार्यान्वित नहीं किया जाता है : मैं अंतर्निहित 'फिडेल-क्लास' में डेटा प्राप्त करने से उनके पास कैसे जाऊंगा अधिक विशिष्ट समकक्षों?
- क) मुझे आने वाले संदेशों को संभालने की जरूरत है; मैं इसे कैसे सेट करूंगा? मैं धारा बिल्कुल प्राप्त कर सकते हैं, लेकिन जहां मैं प्राप्त बाइट्स पुन: प्राप्त करने संभाल होगा? मुझे लगता है कि मुझे किसी प्रकार के "ऑब्जर्वेबलस्ट्रीम" की आवश्यकता है, जो कि मैं सदस्यता ले सकता हूं? क्या मैं इसे डालूंगा
लेख / लिंक मैं पहले से ही पढ़ा / स्किम्ड / संदर्भ के लिए इस्तेमाल किया:
- रिएक्टिव एक्सटेंशन का उपयोग करके एक सॉकेट का उपभोग करें
- सिंपल ऑब्जर्वेबल सीक्वेंस बनाना और सब्सक्राइब करना
- प्रत्येक ObservableTcpListener कनेक्शन / सत्र में संदर्भ कैसे जोड़ें या संबद्ध करें
- अतुल्यकालिक प्रोग्रामिंग रिएक्टिव फ्रेमवर्क और टास्क पैरेलल लाइब्रेरी के साथ - भाग 1 , 2 और 3 ।
- सॉकेट प्रोग्रामिंग के लिए रिएक्टिव एक्सटेंशन्स (आरएक्स) का उपयोग करना व्यावहारिक है?
- एक .NET Rx संचालित वेब सर्वर और .NET Rx संचालित वेब सर्वर, 2 ले लो
- कुछ आरएक्स ट्यूटोरियल