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
    Merhaba arkadaşlar,

    D diline yeni başlayan biriyim yapmak istediğim bir şey var. Yapmak istediğim belirlediğim portu okumak gelen isteğe göre veriyi hazırlayıp cliente göndermek yani browser'a göndermek. Bir de get bilgilerini okumak istiyorum bunu nasıl yapabilirim.

    Bu konuda yardımlarınızı bekliyorum teşekkür ederim.
     
  2. acehreli

    acehreli Ali Çehreli

    Kayıt:
    19 Ekim 2002
    Mesajlar:
    4,973
    Beğenilen Mesajlar:
    2
    Aynı bilgisayar üzerinde hem sunucu hem istemci rolünde işleyebilen şöyle bir örnek var:

    Kod:
    import std.stdio;
    import std.socket;
    import std.getopt;
    import std.string;
    
    enum port = 8080;
    
    int main(string[] parametreler)
    {
        // Sunucu rolünde mi olalım istemci mi
        enum Rol { sunucu, istemci }
        Rol rol;
    
        try {
            // 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:  sunucu(); break;
        case Rol.istemci: istemci(); break;
        }
    
        return 0;
    }
    
    void sunucu()
    {
        // Önce bağlantıları karşılayacak olan soketi hazırlıyoruz
        auto dinleyici = new TcpSocket();
        scope (exit) {
            /* Gereken temizlik işlemleri
             *
             * Not: Tabii ki alt düzeyler kavramlar olan soketlerle böyle uğraşmak
             * yerine soketi sarmalayan bir Soket yapısı tanımlayabiliriz ve bu
             * işlemleri o türün sonlandırıcısında yapabiliriz. */
    
            writefln("Yerel %s kapatılıyor", dinleyici.localAddress());
    
            // BOTH: "hem okuma hem yazma" anlamında
            dinleyici.shutdown(SocketShutdown.BOTH);
            dinleyici.close();
        }
    
        // Hangi portu dinleyeceğini belirtiyoruz
        dinleyici.bind(new InternetAddress(port));
        writefln("Port: %s", port);
    
        // İstemci bekliyoruz
        dinleyici.listen(1);
        writefln("Bekliyorum...");
    
        // Şimdi o sokette bağlantı kabul ediyoruz
        Socket bağlantı = dinleyici.accept();
        scope (exit) {
            writefln("İstemci bağlantısı %s kapatılıyor", bağlantı.remoteAddress());
            bağlantı.shutdown(SocketShutdown.BOTH);
            bağlantı.close();
        }
        writefln("İstemci bağlandı: %s", bağlantı.remoteAddress());
    
        // Okunan veriyi bu belleğe alacağız
        ubyte[1000] bellek;
        bool bitti_mi = false;
    
        while (!bitti_mi) {
            const adet = bağlantı.receive(bellek);
    
            if (adet == Socket.ERROR) {
                writefln("OKUMA HATASI");
    
            } else {
                writefln("%s bayt aldım: %s", adet, bellek[0..adet]);
                // Okunan verinin ne anlama geldiği bütünüyle sunucu ve istemci
                // arasındaki protokole bağlıdır.
            }
    
            bitti_mi = (adet < bellek.length);
        }
    }
    
    void istemci()
    {
        auto bağlantı = new TcpSocket();
        bağlantı.connect(new InternetAddress("127.0.0.1", port));
        scope (exit) {
            bağlantı.shutdown(SocketShutdown.BOTH);
            bağlantı.close();
        }
    
        // Bu örnekte mesajı string olarak göndereceğiz. Aslında send() parametre
        // olarak const(void)[] alır.
        string mesaj = format("%s%s%s", "merhaba", 42, "dünya");
        const adet = bağlantı.send(mesaj);
        writefln("%s bayt gönderdim", adet);
    }
    Yukarıdaki programı şuradan aldım:

    http://ddili.org/forum/thread/1148

    Ali
     
  3. netwalker

    netwalker Üye

    Kayıt:
    20 Kasım 2011
    Mesajlar:
    60
    Beğenilen Mesajlar:
    0
    İlginiz için teşekkür ederim ancak yapmak istediğim şöyle bir şey.

    Öncelikle D ile basit merhaba dünya yapalım.

    Kod:
    import std.stdio;
    
    void main()
    {
        writeln("Merhaba dünya!");
    }
    şimdi bu derleyip çalıştırdığımızda ekrana "Merhaba Dünya" yazacaktır. Buraya kadar her şey tamam. Ben tarayıcıyı açıp "localhost:8080" yazdığım zaman burdaki "Merhaba Dünya!" yazısını browser tarafındada görmek istiyorum.

    Planım şu şekilde. Ön tarafa Nginx koyup gelen sorgulara göre /netwalker.nw yazınca Nginx isteği 8080 portuna proxy_pass edecektir. Ben sadece datayı göndermek istiyorum. Statik dosyaları yani resim,css,javascript dosyalarını Nginx okuyup gereken işlemi nginx yapacaktır.
     
  4. acehreli

    acehreli Ali Çehreli

    Kayıt:
    19 Ekim 2002
    Mesajlar:
    4,973
    Beğenilen Mesajlar:
    2
    Yani 8080'de nginx bekliyor. Bazı işlemleri bizim programa aktaracak.

    Eğer nginx zaten oradaysa bizim program 8080'de olamaz, değil mi? Bizim programın 8081'de olduğunu varsayıyorum.

    O zaman:

    1) Verdiğim programdaki istemci ile ilgili olan herşeyi silelim

    2) Programa açıklayıcı olsun diye eklediğim writeln'leri silelim (veya o mesajları bir dosyaya yazalım)

    3) Portu 8081 olarak değiştirelim

    4) sunucu() işlevi içinden istemciye şöyle mesaj gönderelim:

    Kod:
        string mesaj = "Merhaba dünya!";
        const adet = bağlantı.send(mesaj);
    Bir dener misin...

    Ali
     
  5. netwalker

    netwalker Üye

    Kayıt:
    20 Kasım 2011
    Mesajlar:
    60
    Beğenilen Mesajlar:
    0
    İlginiz için teşekkür ederim açehreli.

    Terminal üzerinde şöyle bir hata verdi.

    Kod:
    Port: 8081Bekliyorum...
    339 bayt aldım: [71, 69, 84, 32, 47, 32, 72, 84, 84, 80, 47, 49, 46, 49, 13, 10, 72, 111, 115, 116, 58, 32, 108, 111, 99, 97, 108, 104, 111, 115, 116, 58, 56, 48, 56, 49, 13, 10, 67, 111, 110, 110, 101, 99, 116, 105, 111, 110, 58, 32, 107, 101, 101, 112, 45, 97, 108, 105, 118, 101, 13, 10, 65, 99, 99, 101, 112, 116, 58, 32, 116, 101, 120, 116, 47, 104, 116, 109, 108, 44, 97, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 47, 120, 104, 116, 109, 108, 43, 120, 109, 108, 44, 97, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 47, 120, 109, 108, 59, 113, 61, 48, 46, 57, 44, 42, 47, 42, 59, 113, 61, 48, 46, 56, 13, 10, 85, 115, 101, 114, 45, 65, 103, 101, 110, 116, 58, 32, 77, 111, 122, 105, 108, 108, 97, 47, 53, 46, 48, 32, 40, 77, 97, 99, 105, 110, 116, 111, 115, 104, 59, 32, 73, 110, 116, 101, 108, 32, 77, 97, 99, 32, 79, 83, 32, 88, 32, 49, 48, 95, 56, 95, 51, 41, 32, 65, 112, 112, 108, 101, 87, 101, 98, 75, 105, 116, 47, 53, 51, 55, 46, 51, 54, 32, 40, 75, 72, 84, 77, 76, 44, 32, 108, 105, 107, 101, 32, 71, 101, 99, 107, 111, 41, 32, 67, 104, 114, 111, 109, 101, 47, 50, 57, 46, 48, 46, 49, 53, 52, 55, 46, 53, 55, 32, 83, 97, 102, 97, 114, 105, 47, 53, 51, 55, 46, 51, 54, 13, 10, 65, 99, 99, 101, 112, 116, 45, 69, 110, 99, 111, 100, 105, 110, 103, 58, 32, 103, 122, 105, 112, 44, 100, 101, 102, 108, 97, 116, 101, 44, 115, 100, 99, 104, 13, 10, 65, 99, 99, 101, 112, 116, 45, 76, 97, 110, 103, 117, 97, 103, 101, 58, 32, 101, 110, 45, 85, 83, 44, 101, 110, 59, 113, 61, 48, 46, 56, 13, 10, 13, 10]
    İstemci bağlantısı 127.0.0.1:60381 kapatılıyor
    logout
    Bu istemci kısmını verdiğiniz koda göre düzenlediğim hali anladığım kadarıyla ama sanırım bende hata yapmış olabilirim bakarsınız sevinirim.

    Kod:
    import std.stdio;import std.socket;
    import std.getopt;
    import std.string;
    
    
    enum port = 8081;
    
    
    int main(string[] parametreler)
    {
        // Sunucu rolünde mi olalım istemci mi
        enum Rol { sunucu, istemci }
        Rol rol;
    
    
        try {
            // 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:  sunucu(); break;
        case Rol.istemci: istemci(); break;
        }
    
    
        return 0;
    }
    
    
    void sunucu()
    {
        // Önce bağlantıları karşılayacak olan soketi hazırlıyoruz
        auto dinleyici = new TcpSocket();
        scope (exit) {
            /* Gereken temizlik işlemleri
             *
             * Not: Tabii ki alt düzeyler kavramlar olan soketlerle böyle uğraşmak
             * yerine soketi sarmalayan bir Soket yapısı tanımlayabiliriz ve bu
             * işlemleri o türün sonlandırıcısında yapabiliriz. */
    
    
            // BOTH: "hem okuma hem yazma" anlamında
            dinleyici.shutdown(SocketShutdown.BOTH);
            dinleyici.close();
        }
    
    
        // Hangi portu dinleyeceğini belirtiyoruz
        dinleyici.bind(new InternetAddress(port));
        writefln("Port: %s", port);
    
    
        // İstemci bekliyoruz
        dinleyici.listen(1);
        writefln("Bekliyorum...");
    
    
        // Şimdi o sokette bağlantı kabul ediyoruz
        Socket bağlantı = dinleyici.accept();
        scope (exit) {
            writefln("İstemci bağlantısı %s kapatılıyor", bağlantı.remoteAddress());
            bağlantı.shutdown(SocketShutdown.BOTH);
            bağlantı.close();
        }
        
        // Okunan veriyi bu belleğe alacağız
        ubyte[1000] bellek;
        bool bitti_mi = false;
    
    
        while (!bitti_mi) {
            const adet = bağlantı.receive(bellek);
    
    
            if (adet == Socket.ERROR) {
                writefln("OKUMA HATASI");
    
    
            } else {
                writefln("%s bayt aldım: %s", adet, bellek[0..adet]);
                // Okunan verinin ne anlama geldiği bütünüyle sunucu ve istemci
                // arasındaki protokole bağlıdır.
            }
    
    
            bitti_mi = (adet < bellek.length);
        }
    }
    
    
    void istemci()
    {
        auto bağlantı = new TcpSocket();
        bağlantı.connect(new InternetAddress("127.0.0.1", port));
        scope (exit) {
            bağlantı.shutdown(SocketShutdown.BOTH);
            bağlantı.close();
        }
    
    
        // Bu örnekte mesajı string olarak göndereceğiz. Aslında send() parametre
        // olarak const(void)[] alır.
        string mesaj = "Merhaba dünya!";
        const adet = bağlantı.send(mesaj);
    }
    Ayrıca nginx 80 portunu dinler config dosyasında /*.nw yazdıp listen 8080 dediğimiz de bu porta proxy_pass eder ordan gelen datayı ekrana basar.

    Bir örnek C tarafında kurtcudukkani diye bir örnek var. Kodları aşağıda ufak düzenleme yaptığımda ve nginx ile ayarlama yaptığımda sorunsuz çalışıyor. Buda size yol gösterebilir.

    Kod:
    #include <stdio.h>#include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <netdb.h>
    #include <ifaddrs.h>
    #include <time.h>
    
    
    #define PORTNO          8080
    #define SITENAME        "facebookMainPage.html"
    
    
    #define BACKLOG         8
    #define HOSTNAME        100
    #define BUFSIZE         10000
    #define SOCKET_ERROR    -1
    
    
    
    
    int write_socket(int s, const void *pBuf, int n);
    int read_socket(int s, void *pBuf, int n);
    static void getIpAddress(char *addrp);
    void errorMessage(char *message);
    
    
    int main(void) {
        
        char servername[25];
        int portno = PORTNO;
        struct sockaddr_in servaddr, clientaddr;
        int servfd, clientfd;
        int addr_len;
        int yes = 1;
        char hostname[100];
        char buf[BUFSIZE];
        char request[250];
        int count = 0;
    
    
        getIpAddress(servername);
    
    
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(portno);
        memset(&servaddr.sin_zero, '\0', 8);
        servaddr.sin_addr.s_addr = inet_addr(servername);
    
    
        gethostname(hostname, 100);
    
    
        printf("KurtcuDukkani\n");
        printf("=============\n");
        printf("[%s] -> (%s):(%d)\n", hostname, servername, PORTNO);
    
    
    
    
        if ((servfd = socket(AF_INET, SOCK_STREAM, 0)) == SOCKET_ERROR) {
            perror("socket");
            exit(EXIT_FAILURE);
        }
    
    
        if (setsockopt(servfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof (int)) == SOCKET_ERROR) {
            perror("setsockopt");
            exit(EXIT_FAILURE);
        }
    
    
        if (bind(servfd, (struct sockaddr *) &servaddr, sizeof (servaddr)) == SOCKET_ERROR) {
            perror("bind");
            exit(EXIT_FAILURE);
        }
    
    
        if (listen(servfd, BACKLOG) == SOCKET_ERROR) {
            perror("listen");
            exit(EXIT_FAILURE);
        }
    
    
        printf("İstemci bekleniyor...\n\n");
    
    
        while (1) {
            addr_len = sizeof (clientaddr);
            if ((clientfd = accept(servfd, (struct sockaddr *) &clientaddr, &addr_len)) == SOCKET_ERROR) {
                perror("accept");
                exit(EXIT_FAILURE);
            }
    
    
            sprintf(request, "HTTP/1.0 200 OK\nContent-Type: text/html; charset=utf-8\n\n");
    
    
            if ((write_socket(clientfd, request, strlen(request)) == SOCKET_ERROR)) {
                    puts("Gonderilemedi...");
                    exit(EXIT_FAILURE);
            }
    
    
            char buf[] = "<b>Merhaba Dünya !</b>";
    
    
            if ((write_socket(clientfd, buf, strlen(buf)) == SOCKET_ERROR)) {
                    puts("Gonderilemedi...");            
                    exit(EXIT_FAILURE);
            }
    
    
            printf("%d OK...\r", ++count);
            fflush(stdout);
            shutdown(clientfd, 2);
        }
    
    
        close(servfd);
        return 0;
    }
    
    
    int write_socket(int s, const void *pBuf, int n) {
        int result;
        int index = 0;
        int left = n;
    
    
        while (left > 0) {
            result = send(s, (const char *) pBuf + index, left, 0);
    
    
            if (result == 0)
                return index;
    
    
            if (result == SOCKET_ERROR)
                return SOCKET_ERROR;
    
    
            index += result;
            left -= result;
        }
    
    
        return index;
    }
    
    
    
    
    int read_socket(int s, void *pBuf, int n)
    {
        int result;
        int index = 0;
        int left = n;
    
    
        while (left > 0) {
            result = recv(s, (char *) pBuf + index , left, 0);
    
    
            if (result == 0)
                return index;
    
    
            if (result == SOCKET_ERROR)
                return SOCKET_ERROR;
    
    
            index += result;
            left -= result;
    
    
        }
        return index;
    }
    
    
    static void getIpAddress(char *addrp)
    {
        struct ifaddrs *iflist, *iface;
    
    
            if (getifaddrs(&iflist) < 0) {
                perror("getifaddrs");
                exit(EXIT_FAILURE);
            }
            
            for (iface = iflist; iface; iface = iface->ifa_next) {
                int af = iface->ifa_addr->sa_family;
                const void *addr;
    
    
                switch (af) {
                    case AF_INET:
                        addr = &((struct sockaddr_in *)iface->ifa_addr)->sin_addr;
                        break;
                    case AF_INET6:
                        addr = &((struct sockaddr_in6 *)iface->ifa_addr)->sin6_addr;
                        break;
                    default:
                        addr = NULL;
                }
    
    
                if (addr) {
                    inet_ntop(af, addr, addrp, 80);
                    if (strstr(iface->ifa_name, "wlan") || strstr(iface->ifa_name, "eth"))
                        if (strchr(addrp, '.'))
                            break;
                    continue;
                }
            }
            freeifaddrs(iflist);
    
    
            if (!strchr(addrp, '.'))
                puts("Internet baglantin yok bi kere :) ");
    }
    
    
    void errorMessage(char *message) 
    {
        printf("%s...\n", message);
        getchar();
        exit(EXIT_FAILURE);
    }
    Bu örnek bahsettiğim şekilde çalışıyor.

    Görmek isterseniz diyerekten bu nginx.conf dosyası

    Kod:
    worker_processes 2; 
    error_log /usr/local/logs/error.log;
    pid /usr/local/logs/nginx.pid;
     
    events {
        worker_connections 1024;
    }
     
    http {
        charset utf-8;
     
        upstream frontends {
            server 127.0.0.1:8080;
        }
     
        #include /path/to/nginx.mime.types;
        default_type application/octet-stream;
     
        access_log /usr/local/logs/access.log;
     
        keepalive_timeout 65;
        proxy_read_timeout 200;
        sendfile on;
        tcp_nopush on;
        tcp_nodelay on;
        gzip on;
        gzip_min_length 1000;
        gzip_proxied any;
        gzip_types text/plain text/css text/xml
                   application/x-javascript application/xml
                   application/atom+xml text/javascript;
     
        proxy_next_upstream error;
     
        server {
            listen 80;
     
            location ^~ /static/ {
                root /users/netwalker/wwwroot;
                if ($query_string) {
                    expires max;
                }
            }
     
            location / {
                proxy_pass_header Server;
                proxy_set_header Host $http_host;
                proxy_redirect off;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Scheme $scheme;
                proxy_pass http://frontends;
            }
        }
    }
    
    
    
     
  6. acehreli

    acehreli Ali Çehreli

    Kayıt:
    19 Ekim 2002
    Mesajlar:
    4,973
    Beğenilen Mesajlar:
    2
    Aslında hata değil. :) O deneme programı gelen baytları yazdırıyor. Ne olduklarını görmek için o baytları bir string'i ilklemek için kullandım:

    Kod:
    import std.stdio;
    
    void main()
    {
        string s = [71, 69, 84, 32, 47, 32, 72, 84, 84, 80, 47, 49, 46, 49, 13,
                    10, 72, 111, 115, 116, 58, 32, 108, 111, 99, 97, 108, 104, 111,
                    115, 116, 58, 56, 48, 56, 49, 13, 10, 67, 111, 110, 110, 101,
                    99, 116, 105, 111, 110, 58, 32, 107, 101, 101, 112, 45, 97,
                    108, 105, 118, 101, 13, 10, 65, 99, 99, 101, 112, 116, 58, 32,
                    116, 101, 120, 116, 47, 104, 116, 109, 108, 44, 97, 112, 112,
                    108, 105, 99, 97, 116, 105, 111, 110, 47, 120, 104, 116, 109,
                    108, 43, 120, 109, 108, 44, 97, 112, 112, 108, 105, 99, 97,
                    116, 105, 111, 110, 47, 120, 109, 108, 59, 113, 61, 48, 46, 57,
                    44, 42, 47, 42, 59, 113, 61, 48, 46, 56, 13, 10, 85, 115, 101,
                    114, 45, 65, 103, 101, 110, 116, 58, 32, 77, 111, 122, 105,
                    108, 108, 97, 47, 53, 46, 48, 32, 40, 77, 97, 99, 105, 110,
                    116, 111, 115, 104, 59, 32, 73, 110, 116, 101, 108, 32, 77, 97,
                    99, 32, 79, 83, 32, 88, 32, 49, 48, 95, 56, 95, 51, 41, 32, 65,
                    112, 112, 108, 101, 87, 101, 98, 75, 105, 116, 47, 53, 51, 55,
                    46, 51, 54, 32, 40, 75, 72, 84, 77, 76, 44, 32, 108, 105, 107,
                    101, 32, 71, 101, 99, 107, 111, 41, 32, 67, 104, 114, 111, 109,
                    101, 47, 50, 57, 46, 48, 46, 49, 53, 52, 55, 46, 53, 55, 32,
                    83, 97, 102, 97, 114, 105, 47, 53, 51, 55, 46, 51, 54, 13, 10,
                    65, 99, 99, 101, 112, 116, 45, 69, 110, 99, 111, 100, 105, 110,
                    103, 58, 32, 103, 122, 105, 112, 44, 100, 101, 102, 108, 97,
                    116, 101, 44, 115, 100, 99, 104, 13, 10, 65, 99, 99, 101, 112,
                    116, 45, 76, 97, 110, 103, 117, 97, 103, 101, 58, 32, 101, 110,
                    45, 85, 83, 44, 101, 110, 59, 113, 61, 48, 46, 56, 13, 10, 13,
                    10];
    
        writeln(s);
    }
    
    Çıktısı şöyle:

    Kod:
    GET / HTTP/1.1
    Host: localhost:8081
    Connection: keep-alive
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.57 Safari/537.36
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: en-US,en;q=0.8
    
    Yani senin D programın nginx'ten o isteği alıyor. Şimdi ona yanıtını göndermeli. Bu konuda deneyimim yok; aradaki protokol ne ise ona uymalısın.

    Daha önce 8080 dediğin için nginx orada diye düşünmüştüm:

    Yani, yanlış anlamışım: nginx bildiğimiz 80'de, senin programın ona 8080'den bilgi aktaracak. Senin programın HTTP yanıtını oluşturur ve nginx'e gönderir. Aşağıdaki program tek hata ile çalışıyor:

    Kod:
    import std.stdio;
    import std.socket;
    import std.getopt;
    import std.string;
    
    enum port = 8080;
    
    void main()
    {
        sunucu();
    }
    
    void sunucu()
    {
        // Önce bağlantıları karşılayacak olan soketi hazırlıyoruz
        auto dinleyici = new TcpSocket();
        scope (exit) {
            /* Gereken temizlik işlemleri
             *
             * Not: Tabii ki alt düzeyler kavramlar olan soketlerle böyle uğraşmak
             * yerine soketi sarmalayan bir Soket yapısı tanımlayabiliriz ve bu
             * işlemleri o türün sonlandırıcısında yapabiliriz. */
    
    
            // BOTH: "hem okuma hem yazma" anlamında
            writefln("Dinleyici kapatılıyor");
            dinleyici.shutdown(SocketShutdown.BOTH);
            dinleyici.close();
        }
    
    
        // Hangi portu dinleyeceğini belirtiyoruz
        Address[] adresler = getAddress("localhost", port);
        writefln("Adresler: %s", adresler);
        auto adres = adresler[0];
        writefln("Adres: %s", adres);
        dinleyici.bind(adres);
    
        // İstemci bekliyoruz
        dinleyici.listen(1);
        writeln("Bekliyorum...");
    
    
        // Şimdi o sokette bağlantı kabul ediyoruz
        Socket bağlantı = dinleyici.accept();
        scope (exit) {
            writefln("İstemci bağlantısı %s kapatılıyor", bağlantı.remoteAddress());
            bağlantı.shutdown(SocketShutdown.BOTH);
            bağlantı.close();
        }
    
        // Okunan veriyi bu belleğe alacağız
        ubyte[1000] bellek;
        bool bitti_mi = false;
    
        while (!bitti_mi) {
            const adet = bağlantı.receive(bellek);
    
            if (adet == Socket.ERROR) {
                writeln("OKUMA HATASI");
    
            } else {
                writefln("%s bayt aldım: %s", adet, cast(string)bellek[0..adet]);
                // Okunan verinin ne anlama geldiği bütünüyle sunucu ve istemci
                // arasındaki protokole bağlıdır.
    
                string başlık =
                    "HTTP/1.0 200 OK\nContent-Type: text/html; charset=utf-8\n\n";
    
                string yanıt = başlık ~ "<b>Merhaba Dünya !</b>";
    
                bağlantı.send(yanıt);
            }
    
            bitti_mi = (adet < bellek.length);
        }
    }
    
    Linux ortamında bir hatası var: bind() ile ilişkilendirilen dosya yolunun Linux komutları olan unlink veya remove ile silinmesi gerekiyormuş. socket.d'nin bu hizmeti verip vermediğini anlayamadım ama o temizliğin bu programda yapılmadığı açık: Programı kapattıktan hemen sonra tekrar başlatamıyorum. Bir kaç saniye geçmesi ve sistemin 8080 portunu tekrar serbest bırakması gerekiyor. (?)

    Ali
     
  7. netwalker

    netwalker Üye

    Kayıt:
    20 Kasım 2011
    Mesajlar:
    60
    Beğenilen Mesajlar:
    0
    İlginiz için teşekkür ederim açehreli,

    Verdiğiniz kodları derledim derleme sırasında sorun oluşturmadı; Ancak derlenmiş dosyayı çalıştırdıktan sonra ekrana bu hataları verdi. Kullandığım işletim sistemi Mac OS X Unix Tabanlı

    Kod:
    netwalker:~ netwalker$ /Users/netwalker/Desktop/httpd ; exit;
    Adresler: [[::1]:8080, [::1]:8080, 127.0.0.1:8080, 127.0.0.1:8080, [fe80::1%lo0]:8080, [fe80::1%lo0]:8080]
    Adres: [::1]:8080
    Dinleyici kapatılıyor
    std.socket.SocketOSException@std/socket.d(2460): Unable to bind socket: Address family not supported by protocol family
    ----------------
    5   httpd                               0x000000010cc6e7f8 void httpd.sunucu() + 228
    6   httpd                               0x000000010cc6e6f5 _Dmain + 9
    7   httpd                               0x000000010cc90b21 extern (C) int rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).void runMain() + 33
    8   httpd                               0x000000010cc9066d extern (C) int rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).void tryExec(scope void delegate()) + 45
    9   httpd                               0x000000010cc90b6d extern (C) int rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).void runAll() + 61
    10  httpd                               0x000000010cc9066d extern (C) int rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).void tryExec(scope void delegate()) + 45
    11  httpd                               0x000000010cc90621 _d_run_main + 457
    12  httpd                               0x000000010cc90450 main + 20
    13  libdyld.dylib                       0x00007fff918827e1 start + 0
    14  ???                                 0x0000000000000001 0x0 + 1
    ----------------
    logout
    
    
    [Process completed]
    
     
  8. acehreli

    acehreli Ali Çehreli

    Kayıt:
    19 Ekim 2002
    Mesajlar:
    4,973
    Beğenilen Mesajlar:
    2
    bind() ile ilgili sorunu araştırırken InternetAddress'in açıkça oluşturulması yerine getAddress()'in çağrılmasının daha iyi olduğunu okumuştum. bind ile ilgili kodları o yüzden değiştirmiştim.

    Orasını tekrar eski haline çevirmeyi deneyebilirsin çünkü daha önce öyle bir hata almamıştın:

    Kod:
        // Hangi portu dinleyeceğini belirtiyoruz
        dinleyici.bind(new InternetAddress(port));
        writefln("Port: %s", port);
    
    Ali
     
  9. netwalker

    netwalker Üye

    Kayıt:
    20 Kasım 2011
    Mesajlar:
    60
    Beğenilen Mesajlar:
    0
    İlginiz için çok teşekkür ederim;

    Yanlız tek sıkıntı var localhost:8080 girdiğimde ekrana merhaba yazısı yazdığı zaman program sonlanıyor. Sonlanmaması gerekiyor açık kalması ve gelen isteğe göre işlem yapması gerekiyor.

    İşlemleri sonlandırdığınız yerleri kaldırdım ancak yinede işlem sonlanıyor :(
     
  10. acehreli

    acehreli Ali Çehreli

    Kayıt:
    19 Ekim 2002
    Mesajlar:
    4,973
    Beğenilen Mesajlar:
    2
    O program senin başlık satırında yazdığın sorunun çok güzel küçük bir örneğiydi. ;) Benim bildiğim ve bu iş için gereken her şey açıklama satırlarında anlatılıyor.

    Yeni istediğin şey programa bir sonsuz döngü eklemek kadar kolay olmalı:

    Kod:
        while (true) {
            // İstemci bekliyoruz
            dinleyici.listen(1);
            writeln("Bekliyorum...");
    
            // ...
        }
    
    Ama onu zaten denemişsindir diye de düşünmeden edemiyorum... :)

    Ali
     
  11. netwalker

    netwalker Üye

    Kayıt:
    20 Kasım 2011
    Mesajlar:
    60
    Beğenilen Mesajlar:
    0
    İlgi alakanız için çok teşekkür ederim acehreli,

    D dili yine beni büyüledi bir kez daha c ile bunu yapmak çok daha zor ve socket olayını D ile çok daha rahat anladım.

    Ancak sonsuz döngü olayı tahmin ettiğim gibi ekrana sürekli "Bekliyorum..." yazıyor ama tarayıcı üzerinden localhost:8080 yazdığım zaman orasıda sonsuz döngüye giriyor :). Fark ettiğim bir şey var programı çalıştırıp kapattıktan sonra tekrar çalıştırmak istediğimde terminal ekranına.

    Kod:
    std.socket.SocketOSException@std/socket.d(2460): Unable to bind socket: Address already in use
    ----------------
    5   httpd                               0x00000001024f6e24 void httpd.sunucu() + 80
    6   httpd                               0x00000001024f6db5 _Dmain + 9
    7   httpd                               0x0000000102516001 extern (C) int rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).void runMain() + 33
    8   httpd                               0x0000000102515b4d extern (C) int rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).void tryExec(scope void delegate()) + 45
    9   httpd                               0x000000010251604d extern (C) int rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).void runAll() + 61
    10  httpd                               0x0000000102515b4d extern (C) int rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).void tryExec(scope void delegate()) + 45
    11  httpd                               0x0000000102515b01 _d_run_main + 457
    12  httpd                               0x0000000102515930 main + 20
    13  libdyld.dylib                       0x00007fff8bf307e1 start + 0
    14  ???                                 0x0000000000000001 0x0 + 1
    ----------------
    

    Son ricam olacak aslında oldukca yine rahatsız ederim sanırım :).

    (multiple-connections, multi-threads) özellikleri koyabilmem için nasıl yol izlemeliyim ne yapmalıyım bunları biraz açmamı isterseniz şöyle;

    Multiple Connections (şuanda 8080 portu üzerinden dinliyor ve işlem yapıyor buna daha fazla port desteği koyarak örn(8080,8081,8082,8083) portlar'dan gelen verileri işlemci üzerinde paylaştırmak. İşlemci olayı içinse sanırım Multi Threads oluyordu)
     
  12. Atakan Erbaþ

    Atakan Erbaþ Aktif Üye

    Kayıt:
    11 Mayıs 2012
    Mesajlar:
    459
    Beğenilen Mesajlar:
    0
    Şehir:
    Bursa
    Ben hiç D bilmem ama C çözümünden ipucu verebilirim. Select gibi işlevleri biliyor musun? Bunlar programı geçici olarak çizelge dışına çıkarırlar. Bu şekilde binlerce istemciye aynı anda hizmet verebilirsin. Hem de az işlemci gücü harcanmış olur. Sen sanırım çoklu bağlantı derken farklı portları aynı anda kullanmak istiyorsun. Yapman gereken şey o portlara da birer dinleyici bağlamak. Sonra da soket kümesine eklemen. Sonrasını select gibi yöntemlerle halledersin.
    İşlemci olayı derken kastettiğini anlamadım. Multi-threading istemci arttıkça kötü performans göstermeye başlar.
     
  13. acehreli

    acehreli Ali Çehreli

    Kayıt:
    19 Ekim 2002
    Mesajlar:
    4,973
    Beğenilen Mesajlar:
    2
    Onu tam anlamadım. Ben denediğimden o kadarı doğru çalışmıştı.

    Gözünden kaçmış herhalde ama o sorunu yukarıda ben de söylemiştim. ;) Öğrenmek için şu konuyu açtım:

    http://forum.dlang.org/post/kvh6g3$1d38$1@digitalmars.com

    Regan Heath çözümler önermiş ama daha deneyemedim.

    Bu konular da C ve C++ ile karşılaştırıldığında D'de olağanüstü kolay ve güçlü. D'nin koşut işlemler (parallelization) ve eş zamanlı programlama (concurrency) olanakları kullanılır. Şuradan başlayan üç bölüm bunlarla ilgili:

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

    Aslında yine de hep 8080'i kullanmak istiyorsun ama aynı portta birden fazla istemciye hizmet vermek istiyorsun. O da herhalde Atakan Erbaş'ın önerdiği select ile olmalı...

    Ali
     
  14. netwalker

    netwalker Üye

    Kayıt:
    20 Kasım 2011
    Mesajlar:
    60
    Beğenilen Mesajlar:
    0
    Konuyu okudum ancak tam anlayamadım kafam karıştı eyer ki deneyebilirseniz sevinirim sonuçta D dilinde benden daha tecrübelisiniz.

    Şöyle çözüme ulaştım ama... Tabiki daha iyi öneriler olursa mennun olurum.

    Kod:
    import std.stdio;
    import std.socket;
    import std.getopt;
    import std.string;
    
    
    enum port = 8080;
    
    
    void main()
    {
        sunucu();
    }
    
    
    void sunucu()
    {
        auto dinleyici = new TcpSocket();
    
    
        Address[] adresler = getAddress("localhost", port);
        writefln("Adres: %s", adresler);
    
    
        dinleyici.bind(new InternetAddress(port));
        writefln("Port: %s", port);
    
    
        dinleyici.listen(1);
        writeln("Dinleniyor...");
    
    
        while(true)
        {
            Socket baglanti = dinleyici.accept();
    
    
            ubyte[1000] bellek;
            bool finished = false;
    
    
            while (!finished)
            {
                const adet = baglanti.receive(bellek);
    
    
                if (adet == Socket.ERROR)
                {
                    writeln("OKUMA HATASI");
                }
                else
                {
                    writefln("%s Bayt Okundu: %s", adet, cast(string)bellek[0..adet]);
                    string başlık = "HTTP/1.0 200 OK\nContent-Type: text/html; charset=utf-8\n\n";
                    string yanıt = başlık ~ "<b>Merhaba Dünya !</b>";
                    baglanti.send(yanıt);
                }
    
    
                finished = (adet < bellek.length);
            }
        }
    }
    
    Bende sizin kitabınızı bastırmıştım sonra 118 sayfayı cıkartıp elime aldığımda ilk bu konuya rastladım okudum uyarlamaya çalışacağım ama tam olarak yukari kodda nasıl uyarlıyacağımı daha çözümleyemedim.

    --- Ekleme ---

    Unutmuşum Port olayın gelince.

    Nginxde verilen ip ve portlara işlem yüküne göre pass etme olayı var şöyle

    Kod:
    upstream frontends {        server 127.0.0.1:8080;
            server 127.0.0.1:8081;
            server 127.0.0.1:8082;
            server 127.0.0.1:8083;
            server 127.0.0.1:8084;
            server 127.0.0.1:8085;
        }
    bunlar sadece 127.0.0.1 olması şart değil ip adreside olabilir farklı ip adresler ve portlar şeklinde tabi her ip'ye verebiliceğin port max 5 tane olabilir 5 den fazlasıda gereksiz.

    nginx dediğim gibi işlem yüküne göre işlemleri portlara dağıtıyor.

    Bense bu portları okuyor olacağım ve bir port üzerinden gelen tüm işlemleri direk karşılayıp işlemeye çalışmak yerine portlar üzerinden gelen istekleri karşılayıp işlemci üzerinde dağıtarak hem daha cabuk işlem yapmasını sağlamak hemde güzel bir performans elde edilebilir.

    aynı zaman ölçeklenebilirlik gayet güzel olacaktır.

    --- Ekleme ---

    Unutmuşum Port olayın gelince.

    Nginxde verilen ip ve portlara işlem yüküne göre pass etme olayı var şöyle

    Kod:
    upstream frontends {        server 127.0.0.1:8080;
            server 127.0.0.1:8081;
            server 127.0.0.1:8082;
            server 127.0.0.1:8083;
            server 127.0.0.1:8084;
            server 127.0.0.1:8085;
        }
    bunlar sadece 127.0.0.1 olması şart değil ip adreside olabilir farklı ip adresler ve portlar şeklinde tabi her ip'ye verebiliceğin port max 5 tane olabilir 5 den fazlasıda gereksiz.

    nginx dediğim gibi işlem yüküne göre işlemleri portlara dağıtıyor.

    Bense bu portları okuyor olacağım ve bir port üzerinden gelen tüm işlemleri direk karşılayıp işlemeye çalışmak yerine portlar üzerinden gelen istekleri karşılayıp işlemci üzerinde dağıtarak hem daha cabuk işlem yapmasını sağlamak hemde güzel bir performans elde edilebilir.

    aynı zaman ölçeklenebilirlik gayet güzel olacaktır.
     
    Son düzenleme yönetici tarafından yapıldı: 27 Ağustos 2013
  15. Atakan Erbaþ

    Atakan Erbaþ Aktif Üye

    Kayıt:
    11 Mayıs 2012
    Mesajlar:
    459
    Beğenilen Mesajlar:
    0
    Şehir:
    Bursa
    3 seçeneğin var:
    1)Select gibi yöntemler kullanmak. Select için port farketmez. İçine okuma dosya kümesi, yazma dosya kümesi ve hata dosya kümesini eklersin(böyle mi çevriliyor bilmiyorum :) ). Bu kümelere sırasıyla veri beklediğin ve yazmak için beklediğin soketleri eklersin. Select yerine sonsuz döngüye almak performans açısından iyi değildir. Select'i döngü içinde kullanmaktan bahsetmiyorum, her soketi select yerine teker teker kontrol etmekten bahsediyorum. Select çok daha verimlidir. Bu işlev çalıştırıldığında daha önce dediğim gibi program çizelge dışına alınır. Yani bir nevi durdurulur. İşletim sistemi bu kümelerden birine uyarı verince program devam eder. CPU israfı olmaz. Tabi sadece select değil poll gibi yöntemler de mevcut ama hiç denemedim.
    2)Multi-threading. Bu yöntem çok sayıda sokette kullanılırsa performans düşüklüğüne yol açar. Azami 10 istemciye hizmet vereceksen kullanmalısın. Senin yapacağın şey için uygun yöntem olduğunu sanmıyorum.
    3)Karışım. Her porta bir thread olacak şekilde bunu yapabilirsin. Aynı zamanda her thread'de de select çalıştırırsın.


    D yolunu bilmiyorum ama C'de multithreading'in select'ten çok daha çetrefilli olduğunu söyleyebilirim. Performans konusunda da 1 ile 3 arasında kaldım. Senin programında 2'yi eleyebiliriz. Bu yöntemin performans düşüklüğü göstermesinin sebebi sistemlerin işlemleri aynı anda yürütülmesi için bir takım harcamalar yapıyor oluşu. 10 thread oluşturursan işletim sistemi bu thread'ler
    arasında geçiş yaparken ister istemez harcama yapacaktır. Fakat işini select ile halledersen sistem kaynağı harcamazsın bile neredeyse.
    Ali Bey'in dediği gibi paralel programlama da yapabilirsin ama benim bu konuda bilgim yok. :)
    D gerçekten C'ye nazaran daha kısa yoldan sonuca ulaşılabilen bir dil gibi duruyor. C'de hostent sin_addr sin_zero derken çok uzuyor işler. Ama performansından memnunum.
    KUTALMIS yok buralarda, ben de sokette onun sayesinde bayağı bilgi edinmiştim. Olsaydı birkaç şey de o eklerdi.
     
  16. acehreli

    acehreli Ali Çehreli

    Kayıt:
    19 Ekim 2002
    Mesajlar:
    4,973
    Beğenilen Mesajlar:
    2
    Öyle ama C'yi hangi dille karşılaştırsak öyle olur. ;) Bu gibi konularda D'yi C++ ile karşılaştırmak daha doğru olur. C++11'in yenilikleri C++'ı D'ye yaklaştırdı.

    Performans konusunda hem C++ hem de D C ile aynıdır. Yani, o dillerin kendilerinden gelen ek bir performans kaybı yoktur.

    Ali
     
  17. netwalker

    netwalker Üye

    Kayıt:
    20 Kasım 2011
    Mesajlar:
    60
    Beğenilen Mesajlar:
    0
    Önerilerin için teşekkür ederim Atakan dikkate alacağım.

    Yardımların için teşekkür ederim acehreli.

    1- Şuanda çökme konusu dışında sorunsuz çalışıyor; Ancak biraz yük bindiği zaman erkana sürekli "0 bayt data" yazısıyla sonsuz döngüye giriyor bunu nasıl önlerim.
    2- Performans konusunda Atakan arkadaşın söylediklerini dikkate alarak D dilini daha iyi bilen biri olarak ne yapmamı önerir nasıl yol izlememi önerirsiniz tam olarak (kullanmamı yapmamı önerdiğiniz şeyler).
    3- Soketi While döngüsüne alarak sürekli çalıştırmak doğru mu ?
     
  18. acehreli

    acehreli Ali Çehreli

    Kayıt:
    19 Ekim 2002
    Mesajlar:
    4,973
    Beğenilen Mesajlar:
    2
    Çökme derken daha önce konuştuğumuz konuyu mu söylüyorsun? Port, ancak program sonlandıktan belli bir süre sonra tekrar kullanılabiliyordu hani. Onun çözümü, SocketOption.REUSEADDR imiş.

    Biraz yük binmek? Biraz daha açıklayıcı olabilir misin? "bayt data" nereden geliyor acaba? Bizim programda öyle bir mesaj olduğunu hatırlamıyorum.

    Daha önce söylediklerim geçerli: std.parallelism ve std.concurrency modülleri bazı eksikliklerine rağmen olağanüstü güçlü ve kullanışlı modüller.

    Benim bildiğim kadarıyla öyle. İstemci bekleyen sunucular çoğunlukla sonsuz bir döngüde beklerler ve hizmet için bir iş parçacığını görevlendirirler. Aşağıdaki programda ben de öyle yaptım. Ancak, o örnek program iş parçacığı adedinde hiçbir kısıtlama getirmiyor. Aynı anda ne kadar istemci gelirse hepsi için bir iş parçacığı başlatıyor.

    Örneği ilginçleştirmek için yanıtı 10 saniye geciktirerek gönderdim. Eğer birden fazla istek gönderdiğinde (örneğin, farklı konsollardan veya farklı tarayıcılardan), hepsinin de aynı anda işlendiklerini göreceksin; art arda değil...

    Ayrıca, baştaki örnekteki temizlik işlemlerini de zaten olmaları gerektiği gibi yapı ve sınıfların sonlandırıcı işlevleri içine yerleştirdim. Kullanım çok daha kolay hale geldi.

    Kod:
    import std.stdio;
    import std.socket;
    import std.concurrency;
    import std.range;
    import core.thread;
    
    enum port = 8080;
    
    void main()
    {
        auto sunucu = Sunucu(port);
        sunucu.başla();
    }
    
    /*
     * İstemcilere eş zamanlı olarak hizmet eden işlev. Parametre olarak bir
     * İstemci 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 işçiİşlevi(shared(İstemci) istemci_)
    {
        scope (exit) {
            destroy(istemci_);
        }
    
        /* concurrency modülü henüz shared ile tam uyumlu olarak
         * çalışmıyor. Şimdilik en iyisi shared olarak almak ve işlev içinde tür
         * dönüşümü ile shared'i kaldırmak:
         */
        auto istemci = cast()istemci_;
        string istek = istemci.oku();
    
        enum saniye = 10;
        writefln("İsteği aldım. Yapay olarak %s saniye bekleyeceğim", saniye);
    
        foreach (i; 0 .. 10) {
            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.gönder(yanıt);
    }
    
    struct Sunucu
    {
        Socket dinleyici;
    
        this(ushort port)
        {
            // Önce bağlantıları karşılayacak olan soketi hazırlıyoruz
            dinleyici = new TcpSocket();
            dinleyici.setOption(
                SocketOptionLevel.SOCKET, SocketOption.REUSEADDR, true);
    
            // Hangi portu dinleyeceğini belirtiyoruz
            auto adres = new InternetAddress(port);
            dinleyici.bind(adres);
            writefln("Adres: %s", adres);
    
            // İstemci bekliyoruz
            dinleyici.listen(1);
            writeln("Bekliyorum...");
    
        }
    
        ~this()
        {
            writefln("Dinleyici kapatılıyor");
            dinleyici.shutdown(SocketShutdown.BOTH);
            dinleyici.close();
        }
    
        /*
         * Sonsuz döngüde istemci bekler ve hiç zaman geçirmeden onun isteğini
         * yerine getirecek olan bir işçi başlatır.
         */
        void başla()
        {
            while (true) {
                // Şimdi o sokette bağlantı kabul ediyoruz
                Socket bağlantı = dinleyici.accept();
    
                writefln("İstemci bağlandı: %s", bağlantı.remoteAddress());
    
                auto istemci = new İstemci(bağlantı);
    
                /* 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(&işçiİşlevi, cast(shared)istemci);
            }
        }
    }
    
    class İstemci
    {
        Socket bağlantı;
    
        this(Socket bağlantı)
        {
            this.bağlantı = bağlantı;
        }
    
        ~this()
        {
            writefln("İstemci bağlantısı %s kapatılıyor",
                     bağlantı.remoteAddress());
            bağlantı.shutdown(SocketShutdown.BOTH);
            bağlantı.close();
        }
    
        string oku()
        {
            string istek;
    
            // Okunan veriyi parça parça bu belleğe alacağız
            ubyte[1000] bellek;
            bool bütünVeriOkundu_mu = false;
    
            while (!bütünVeriOkundu_mu) {
                const adet = bağlantı.receive(bellek);
    
                if (adet == Socket.ERROR) {
                    stderr.writeln("OKUMA HATASI");
                    break;
    
                }
    
                istek ~= bellek;
    
                bütünVeriOkundu_mu = (adet < bellek.length);
            }
    
            return istek;
        }
    
        void gönder(string veri)
        {
            bağlantı.send(veri);
        }
    }
    Yukarıdaki programı bir inceler misin? Nasıl? Şimdiye kadarki isteklerini yerine getiriyor, değil mi?

    Ali
     
  19. netwalker

    netwalker Üye

    Kayıt:
    20 Kasım 2011
    Mesajlar:
    60
    Beğenilen Mesajlar:
    0
    Öncelikle ilgi alakanız için çok teşekkür ederim acehreli,

    Apache Benchmark'ı biliyorsunuz sanırım terminal üzerinden şu komut ile "ab -n 2000 -c 10 http://127.0.0.1:8080/" şeklinde test başlattığım zaman normalde hemen bitiyor ve program sonsuz döngüye giriyor. Tarayıcı üzerinden erişmek istediğimde sayfa bulunamıyordu.

    Son hali ile test ettim şu anda bir sorun görünmüyor.

    Bilgilendirmek amaçlı olarak bir kaç not:

    Html datası Başlıktan önce gelirmiş araştırmam sonucu öyle bilgi edinmiştim

    Kod:
    1. http 1.0 sürümünde content-length belirtmek zorunlu
    
    
    2. data \r\n\r\n den sonra gelir GET isteklerinde
    3. post işlemlerinde bütün dataya content-length ile erişebilirsiniz
    şeklinde bilgi edinmiştim.

    Sanırım hepsini aynı anda alıyor ama arada süre olduğu için tabi sonucu ekrana 10 saniye sonra yansıtıyor. Tarayıcı ekranına Merhaba Dünya 10 sonra sonra geliyor.

    Bu kod parçacıklarını inceleyebilirmisiniz.

    Kod:
    [COLOR=#333333][FONT=Consolas][B]class[/B][COLOR=#333333][FONT=Consolas]ConnectionThread2[COLOR=#333333][FONT=Consolas]:[COLOR=#333333][FONT=Consolas]Thread[COLOR=#333333][FONT=Consolas]{
        [B]import[/B] std.concurrency;
        [B]this[/B]([COLOR=#445588][B]void[/B] [B]function[/B](Socket) handler) {
            [B]this[/B].handler = handler;
            [B]super[/B](&run);
        }
    
    
        [COLOR=#445588][B]void[/B] run() {
            tid = thisTid();
            available = [B]true[/B];
            [B]while[/B]([B]true[/B])
            receive(
                ([COLOR=#999988][I]/*Socket*/[/I] size_t s) {
                    available = [B]false[/B];
                    [B]try[/B] {
                        handler([B]cast[/B](Socket) [B]cast[/B]([COLOR=#445588][B]void[/B]*) s);
                    } [B]catch[/B](Throwable t) {}
                    available = [B]true[/B];
                }
            );
        }
    
    
        [COLOR=#445588][B]bool[/B] available;
        Tid tid;
        [COLOR=#445588][B]void[/B] [B]function[/B](Socket) handler;
    [COLOR=#333333][FONT=Consolas]}
    Kod:
    [COLOR=#999988][FONT=Consolas][I]/**[/I]
    [COLOR=#999988][I]    To use this thing:[/I]
    
    
    [COLOR=#999988][I]    void handler(Socket s) { do something... }[/I]
    [COLOR=#999988][I]    auto manager = new ListeningConnectionManager(80, &handler);[/I]
    [COLOR=#999988][I]    manager.listen();[/I]
    
    
    [COLOR=#999988][I]    I suggest you use BufferedInputRange(connection) to handle the input. As a packet[/I]
    [COLOR=#999988][I]    comes in, you will get control. You can just continue; though to fetch more.[/I]
    
    
    
    
    [COLOR=#999988][I]    FIXME: should I offer an event based async thing like netman did too? Yeah, probably.[/I]
    [COLOR=#999988][I]*/[/I]
    [B]class[/B] ListeningConnectionManager {
        [COLOR=#445588][B]void[/B] listen() {
            [B]version[/B](multiple_connections_per_thread) {
                [B]import[/B] std.concurrency;
                [B]import[/B] std.random;
                ConnectionThread2[[COLOR=#009999]16] pool;
                [B]foreach[/B]([B]ref[/B] p; pool) {
                     p = [B]new[/B] ConnectionThread2(handler);
                     p.start();
                }
    
    
                [B]while[/B]([B]true[/B]) {
                    [B]auto[/B] connection = listener.accept();
    
    
                    [COLOR=#445588][B]bool[/B] handled = [B]false[/B];
                    retry:
                    [B]foreach[/B](p; pool)
                        [B]if[/B](p.available) {
                            handled = [B]true[/B];
                            send(p.tid, [B]cast[/B](size_t) [B]cast[/B]([COLOR=#445588][B]void[/B]*) connection);
                            [B]break[/B];
                        }
    
    
                    [COLOR=#999988][I]// none available right now, make it wait a bit then try again[/I]
                    [B]if[/B](!handled) {
                        Thread.sleep(dur![COLOR=#DD1144]"msecs"([COLOR=#009999]25));
                        [B]goto[/B] retry;
                    }
                }
            } [B]else[/B] {
                [B]foreach[/B](connection; [B]this[/B])
                    handler(connection);
    
    
            }
        }
    
    
        [B]this[/B]([COLOR=#445588][B]ushort[/B] port, [COLOR=#445588][B]void[/B] [B]function[/B](Socket) handler) {
            [B]this[/B].handler = handler;
            listener = [B]new[/B] TcpSocket();
            listener.setOption(SocketOptionLevel.SOCKET, SocketOption.REUSEADDR, [B]true[/B]);
            listener.bind([B]new[/B] InternetAddress(port));
            listener.listen([COLOR=#009999]10);
        }
    
    
        Socket listener;
        [COLOR=#445588][B]void[/B] [B]function[/B](Socket) handler;
    
    
        [COLOR=#445588][B]bool[/B] running;
        [COLOR=#445588][B]void[/B] quit() {
            running = [B]false[/B];
        }
    
    
        [COLOR=#445588][B]int[/B] opApply([B]scope[/B] CMT dg) {
            running = [B]true[/B];
            shared([COLOR=#445588][B]int[/B]) loopBroken;
    
    
            [B]while[/B](!loopBroken && running) {
                [B]auto[/B] sn = listener.accept();
                [B]try[/B] {
                    [B]version[/B](cgi_no_threads) {
                        [COLOR=#999988][I]// NEVER USE THIS[/I]
                        [COLOR=#999988][I]// it exists only for debugging and other special occasions[/I]
    
    
                        [COLOR=#999988][I]// the thread mode is faster and less likely to stall the whole[/I]
                        [COLOR=#999988][I]// thing when a request is slow[/I]
                        dg(sn);
                    } [B]else[/B] {
                        [COLOR=#999988][I]/*[/I]
    [COLOR=#999988][I]                    version(cgi_multiple_connections_per_thread) {[/I]
    [COLOR=#999988][I]                        bool foundOne = false;[/I]
    [COLOR=#999988][I]                        tryAgain:[/I]
    [COLOR=#999988][I]                        foreach(t; pool)[/I]
    [COLOR=#999988][I]                            if(t.s is null) {[/I]
    [COLOR=#999988][I]                                t.s = sn;[/I]
    [COLOR=#999988][I]                                foundOne = true;[/I]
    [COLOR=#999988][I]                                break;[/I]
    [COLOR=#999988][I]                            }[/I]
    [COLOR=#999988][I]                        Thread.sleep(dur!"msecs"(1));[/I]
    [COLOR=#999988][I]                        if(!foundOne)[/I]
    [COLOR=#999988][I]                            goto tryAgain;[/I]
    [COLOR=#999988][I]                    } else {[/I]
    [COLOR=#999988][I]                    */[/I]
                            [B]auto[/B] thread = [B]new[/B] ConnectionThread(sn, &loopBroken, dg);
                            thread.start();
                        [COLOR=#999988][I]//}[/I]
                    }
                    [COLOR=#999988][I]// loopBroken = dg(sn);[/I]
                } [B]catch[/B](Exception e) {
                    [COLOR=#999988][I]// if a connection goes wrong, we want to just say no, but try to carry on unless it is an Error of some sort (in which case, we'll die. You might want an external helper program to revive the server when it dies)[/I]
                    sn.close();
                }
            }
    
    
            [B]return[/B] loopBroken;
        }
    [COLOR=#333333][FONT=Consolas]}
    Kod:
    [COLOR=#333333][FONT=Consolas][B]import[/B][COLOR=#333333][FONT=Consolas]core[COLOR=#333333][FONT=Consolas].[COLOR=#333333][FONT=Consolas]thread[COLOR=#333333][FONT=Consolas];
    [B]class[/B] ConnectionThread : Thread {
        [B]this[/B](Socket s, shared([COLOR=#445588][B]int[/B])* breakSignifier, CMT dg) {
            [B]this[/B].s = s;
             [B]this[/B].breakSignifier = breakSignifier;
            [B]this[/B].dg = dg;
            [B]super[/B](&runAll);
        }
    
    
        [COLOR=#445588][B]void[/B] runAll() {
            [B]if[/B](s ![B]is[/B] [B]null[/B])
                run();
            [COLOR=#999988][I]/*[/I]
    [COLOR=#999988][I]        version(cgi_multiple_connections_per_thread) {[/I]
    [COLOR=#999988][I]            while(1) {[/I]
    [COLOR=#999988][I]                while(s is null)[/I]
    [COLOR=#999988][I]                    sleep(dur!"msecs"(1));[/I]
    [COLOR=#999988][I]                run();[/I]
    [COLOR=#999988][I]            }[/I]
    [COLOR=#999988][I]        }[/I]
    [COLOR=#999988][I]        */[/I]
        }
    
    
        [COLOR=#445588][B]void[/B] run() {
            [B]scope[/B](exit) {
                [COLOR=#999988][I]// I don't want to double close it, and it does this on close() according to source[/I]
                [COLOR=#999988][I]// might be fragile, but meh[/I]
                [B]if[/B](s.handle() != socket_t.init)
                    s.close();
                s = [B]null[/B]; [COLOR=#999988][I]// so we know this thread is clear[/I]
            }
            [B]if[/B]([B]auto[/B] result = dg(s)) {
                *breakSignifier = result;
            }
        }
    
    
        Socket s;
        shared([COLOR=#445588][B]int[/B])* breakSignifier;
        CMT dg;
    [COLOR=#333333][FONT=Consolas]}
    Bu kodları bana Adam isimli cgi.d yazarı gönderdi incele diye ancak nasıl mantık izlediğini çözümlüyemedim. Aslında anladım ama sizinde fikrinizi örneğinizi almak isterim.

    Kesinlikle getiriyor çok teşekkür ederim bu yapı üzerinden devam ederek geliştiriceğim ve ufak bir blog yapıcam amacım soket'i öğrenmek.
    Sizinle bunu daha rahat kavradım heleki D dili harika bir kez daha anladım.

    Buraya göz atabilirmisiniz: http://en.wikipedia.org/wiki/Non-blocking_algorithm

    Tekrar teşekkür ederim.
     
    Son düzenleme yönetici tarafından yapıldı: 28 Ağustos 2013
  20. Atakan Erbaþ

    Atakan Erbaþ Aktif Üye

    Kayıt:
    11 Mayıs 2012
    Mesajlar:
    459
    Beğenilen Mesajlar:
    0
    Şehir:
    Bursa
    Performans açısından multi-threading çok daha kötü fazla istemci olduğunda...
    En iyisi dosyaya okuma veya yazma yapıldığını anında anlayabilen işletim sisteminden bu verileri almak. Ama istersen ikisini de dene, farkı kendin gör. Gerçi bu fark çok kişi olduğunda belirgin hale gelir sanırım. 1024 istemci bağlanırsa 1024 parçacık oluşturup hepsini bekleyecek misin :) Sonsuz döngü içerisinde select türevi kullanmak en etkili yöntemlerden biri. Ama dikkatli ol, select çok soket olduğunda biraz kötüleşiyor diye hatırlıyorum. Poll de kullanabilirsin. Burada select'i biraz tanıtmış:
    http://dlang.org/phobos/std_socket.html#.Socket.select
    Burada da parametredeki SocketSet sınıfını tanıtmış:
    http://dlang.org/phobos/std_socket.html#.SocketSet

    Açıkçası görünce D'ye başlayasım geldi :D