socket
Yıllar önce (Olay-temelli Eşzamansız Desen (EAP) modasıyla) bazı (daha fazla veya daha az) "düşük seviyeli" TcpListener
asenkron programlama yaptıktan ve son zamanlarda bir " Asenkron Programlama Modeli (APM) " ye ("Asenkron Programlama Modeli " ) geçtikten sonra Gitmeye çalışıyorum async/await
(Görev Tabanlı Asenkron Desen (TAP) ) Neredeyse tüm bu 'düşük seviyeli tesisat' ile uğraşmak zorunda kaldım. Ben de düşündüm; neden vermez RX
gitmek ( Reaktif Uzantıları benim sorunum etki alanına daha iyi bir şekilde oturması olabileceğinden).
Yazdığım kodun birçoğunun Tcp üzerinden uygulamama bağlanan birçok müşteriyle yapması gerekiyor, bu da daha sonra iki yönlü (zaman uyumsuz) bir iletişim başlatıyor. İstemci veya sunucu herhangi bir noktada bir mesajın gönderilmesi ve yapılması gerektiğine karar verebilir, bu nedenle bu sizin klasik request/response
kurulumunuz değil, her iki tarafa da istediklerini göndermeleri için açık olan gerçek zamanlı, iki yönlü bir "hat" dan daha fazlasıdır. , ne zaman isterlerse. (Bunu tanımlayacak iyi bir adı varsa, duyduğuma sevinirim!).
"Protokol" uygulama başına farklılık gösterir (ve sorumla gerçekten ilgili değil). Ancak başlangıçta bir sorum var:
- Yalnızca bir "sunucu" çalıştığı göz önüne alındığında, ancak her birinin (daha iyi bir tanımlamanın olmayışı için) kendi "devlet makinelerini" içerden takip eden birçok (genellikle binlerce) bağlantıyı takip etmesi gerekir. devletler vs. hangi yaklaşımı tercih ederdiniz? EAP / TAP / APM? RX bir seçenek olarak kabul edilir mi? Değilse neden?
Bu yüzden, Async 'den beri çalışmam gerekiyor a) bu bir istek / cevap protokolü değil, bu yüzden bir "mesaj bekliyor" veya "mesaj gönderme" - bloke aramada mesaj gönderemiyorum o müşteriyi engellemek için sadece onunla yaşayabilirim) ve b) birçok eşzamanlı bağlantıya ihtiyacım var. Engelli aramaları kullanarak bunu (güvenilir) yapmanın bir yolu göremiyorum.
Uygulamalarımın çoğu VoiP ile ilgili; FreeSwitch / OpenSIPS vb. uygulamalardan SIP cientlerinden veya PBX'den (ilgili) mesajlaşma olabilir. Ancak, en basit haliyle, birçok "sohbet" istemcisi ile uğraşmaya çalışan bir "sohbet" sunucusu hayal etmeye çalışabilirsiniz. Çoğu protokol metin tabanlıdır (ASCII).
Bu yüzden, yukarıda belirtilen tekniklerin birçok farklı permütasyonunu uyguladıktan sonra, basitçe başlatabileceğim bir nesne yaratarak çalışmamı basitleştirmek istiyorum IPEndpoint
, ilgilendiğim bir şey olduğunda ne olacağını dinleyeceğim ve bana söyleyeceğini söyle olayları kullanın, bu nedenle bazı EAP genellikle diğer iki teknikle karıştırılır). Sınıf, protokolü “anlamaya” çalışmakla uğraşmamalı; yalnızca gelen / giden dizeleri işlemelidir. Ve böylece, (sonunda) çalışmayı kolaylaştıracağını umduğum RX'e göz kulak olmak, sıfırdan yeni bir "keman" yarattım:
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?
}
}
Bu "keman" da, küçük bir uygulamaya özgü ayrıntı olduğunu biliyorum; Bu durumda FreeSwitch ESL ile çalışıyorum, bu yüzden "connect\n\n"
daha genel bir yaklaşıma yeniden yönlendirilirken kemandakiler kaldırılmalıdır.
Ayrıca, anonim yöntemleri Server sınıfındaki özel örnek yöntemlerine yeniden yansıtmam gerektiğinin de farkındayım; Sadece OnSomething
yöntem adları için hangi kuralların (örneğin, örneğin " ") kullanıldığından emin değilim ?
Bu benim temelim / başlangıç noktam / vakfım (biraz "ince ayar" gerektiriyor). Bununla ilgili bazı sorularım var:
- Yukarıdaki soruya bakın "1"
- Doğru yolda mıyım? Yoksa "tasarım" kararlarım haksız mı?
- Eşzamanlılık bilge: Bu binlerce müşteriyle başa çıkacak (gerçek mesajları bir kenara ayırma / kullanma)
- İstisnalar: İstemcilerde "yukarı" dan sunucuya ("RX-bilge"); iyi bir yol ne olurdu?
- Artık
ClientId
, istemcileri bir şekilde veya başka bir şekilde gösterdiğimi farz edersem, sunucu sınıfımdan (bunu kullanarak ) herhangi bir bağlı istemciyi alabilir ve yöntemleri doğrudan arayabilirim. Metodları Server sınıfı üzerinden de çağırabilirim (örneğin,Send(clientId, rawmessage)
metot (diğer yaklaşım hızlıca diğer tarafa bir mesaj almak için bir "kolaylık" metodu olur). - Buradan nereye (ve nasıl) gideceğimi bilemiyorum:
- a) Gelen mesajları ele almam gerekiyor; bunu nasıl ayarlarım? Ders akışını alabilirim, ancak alınan baytları almayı nerede ele alabilirim? Sanırım abone olabileceğim bir çeşit "ObservableStream" e ihtiyacım var? Bunu
FiddleClient
veya içine koyabilir miyimFiddleServer
? - b) Bu
FiddleClient
/FiddleServer
sınıfların, uygulamalara özel protokol işlemlerini vb. daha spesifikFooClient
/FooServer
sınıflar kullanarak uyarlamak için özel olarak uygulanıncaya kadar olayı kullanmaktan kaçınmak istediğimi varsayalım: temeldeki 'Fiddle' sınıflarındaki verileri elde etmekten nasıl başarabilirim daha spesifik meslektaşları?
- a) Gelen mesajları ele almam gerekiyor; bunu nasıl ayarlarım? Ders akışını alabilirim, ancak alınan baytları almayı nerede ele alabilirim? Sanırım abone olabileceğim bir çeşit "ObservableStream" e ihtiyacım var? Bunu
Makaleler / bağlantılar zaten okudum / eksik / referans için kullandım:
- Reactive Extension kullanarak bir Soket kullanın
- Basit Gözlemlenebilir Dizilere Yaratma ve Abone Olma
- Her ObservableTcpListener Connection / Session'a içerik ekleme veya ilişkilendirme
- Reaktif Çerçeve ve Görev Paralel Kütüphane ile Asenkron Programlama - Bölüm 1 , 2 ve 3 .
- Soket programlama için Reaktif Uzantıların (Rx) kullanımı pratikte mi?
- Bir .NET Rx Driven Web Sunucusu ve .NET Rx Driven Web Sunucusu, 2
- Bazı Rx dersleri