2013-05-25 16 views
41

Temel olarak, socket.recv()'un okuyabildiği her şeyi veya diğer tarafın kapatıldığını belirten boş bir dize döneceğini birkaç yerde okudum (resmi dokümanlar, bağlantı kapatıldığında geri döndüklerinden bile bahsetmiyor bile ... harika!). Bu, aslında, gerçekten bir şey almak için bir şey olduğunda recv() döndürdüğünü biliyoruz, çünkü soketleri engellemek için iyi ve dandy, bu yüzden boş bir dize döndürdüğünde, ZORUNLU diğer tarafın bağlantıyı kapattığı anlamına gelir, değil mi?Zaman aşımı oluşana kadar veri alınmazsa, Python'un socket.recv() bloğu engelleme olmayan soketler için ne döndürür?

Tamam, peki ya benim soketim bloke olmadığında ne olur? Biraz aradım (belki de yeterli değil, kim bilir?) Ve diğer tarafın bloke edici olmayan bir soket kullanarak bağlantıyı ne zaman kapattığını anlayamıyorum. Bize bunu söyleyen bir yöntem veya özellik yok gibi görünüyor ve boş dize recv() dönüş değeri karşılaştırmak kesinlikle işe yaramaz gibi görünüyor ... sadece bu sorun mu yaşıyorsunuz?

Basit bir örnek olarak, benim soketimizin zaman aşımının 1.2342342 olarak ayarlandığını (burada hangi negatif olmayan numara olursa olsun) saniyelerle ve socket.recv(1024) numaralı telefonu aradığımı, ancak diğer tarafın 1.2342342 ikinci dönem boyunca hiçbir şey göndermediğini varsayalım. Eğer select bağlantılı olarak recv kullandığınızda soket okunan hazırdır ama varsa

cevap

51

: verileri alır

def listenToSockets(self): 

    while True: 

     changed_sockets = self.currentSockets 

     ready_to_read, ready_to_write, in_error = select.select(changed_sockets, [], [], 0.1) 

     for s in ready_to_read: 

      if s == self.serverSocket: 
       self.acceptNewConnection(s) 
      else: 
       self.readDataFromSocket(s) 

Ve işlevi hiçbir veri bulunmayan bir bloke edici soket, recv socket.error istisnasını atar ve istisnanın değeri EAGAIN ya da EWOULDBLOCK hatası verir. Örnek:

import sys 
import socket 
import fcntl, os 
import errno 
from time import sleep 

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
s.connect(('127.0.0.1',9999)) 
fcntl.fcntl(s, fcntl.F_SETFL, os.O_NONBLOCK) 

while True: 
    try: 
     msg = s.recv(4096) 
    except socket.error, e: 
     err = e.args[0] 
     if err == errno.EAGAIN or err == errno.EWOULDBLOCK: 
      sleep(1) 
      print 'No data available' 
      continue 
     else: 
      # a "real" error occurred 
      print e 
      sys.exit(1) 
    else: 
     # got a message, do something :) 

durum size socket.settimeout(n) veya socket.setblocking(False) ile bir anda dışarı aracılığıyla engellenmeyen davranışı etkinleştirdikten durum için biraz farklıdır. Bu durumda bir socket.error stili yükseltilir, ancak bir zaman aşımı durumunda, istisnanın eşlik eden değeri her zaman 'zaman aşımına' ayarlanmış bir dizedir. dışı blockng moduna soketi koymak OS özgü işlevsellik bağlı değildir çünkü açıklamalarda belirtildiği gibi

import sys 
import socket 
from time import sleep 

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
s.connect(('127.0.0.1',9999)) 
s.settimeout(2) 

while True: 
    try: 
     msg = s.recv(4096) 
    except socket.timeout, e: 
     err = e.args[0] 
     # this next if/else is a bit redundant, but illustrates how the 
     # timeout exception is setup 
     if err == 'timed out': 
      sleep(1) 
      print 'recv timed out, retry later' 
      continue 
     else: 
      print e 
      sys.exit(1) 
    except socket.error, e: 
     # Something else happened, handle error, exit, etc. 
     print e 
     sys.exit(1) 
    else: 
     if len(msg) == 0: 
      print 'orderly shutdown on server end' 
      sys.exit(0) 
     else: 
      # got a message do something :) 

, bu da bir daha taşınabilir bir çözümdür: Yani, yapabileceğiniz bu durumun ele alınması. Daha fazla ayrıntı için recv(2) ve python socket numaralı telefon numaralarına bakın.

