2010-10-02 73 views
0

我是套接字編程的初學者,需要您的幫助。我使用Python文檔中的ThreadedTCPServer示例實現了簡單的回顯服務器。它工作正常,但我有以下問題:使用SocketServer實現網絡服務器的問題

  1. 當客戶端嘗試發送零長度數據時,服務器掛起(在socket.recv中)。
  2. 當客戶端發送的數據是BUF_SIZE的倍數時,服務器掛起(在socket.recv中)。
  3. 我不知道從外面停止服務器的正確方法。我想要一個腳本,例如stopServer.py,當需要停止服務器時,它可以從服務器的主機啓動。我實現了發送到服務器端口的「STOP」命令。這種方法對我來說看起來很不錯,但存在安全風險。請注意,我需要一個跨平臺的解決方案。所以,信號可能不適合。

如果您對如何解決上述問題有任何建議,我將不勝感激。 下面列出了該示例的代碼。

祝您有美好的一天! Zakhar

client.py

import sys 
import socket 
from common import recv 

if len (sys.argv) == 1 : 
    print "The file to be sent is not specified" 
    sys.exit (1) 
fname = sys.argv [1] 

print "Reading '%s' file..." % fname, 
f = open (sys.argv [1]) 
msg = f.read() 
f.close() 
print "OK" 

if len (msg) == 0 : 
    print "Nothing to send. Exit." 
    sys.exit (0) 

print "Client is sending:" 
print msg 
print "len (msg)=%d" % len (msg) 

sock = socket.socket (socket.AF_INET, socket.SOCK_STREAM) 
sock.connect (('localhost', 9997)) 
sock.sendall (msg) 
print "Sending has been finished" 
print "Waiting for response..." 
response = recv (sock) 
print "Client received:" 
print response 
print "len (response)=%d\n" % len (response) 
sock.close() 

server.py

import threading 
import SocketServer 
from common import recv 

class ThreadedTCPRequestHandler (SocketServer.BaseRequestHandler): 

    def handle(self) : 
     recvData = recv (self.request) 
     print "NEW MSG RECEIVED from %s" % self.client_address [0] 
     print "Data:" 
     print recvData 
     print "dataSize=%d" % len (recvData) 
     if recvData == "!!!exit!!!" : 
      print 'Server has received exit command. Exit.' 
      self.server.shutdown() 
     else : 
      curThread = threading.currentThread() 
      response = "%s: %s" % (curThread.getName(), recvData) 
      self.request.sendall (response) 

class ThreadedTCPServer (SocketServer.ThreadingMixIn, SocketServer.TCPServer) : 
    pass 

if __name__ == "__main__": 
    HOST, PORT = "localhost", 9997 

    server = ThreadedTCPServer ((HOST, PORT), ThreadedTCPRequestHandler) 
    server.serve_forever() 

common.py

''' 
Common code for both: client and server. 
''' 

def recv (sock) : 
    ''' 
    Reads data from the specified socket and returns them to the caller. 
    IMPORTANT: 
    This method hangs in socket.recv() in the following situations (at least 
    on Windows): 
    1. If client sends zero-length data. 
    2. If data sent by client is multiple of BUF_SIZE. 
    ''' 
    BUF_SIZE = 4096 
    recvData = "" 
    while 1 : 
     buf = sock.recv (BUF_SIZE) 
     if not buf : 
      break 
     recvData += buf 
     if len (buf) < BUF_SIZE : # all data have been read 
      break 
    return recvData 

回答

2

我不知道蟒蛇,但我會盡量根據回答我的socket編程知識。

零字節消息不會導致「sock.recv」出來。有關說明,請參閱here。如果sock.recv返回長度爲0,則表示另一端已斷開TCP連接。

if len (buf) < BUF_SIZE 

這條線如何確定已讀取所有數據。 TCP recv呼叫不是TCP發送呼叫之間的1-1映射,即可以在多個recv呼叫中接收客戶端發送的1發送呼叫。

當您發送多個BUF_SIZE即可以說2 * BUF_SIZE時,您可能會一次接收所有這一切。在這一點上,你的服務器將掛起爲len(buf) > BUF_SIZE,你會卡在sock.recv中。嘗試&找出如何使用非阻塞套接字。

+0

感謝您的回覆。關於「如果len(buf) BUF_SIZE」是不可能的情況。有關詳細信息,請參閱socket.recv python doc。 – Zakhar 2010-10-05 06:26:44

+0

recv調用通常返回由BUF_SIZE限制的套接字上可用的任何數據(在您的情況下)。正如我之前所說的,在TCP中,如果您在一個recv中發送BUF_SIZE數據,則無法保證您將通過一次recv呼叫來收回它們。 – 2010-10-05 14:23:06

0

一些實驗後,我發現以下幾點:

  1. 需要重命名common.recv到common.recvall
  2. 需要註釋中common.recv以下2行(錯誤):

    if len (buf) < BUF_SIZE : # all data have been read 
    break 
    
  3. 本示例僅在發送到服務器的數據可以通過服務器端的1個socket.recv調用讀取時纔有效。所以,發送的數據必須小於BUF_SIZE。否則,客戶端和服務器在sock.recv調用中都處於死鎖狀態。這是因爲對共享資源(套接字)的併發訪問。爲了解決這個問題,客戶端必須在不同的端口上收聽回覆。