Reaktif Eklentiler kullanarak zaman uyumsuz ağ programlama


25

socketYıllar önce (Olay-temelli Eşzamansız Desen (EAP) modasıyla) bazı (daha fazla veya daha az) "düşük seviyeli" TcpListenerasenkron 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 RXgitmek ( 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/responsekurulumunuz 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:

  1. 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 OnSomethingyö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:

  1. Yukarıdaki soruya bakın "1"
  2. Doğru yolda mıyım? Yoksa "tasarım" kararlarım haksız mı?
  3. Eşzamanlılık bilge: Bu binlerce müşteriyle başa çıkacak (gerçek mesajları bir kenara ayırma / kullanma)
  4. İstisnalar: İstemcilerde "yukarı" dan sunucuya ("RX-bilge"); iyi bir yol ne olurdu?
  5. 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).
  6. 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 FiddleClientveya içine koyabilir miyim FiddleServer?
    • b) Bu FiddleClient/ FiddleServersınıfların, uygulamalara özel protokol işlemlerini vb. daha spesifik FooClient/ FooServersı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ı?

Makaleler / bağlantılar zaten okudum / eksik / referans için kullandım:


Mevcut ReactiveSockets kütüphanesine bir göz atın
Flagbug

2
Kütüphaneler veya linkler aramıyorum (referans için takdir edilmelerine rağmen), ancak sorularıma ve genel kurulumuma girdi / tavsiye / yardım almak için. Kendi kodumu geliştirmeyi öğrenmek ve bunu hangi yöne çekeceğime karar verebilmek, profesyonelleri ve eksileri tartmak vb. Hakkında daha fazla bilgi edinmek istiyorum. Bazı kütüphanelere atıfta bulunmamak, onu bırakmak ve devam etmek. Bu deneyimden ders almak ve Rx / network programlama ile daha fazla tecrübe edinmek istiyorum.
RobIII

Elbette, ancak kütüphane açık kaynak olduğundan, orada nasıl uygulandığını görebilirsiniz
Flagbug

1
Elbette, ancak kaynak koduna bakmak, bazı tasarım kararlarının neden verildiğini / alınmadığını açıklamıyor . Ve Ağ programlama ile birlikte Rx için nispeten yeni olduğum için, bu kütüphanenin iyi olup olmadığını, tasarımın mantıklı olup olmadığını, doğru kararların verilip verilmediğini ve benim için doğru olsa bile söyleyecek kadar deneyimim yok.
RobIII

Bir el sıkışmasının aktif üyesi olarak sunucuyu yeniden düşünmenin iyi olacağını düşünüyorum, bu yüzden bu bağlantılar için dinleyiciler yerine bağlantıyı başlatır. Örneğin, burada: codeproject.com/Articles/20250/Reverse-Connection-Shell

Yanıtlar:


1

... Çalışmamı basitleştirmek için basitçe örnekleyebileceğim bir nesne oluşturarak basitleştirmek istiyorum, hangi IPEndpoint'in dinleyeceğini ve ne zaman ilgi çekici bir şey olursa bana söylemesini isteyin ...

Bu ifadeyi okuduktan sonra hemen "aktörler" diye düşündüm. Aktörler, yalnızca iletiyi ilettiğiniz yerde (nesnenin yöntemlerini doğrudan çağırmak yerine) yalnızca tek bir girişe sahip olmaları dışında nesnelere çok benzer ve eşzamansız olarak çalışırlar. Çok basitleştirilmiş bir örnekte, oyuncuyu yaratır ve sonucu IPEndpoint ve sonucun gönderileceği adresin adresini içeren bir mesaj gönderirsiniz. Söner ve arka planda çalışır. Ondan yalnızca "ilgilenilen bir şey" olduğunda haber alırsınız. Yükü idare etmek için ihtiyaç duyduğunuz kadar oyuncu başlatabilirsiniz.

Bazıları olduğunu bilmeme rağmen, Net'teki hiçbir aktör kütüphanesine aşina değilim. TPL Dataflow kitaplığına aşina oldum ( http://DataflowBook.com kitabımda kapsayan bir bölüm olacak ) ve bu kitaplıkla basit bir aktör modelini uygulamak kolay olmalı.


Bu ilginç görünüyor. Bana uygun olup olmadığını görmek için bir oyuncak projesi hazırlayacağım. Önerin için teşekkürler.
RobIII
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.