Duyuruyu Kapat
Facebook Gözat
Twitter Gözat

ddili ile socket okuma/veri gönderme ve "non-blocking i/o"

Konu, 'D Dili' kısmında netwalker tarafından paylaşıldı.

  1. netwalker

    netwalker Üye

    Kayıt:
    20 Kasım 2011
    Mesajlar:
    60
    Beğenilen Mesajlar:
    0
    Atakan, Anlayamadım hangi çözüme karar kıldın dan kasdettiğiniz nedir ?

    acehreli, Biraz araştırmam okumam sonucu bu tarz uygulama geliştirmek D dili için rahat uygun olsada C dili bu konuda daha üstün.
    Fark ettim ki D ile yazılan uygulama çok yüksek boyutlarda oluyor mesala yaptığınız uygulamanın derlenmiş hali 1.3 mb bu durum çok ilginç doğrusu. C ile yazılmış koca Nginx derlenmiş hali 841 kb. Sanırım D derleyicisinde optimizasyon sorunu var. Bir de sizden ricam konunun başlığını D ile non-blocking i/o olarak değiştirebilirmisiniz çok güzel konular yazılar oluştu en azından herkes okusun bilgilensin başlık daha doğru olsun diye düşünüyorum.
     
  2. Atakan Erbaþ

    Atakan Erbaþ Aktif Üye

    Kayıt:
    11 Mayıs 2012
    Mesajlar:
    459
    Beğenilen Mesajlar:
    0
    Şehir:
    Bursa
    Evet C'de boyut açısından sorun yaşamazsın. Ama 1 MB da çok büyük değil değil mi?
    Tamponla mı yapmak istiyorsun? Multithreading mi yapacaksın, yoksa event driven mı? Yoksa karışımını mı?
     
  3. acehreli

    acehreli Ali Çehreli

    Kayıt:
    19 Ekim 2002
    Mesajlar:
    4,973
    Beğenilen Mesajlar:
    2
    netwalker, lütfen yanıt yazdığın mesajı bütünüyle alıntılama. Bunu kolaylaştıran bir öneri: Hiçbir zaman "Alıntı Yaparak Cevapla"ya tıklama; onun yerine sayfanın en altındaki "Hızlı Cevap" pencerisini kullan. Teşekkürler...

    Ali
     
  4. netwalker

    netwalker Üye

    Kayıt:
    20 Kasım 2011
    Mesajlar:
    60
    Beğenilen Mesajlar:
    0
    Atakan, Tam emin olamıyorum hangisinin daha uygun olacağına C tarafında bazı multi-thread örneklerini denedim multi-thread kafama yatıyor. Cache yapısı kurmayı düşünüyorum gelen sayfayı hafızaya alıp herhangi bir güncelleme yoksa direk hafızadan okumayı düşünüyorum ama buda mantıksız olur veritabanı işlemleri oldukça sayfalar sürekli güncellenir anlamı olmaz açıkcası kafam karıştı hafta sonu üzerinde güzelce çalışmayı planlıyorum.

    Peki Atakan sen ne önerirsin ne yapmalıyım ?

    acehreli, Özür dilerim ancak ne yaptığımı tam olarak anlayamadım ? son mesajım da herhangi bir alıntılama yok ?
     
  5. Atakan Erbaþ

    Atakan Erbaþ Aktif Üye

    Kayıt:
    11 Mayıs 2012
    Mesajlar:
    459
    Beğenilen Mesajlar:
    0
    Şehir:
    Bursa
    Dediğim gibi multithreading çok istemci olduğunda berbattır, select tarzı yöntemler daha kullanışlıdır. Önbellek yapısı kurabilirsin problem olmaz. İstersen yarın yapman gerekenleri yazayım, ama önce sen "tam olarak" ne yapmak istediğini yazarsan daha iyi olur. "Gelen isteklere cevap vermek istiyorum." çok genel bir tanım.
     
  6. netwalker

    netwalker Üye

    Kayıt:
    20 Kasım 2011
    Mesajlar:
    60
    Beğenilen Mesajlar:
    0
    Atakan, evet gelen isteklere yanıt vermek istiyorum ancak non-blocking bir yapı olsun ve performans olsun istiyorum çoklu port desteği ile kolay ölçeklenebilirlik olsun istiyorum mesala python tarafında örneği mevcuttur Tornado diye bir framework vardır.

    Bknz: www.tornadoweb.org
     
  7. acehreli

    acehreli Ali Çehreli

    Kayıt:
    19 Ekim 2002
    Mesajlar:
    4,973
    Beğenilen Mesajlar:
    2
    Gözümle görsem inanmam! :p C'de kurucu (constructor) ve sonlandırıcı (destructor) olanağı bile yok. Böyle düşünen arkadaşların biraz olsun karmaşık bir C projesi geliştirmelerini öneririm. Çalıştığım yerdeki kütüphanelerin çoğunluğu C ile yazılmış. C kullanmanın ne büyük bir eziyet olduğunu anlatamam!

    Örnek: Diyelim, belirli bir yapının nesnelerinden oluşan bir topluluk oluşturacaksınız. Hash table olur, ikili ağaç olur, vs. Haydi kolay gelsin! :p

    D'nin çalışma zamanını (D runtime) C'ninkiyle karşılaştıramayız. Aralarında dağlar kadar yetenek farkı var. Ayrıca, program boyutu D'nin değil, belirli bir D derleyicisinin bir özelliğidir.

    dmd ile oluşturulan program öyle göründü diye dilin kendisini kötüleyemeyiz, değil mi?

    Hepsi o kadar mı yoksa çalışma zamanında başka kütüphanelerle bağlanıyor mu?

    Doğrudur. Hız veya boyut gerçekten sorun oluşturduğunda başka D derleyicilerine bakılabilir: En çok bilinenler gdc ve ldc ama geliştirilmekte olan başkaları var.

    Ali
     
  8. Atakan Erbaþ

    Atakan Erbaþ Aktif Üye

    Kayıt:
    11 Mayıs 2012
    Mesajlar:
    459
    Beğenilen Mesajlar:
    0
    Şehir:
    Bursa
    Ali Bey aslında C++ kullanılarak işler kolaylaştırılabilir.
     
  9. acehreli

    acehreli Ali Çehreli

    Kayıt:
    19 Ekim 2002
    Mesajlar:
    4,973
    Beğenilen Mesajlar:
    2
    Biz de aynen öyle yapıyoruz zaten! :) Öncelikle, arka plandaki C kütüphanelerini C++ sınıfları ve işlevleri ile sunuyoruz ve herşey öylece kolaylaşmış oluyor.

    Bazen C kütüphanesine ek yapmak gerekebiliyor: Yeni bir struct tanımlanacak, bu struct'ın nesneleri bir süre bir toplulukta barındırılacaklar. C'de yazılmış olan hash table ve ağaç yapılarımız olmasına rağmen bunlar yeni struct türünün nasıl kopyalandığını, topluluğun onun sahibi olup olmadığı, vs. gibi her ayrıntıyı da bilmek istiyorlar.

    Bütün o külfete girmektense arka plandaki C kütüphanesinden C++ kütüphanesi çağırıyoruz; o kütüphane C'ye 'opaque pointer' arkasına gizlenmiş olan set<BenimYapim>, vector<SeninYapin> vs. gibi topluluklar veriyor.

    Ali
     
  10. Atakan Erbaþ

    Atakan Erbaþ Aktif Üye

    Kayıt:
    11 Mayıs 2012
    Mesajlar:
    459
    Beğenilen Mesajlar:
    0
    Şehir:
    Bursa
    Büyük projelerde C'ye dayanılmıyor.
    Soketler için çok güzel bir sınıf yazılabilir. D'de pek gerek var gibi durmuyor ama C++'nın olanaklarından faydalanmak lazım :)
    netwalker D aslında kötü bir dil gibi durmuyor. Kararını değiştirme bence.
     
  11. netwalker

    netwalker Üye

    Kayıt:
    20 Kasım 2011
    Mesajlar:
    60
    Beğenilen Mesajlar:
    0
    Beni yanlış anladınız ben D kötü dil demedim kullanmıyacağım da demedim sadece okuduğum kadarı ile fikrimi söyledim ki zaten optimizasyon konusunda acehreli bana hak verdi.

    D dilinde devam edeceğim geliştirmeye çünkü sırf merakımdan C ile nasıl yapılacağına filan baktığımda karma karışık olduğunu gördüm ancak şu göz ardı edilemez ki C dili artık bir standart ve işletim sistemleriyle bütünleşik.
     
  12. Atakan Erbaþ

    Atakan Erbaþ Aktif Üye

    Kayıt:
    11 Mayıs 2012
    Mesajlar:
    459
    Beğenilen Mesajlar:
    0
    Şehir:
    Bursa
    İstemcilerle ilgili not tutman gerekiyor mu? Yani istemcinin bir isteği sonraki istekleri etkiliyor mu?
     
  13. netwalker

    netwalker Üye

    Kayıt:
    20 Kasım 2011
    Mesajlar:
    60
    Beğenilen Mesajlar:
    0
    Aslında etkilemesi lazım diye düşünüyorum :rolleyes:
     
  14. Atakan Erbaþ

    Atakan Erbaþ Aktif Üye

    Kayıt:
    11 Mayıs 2012
    Mesajlar:
    459
    Beğenilen Mesajlar:
    0
    Şehir:
    Bursa
    O halde nelerin etkileyeceğini bilmeli ve buna göre bir yapı oluşturmalısın. Her işlemde soketin içinde bulunduğu yapıyı bulman ve işlemi ona göre yapman lazım. D'de yapı nasıl tanımlanıyor bilmiyorum.
    Bu arada mesajım geldi mi?
     
  15. acehreli

    acehreli Ali Çehreli

    Kayıt:
    19 Ekim 2002
    Mesajlar:
    4,973
    Beğenilen Mesajlar:
    2
    Yapılar D'de kavram ve olanak olarak C ile C++ arasında bir yerdeler. Örneğin, C++'ın aksine, türemeyi ve çokşekilliği desteklemiyorlar.

    Kitabın içindekiler:

    http://ddili.org/ders/d/

    Özellikle aşağıdaki bölümler yapıların olanakları ile ilgili:

    * Yapılar

    * Üye İşlevler

    * const ref Parametreler ve const Üye İşlevler

    * Kurucu ve Diğer Özel İşlevler

    * İşleç Yükleme

    * Sarma ve Erişim Hakları

    * İşlev Çağırma Ortak Söz Dizimi (UFCS)

    * Nitelikler

    * Yapı ve Sınıflarda Sözleşmeli Programlama

    * alias this

    * Yapı ve Sınıflarda foreach

    Ama söz dizimi çok tanıdıktır:
    Kod:
    struct Yapı
    {
        int i;
        string s;
        double d;
    }
    Ali

    --- Ekleme ---

    Birisi basit bir web sunucusu yazdığını duyurdu:

    https://github.com/gedaiu/DSWS

    Şöyle bir kullanım örneği de var:

    https://github.com/gedaiu/DSWS/blob/master/src/example/main.d

    Bizim konuştuğumuz soket işlemlerinden benzer biçimde yararlanmış:

    https://github.com/gedaiu/DSWS/blob/master/src/simpleWebServer/WebServer.d

    Ali
     
  16. acehreli

    acehreli Ali Çehreli

    Kayıt:
    19 Ekim 2002
    Mesajlar:
    4,973
    Beğenilen Mesajlar:
    2
    Buradaki örneklerde hep TcpSocket ve port üzerinden haberleşen InternetAddress kullandık. Eğer Nginx ile bizim sunucu aynı bilgisayar üzerinde olacaksa ve Unix altında isek, bu yöntemlerin yavaş olduklarını öğrendim. Onun yerine, Unix domain sockets kullanmak akıllıca olurmuş çünkü o zaman programlar arasındaki iletişim kernel üzerinden hızlıca gerçekleştiriliyormuş.

    Böyle soketlerin SOCK_STREAM, SOCK_SEQPACKET, veya SOCK_DGRAM olarak oluşturulmaları gerekiyormuş ancak Phobos bu konuda hatalıymış. Hata yakında çıkacak olan 2.064'te giderilmiş.

    Ali
     
  17. netwalker

    netwalker Üye

    Kayıt:
    20 Kasım 2011
    Mesajlar:
    60
    Beğenilen Mesajlar:
    0
    Peki ne yapmamı önerirsin acehreli, çünkü yaptığım sistemi unix üzerinde kullanacağım.
     
  18. Atakan Erbaþ

    Atakan Erbaþ Aktif Üye

    Kayıt:
    11 Mayıs 2012
    Mesajlar:
    459
    Beğenilen Mesajlar:
    0
    Şehir:
    Bursa
    C'de bu soketleri socket fonksiyonuyla oluşturabilirsin.
    acehreli, tcpsocket'in kullanımı oldukça kolaymış, aynı şeyler o dediğiniz türler için de geçerli mi?
     
  19. acehreli

    acehreli Ali Çehreli

    Kayıt:
    19 Ekim 2002
    Mesajlar:
    4,973
    Beğenilen Mesajlar:
    2
    netwalker, şimdilik burada konuştuğumuz yöntemleri kullanabilirsin. dmd 2.064 çıktığında eğer domain socket ile ilgili olan hata gerçekten giderilmişse hemen hemen hiçbir değişiklik yapman gerekmeyecektir. (Nginx'in de domain socket'i desteklediğini varsayıyorum ama bunun gerekip gerekmediğinden bile emin değilim.)

    Atakan Erbaş, evet, InternetAddress yerine UnixAddress kullanmak kadar basit olmalı ama 2.064 çıkınca deneyebileceğiz.

    Ali
     
  20. acehreli

    acehreli Ali Çehreli

    Kayıt:
    19 Ekim 2002
    Mesajlar:
    4,973
    Beğenilen Mesajlar:
    2
    2.064'ü beklemek yerine github'daki projeleri derleyerek denedim. dmd, druntime, ve phobos'un en son durumlarını denemek isteyenler için üçünü birleştiren bir proje var: GitHub'da carlor'un dlang-workspace adlı projesi. Ben onu kullanıyorum.

    Aşağıda Unix domain socket üzerinden haberleşen bir program var. Kendi geliştirme sürecim kolay olsun diye aynı program hem sunucu hem istemci olarak işleyebiliyor:

    Kod:
    import std.stdio;
    import std.socket;
    import std.getopt;
    import std.string;
    import std.range;
    import std.concurrency;
    import core.thread;
    
    /* 'Unix doman socket'ler dosya sisteminde dosya olarak
     * beliriyorlarmış. Anladığım kadarıyla kullanıcının bu dosyayla doğrudan bir
     * işi olmuyor.
     *
     * Dosya isminin ilk karakterinin '\0' olması da en azından dosya isim
     * çakışmalarını önlüyormuş. Hatta, böyle isimdeki dosyaların remove() ile
     * silinmeleri de gerekmiyormuş.
     */
    enum soketİsmi = "\0/tmp/deneme_soketi";
    
    int main(string[] parametreler)
    {
        // Sunucu rolünde mi olalım istemci mi
        enum Rol { sunucu, istemci }
        Rol rol;
    
        try {
            // Programın ismi dahil, iki parametre olmalı
            enforce(parametreler.length == 2);
    
            // Rol olamayacak değer geldiğinde hata atar
            getopt(parametreler, "rol", &rol);
    
        } catch {
            stderr.writefln("Kullanım:\n    %s --rol={sunucu|istemci}",
                            parametreler[0]);
            return 1;
        }
    
        final switch (rol) {
        case Rol.sunucu:  sunucuOl(soketİsmi); break;
        case Rol.istemci: istemciOl(soketİsmi); break;
        }
    
        return 0;
    }
    
    void sunucuOl(string soketİsmi)
    {
        auto sunucu = Sunucu(soketİsmi);
        sunucu.hizmetEt();
    }
    
    void istemciOl(string soketİsmi)
    {
        auto istemci = İstemci(soketİsmi);
        istemci.iste();
    }
    
    /* Bu sunucu her isteğe karşılık farklı bir işçi başlatır, istemcinin
     * bağlantısını ona devreder ve hiç vakit geçirmeden yeni istemci beklemeye
     * başlar.
     *
     * Bu tasarımda işçiler spawn ile başlatılıyorlar ve hemen
     * unutuluyorlar. Dolayısıyla, istemcinin yanıtını göndermek bütünüyle işçinin
     * görevi oluyor.
     */
    struct Sunucu
    {
        Socket dinleyici;
    
        this(string soketİsmi)
        {
            // Önce bağlantıları karşılayacak olan soketi hazırlıyoruz
            dinleyici = new Socket(AddressFamily.UNIX, SocketType.STREAM);
            auto adres = new UnixAddress(soketİsmi);
    
            dinleyici.bind(adres);
            dinleyici.listen(1);
        }
    
        ~this()
        {
            dinleyici.shutdown(SocketShutdown.BOTH);
            dinleyici.close();
            destroy(dinleyici);
            // Dosyanın silinmesinin gerekip gerekmediğinden aslında emin değilim:
            remove(soketİsmi.toStringz);
        }
    
        /* Sonsuz döngüde istemci bekler ve hiç zaman geçirmeden onun isteğini
         * yerine getirecek olan bir işçi başlatır. */
        void hizmetEt()
        {
            while (true) {
                // İstemci bekliyoruz
                writeln("Bekliyorum...");
    
                // Şimdi o sokette bağlantı kabul ediyoruz. Bu Socket nesnesinin
                // sahipliğini sunucuİşlev devralacak.
                Socket istemci = dinleyici.accept();
    
                writefln("İstemci bağlandı: %s", istemci.remoteAddress());
    
                /* Eş zamanlı işleyecek olan bir işçi başlatıyoruz ve hemen tekrar
                 * beklemeye dönüyoruz. Burada işçi sayısında hiçbir kısıtlama
                 * getirilmiyor. İstendiğinde başka tasarımlar da
                 * düşünülebilir. */
                auto işçi = spawn(&sunucuİşçi, cast(shared)istemci);
            }
        }
    }
    
    /*
     * İstemcilere eş zamanlı olarak hizmet eden işlev. Parametre olarak bir
     * istemci alır ve ona hizmet eder. Bu tasarımda kendisini başlatan ana iş
     * parçacığı ile hiçbir iletişimi olmuyor. Yanıtını gönderdikten sonra hemen
     * sonlanıyor.
     */
    void sunucuİşçi(shared(Socket) istemci_)
    {
        /* Bu tasarımda istemcinin sorumluluğu bu işleve ait; çıkarken
         * sonlandıralım. */
        scope (exit) {
            destroy(istemci_);
        }
    
        /* std.concurrency modülü henüz shared ile tam uyumlu olarak
         * çalışmıyor. Şimdilik en iyisi parametreyi yukarıda yaptığımız gibi
         * shared olarak almak ve işlev içinde kullanmadan önce tür dönüşümü ile
         * shared'i kaldırmak: */
        auto istemci = cast()istemci_;
    
        string istek = istemci.tekParçaOlarakOku();
    
        enum saniye = 2;
        writefln("İsteği aldım. Yapay olarak %s saniye bekleyeceğim", saniye);
    
        foreach (i; saniye.iota) {
            Thread.sleep(1.seconds);
            writef(" .");
            stdout.flush();
        }
        writeln();
    
        enum başlık =
            "HTTP/1.0 200 OK\nContent-Type: text/html; charset=utf-8\n\n";
    
        string yanıt = başlık;
        yanıt ~= "<b>Merhaba Dünya!</b>\n";
    
        istemci.send(yanıt);
    }
    
    /* Socket.receive'in aksine, okunan parçaları birleştirir ve tek string olarak
     * döndürür. */
    string tekParçaOlarakOku(Socket soket)
    {
        string okunan;
    
        // Okunan veriyi parça parça bu belleğe alacağız
        ubyte[1000] parça;
        bool bütünVeriOkundu_mu = false;
    
        while (!bütünVeriOkundu_mu) {
            const adet = soket.receive(parça);
    
            if (adet == Socket.ERROR) {
                stderr.writeln("OKUMA HATASI");
                break;
            }
    
            okunan ~= parça[0..adet];
    
            bütünVeriOkundu_mu = (adet < parça.length);
        }
    
        return okunan;
    }
    
    struct İstemci
    {
        Socket soket;
    
        this(string soketİsmi)
        {
            soket = new Socket(AddressFamily.UNIX, SocketType.STREAM);
            soket.connect(new UnixAddress(soketİsmi));
        }
    
        ~this()
        {
            soket.shutdown(SocketShutdown.BOTH);
            soket.close();
            destroy(soket);
        }
    
        void iste()
        {
            string mesaj = format("%s%s%s", "merhaba", 42, "dünya");
            const adet = soket.send(mesaj);
            writefln("%s bayt gönderdim", adet);
    
            string yanıt = soket.tekParçaOlarakOku();
            writefln("Yanıt aldım: %s", yanıt);
        }
    }
    
    Ali