2016-03-26 31 views
0

üzerinde dinamik arabellekle engelleme olmayan recv recv aşağıdaki kodda recv'nin neden engellendiğini anlamaya çalışıyorum, eğer telnet ve 'GET/HTTP/1.1' gönderirseniz, recv beklemeye devam ediyor veri ve başka bir telnet bağlantısı da engeller. Ancak,select() ve C

char buffer[1024]; 
nbytes = recv(i, buffer, sizeof buffer, 0); 

şimdiye kadar anlıyorum Ne, gayet iyi çalışıyor ve ben sadece bir deyişle yerine do{} while sabit tampon, kullanırsanız engellemez, o noktada select() hazır okuması durumunda olduğu . recv'u O_NONBLOCK olarak ayarlamam gerekir mi?

char *buffer = NULL; 
unsigned long LEN = 200; 
unsigned long bytes_received = 0; 
unsigned long cur_size = 0; 
int status = 0; 

FD_SET(listener, &master); 
fdmax = listener; 

for(;;){ 
    read_fds = master; // copy it 
    if(select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1){ 
     exit(4); 
    } 

    for(i = 0; i <= fdmax; i++){ 
     if(FD_ISSET(i, &read_fds)){ 
      if(i == listener){ 
       // handle new connections 
       addrlen = sizeof remoteaddr; 
       newfd = accept(listener, (struct sockaddr *)&remoteaddr, &addrlen); 

       if(newfd == -1){ 
        perror("accept"); 
       }else{ 
        FD_SET(newfd, &master); 
        if(newfd > fdmax) { 
         fdmax = newfd; 
        } 
       } 
      }else{ 
       do{ 
        if(bytes_received >= cur_size){ 
         char * tmp; 
         cur_size += LEN; 
         tmp = realloc(buffer, cur_size); 
         buffer = tmp; 
        } 
        status = recv(i, buffer + bytes_received, LEN, 0); 
        if(status == 0){ 
         printf("Done\n"); 
        } 
        else if(status > 0){ 
         bytes_received += status; 
        } 
        else{ 
         fprintf(stderr, "socket error\n"); 
        } 
       } while (status > 0); 
       if(send(i, buffer, strlen(buffer), 0) == -1){ 
        perror("send"); 
       } 
      } 
     } 
    } 
} 
+1

Setsockopt (SOL_SOCKET, SO_NONBLOCK, ...) için herhangi bir çağrı görmüyorum, bu da soketlerinizi bloke etmeyecek. Örneğinizde atlanmış mı yoksa gerçekten programınızdan mı eksik? –

+0

"SO_NONBLOCK" olarak ayarlanmadı, select() 'nin bunu işleyeceğini düşündüm. Teşekkürler. –

+0

Eğer 'select()' herhangi bir büyülü etkiye sahip olsaydı, belgelerde böyle olurdu. – EJP

cevap

2

İlk olarak, engellemek istemiyorsanız soketi engellemeyi ayarlamalısınız. Ne yaparsanız yapın, soket işlemleriniz engelleniyorsa asla engellemeniz mümkün değildir. Bu, önemli güvenlik sonuçları olan ciddi hatalara neden olan yaygın bir hatadır. Engellememeniz durumunda, soketi bloke etmeyecek şekilde ayarlamanız gerekir.

Şimdiye kadar anladığım kadarıyla, bu noktada() 'yi seçin okunmaya hazır durumda.

, okunmaya hazır durumda idi. select işlevi, bir durum raporlama işlevidir ve diğer durum raporlama işlevleri gibi, durumu aradığınızda ve geri döndüğünde aradaki bazı noktalardan durumu bildirir. Daha sonraki bir noktada hala durumun bir garantisi yoktur.

Durumun değişebileceği diğer her türlü yolu dışlayabileceğinizi asla düşünmeyin. Tarih, onu düşünen ve yakılan insanlarla dolu.

Ama engelleniyor belirli bir neden (Tekrar recv çağıran bir do döngü var çünkü) ilk select uğramadan recv aradığınız olmasıdır.

Ne olursa olsun, recv adresinden WOULDBLOCK göstergesini doğru şekilde kullanmanız gerekir. Bu önemli. Ayrıca, recv numaralı telefonu arayarak select numaralı isabetle bir kez daha arama yapabilirsiniz. Diğer sorunları çözerseniz, önemli değil.

+0

Bunu belirtdiğiniz için teşekkürler, şimdi daha net. Hala elde edemediğim bir şey, recv'nin bir GET/HTTP/1.1 telnetini gönderdikten sonra bile neden askıda kalmasıdır? Bu, verilerin mevcut olduğunu söylesin mi? –

+0

Bunu yaparsınız, daha sonra 'recv 'yi çağırır ve verileri alırsınız. Ama sonra 'do' döngüsünü tekrar recv' çağırır. Yuva engellendiğinden, veri mevcut olana kadar engellenir. Soketi bloke etmeyecek şekilde ayarlamalısınız ve 'recv'den bir' WOULDBLOCK 'göstergesini kullanmalısınız ve bu, her durumda sorunu çözecektir. Ama aynı zamanda 'do' döngüsünü ortadan kaldırmayı ve ortak durumda problemi düzeltmek için 'select' isabetiyle yalnızca bir kez 'recv' çağırmayı da isteyebilirsiniz. –

+0

Anladım, teşekkürler efendim! –