+0

Tamam, bu tam olarak ne bilmek gerekiyordu. Teşekkür ederim! Bu soruyu sormak zorunda kalmazdım, sadece resmi dokümanlar bu yöntemle bir istisnanın kaldırılmasından bahsetmiş olsa bile ... biraz da doktorlarla hayal kırıklığına uğramıştı :(ama cevabınızdan çok memnunum :) başparmak yukarıya! –

+1

Kodunuz hakkında sadece bir not. Soketi daha fazla Pythonic ve cross-platformda (ve C-stili bayraklar olmadan okunabilir imho) bloke etmeyecek şekilde ayarlamak, basitçe 's.settimeout (whatever_nonnegative_number_of_your_liking)' ' –

+0

’u çağırmaktır. sorumu sadece yarim cevaplar. Şey, bir zaman aşımı tanımlandığında ve 'recv()' bir zaman aşımından sonra veri olmadan başarısız olduğunda, bir 'socket.timeout' istisnası (' socket.error' değil) yükseltilir. Zaman aşımı istisnası hala bağlantının durumu hakkında bir şey yapmama izin vermiyor. Bağlantı kapandığında 'socket.error' olduğunu varsayalım. Bunu onaylayan var mı? –

4

... recv() çağrı boş bir dize döndürür ve ben bağlantısı hala ayakta olup olmadığı ya da olmasın hiçbir ipucu var okunacak veri yok, müşterinin bağlantıyı kapattığı anlamına gelir.

İşte, bunu işleyen bazı kod da, while döngüsünde recv ikinci kez çağrıldığında atılan istisnayı da unutmayın. Bu durum okumak için sol şey yoksa o müşteri anlamına gelmez atılacaktır bağlantıyı kapattı: durumunda

def readDataFromSocket(self, socket): 

    data = '' 
    buffer = '' 
    try: 

     while True: 
      data = socket.recv(4096) 

      if not data: 
       break 

      buffer += data 

    except error, (errorCode,message): 
     # error 10035 is no data available, it is non-fatal 
     if errorCode != 10035: 
      print 'socket.error - ('+str(errorCode)+') ' + message 


    if data: 
     print 'received '+ buffer 
    else: 
     print 'disconnected' 
+0

Karşılaştığım durum budur; Select.poll kullanıyoruz ve anketin kapatıldığını görmek için anketin okunması gerektiğini gösteren istemci soketine dönüp dönmeyeceğini merak ediyordum. Sanırım bu da soruydu ve cevabınız, anketin okunacak soketi seçeceğini ve socket.recv'yi yapabilirim ve bağlantının kapalı olup olmadığını kontrol etmek için herhangi bir veri olup olmadığını kontrol eder –

4

Basittir: recv() 0 bayt döndürürse; you will not receive any more data on this connection. Ever. Hala gönderebileceksiniz.

Bu, engelleme olmayan soketinizin herhangi bir veri yoksa ancak bağlantının hala devam ediyorsa (diğer uç da gönderilebilir) bir istisna (sisteme bağlı olabilir) olması gerektiği anlamına gelir.

+0

Cevabınız için teşekkürler. Güzel ve basit. Dokümanları okudum ama beynim bir şekilde bu bölümü görmezden geliyor gibiydi: D Son olarak, biraz düşünmeden sonra, 'recv()' nin bağlantı kopmadığı sürece boş bir dizge döndürmeyeceğini fark ettim. engelleme olmayan mod 'recv()', zaman aşımı süresi boyunca veri bulunmadığında 'socket.timeout' artar. Tekrar teşekkürler! :) –

2

Sadece mevcut cevapları tamamlamak için using select instead of nonblocking sockets öneririm. Önemli olan, kilitlenmeyen soketlerin (belki de gönderme hariç) maddeleri karmaşıklaştırmasıdır, bu yüzden bunları kullanmak için hiçbir neden olmadığını söyleyebilirim. Uygulamanızın IO'nun beklemesini engellediği konusunda düzenli olarak sorun yaşıyorsanız, IO'yu arka planda ayrı bir iş parçacığında yapmayı da düşünebilirsiniz.

+0

Başka bir şey yapmak için ihtiyacınız olduğunda, iş parçacığınızın (belki de süresiz) engellenmesi de zorlaştırır ... ve select() işaretlendikten sonra bile bir soket işlemi engellenmesi mümkündür (en azından Linux altında) olmaz :( –