2011-04-03 126 views
1

我有一個標準的分叉TCPServer設置,接收傳入的請求並將文件發送回客戶端。服務器似乎在發送所有數據,但我已經檢查過客戶端收到的字節!=發送的字節。如何保持Python服務器連接打開,直到客戶端完成它?

經過進一步調查,接收方客戶端指出服務器提前關閉連接 - 導致接收失敗。

然後我修改了服務器在發送文件後幾秒鐘睡覺 - 保持套接字打開足夠長的時間,以便客戶端接收並關閉它。這種方法很有效,但在我看來這很不方便,因爲很難預測線程在關閉套接字之前應該睡多久。

我曾嘗試設置SO_LINGER服務器端,以保持連接活着,但它並沒有幫助 - 即使我認爲它應該。

在客戶端完全接收文件之前,必須有更好的方法來阻止。我需要做什麼才能保證套接字在客戶端收到所有數據之前不會關閉?

服務器

class ForkingTCPRequestHandler(SocketServer.BaseRequestHandler): 
    def createSPP(self, dataLen, success): 
     SPPStruct = struct.Struct('I?') 
     values = (socket.htonl(dataLen), success,) 
     packed_data = SPPStruct.pack(*values)  
     return packed_data 

    def handle(self): 
     """Enabling SO_LINGER to keep connection alive doesn't help""" 
     self.request.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 5)) 

     """Send a packet to the client so it knows the length of incoming data""" 
     spp = self.createSPP(os.path.getsize(FILE_NAME), 1) 
     self.request.sendall(spp) 

     """Sending the file, finish() is automatically called after this.""" 
     f = open(FILE_NAME, 'rb') 
     fileData = f.read() 
     self.request.sendall(fileData) 
     f.close() 

    def finish(self): 
     """Sleep until the file is fully received by the client. 
     Sleeping keeps the connection open. BaseRequestHandler automatically 
     closes the connection when finish() returns. This works but is not a 
     robust solution.""" 
     time.sleep(5) 

class ForkingTCPServer(SocketServer.ForkingMixIn, SocketServer.TCPServer): 
    pass 

if __name__ == '__main__': 
    try: 
     server = ForkingTCPServer((HOST, PORT), ForkingTCPRequestHandler) 
    except socket.error as e: 
     sys.exit(1) 

    try: 
     server.serve_forever() 
    except KeyboardInterrupt: 
     server.shutdown() 
     sys.exit(0) 

客戶端連接到服務器

// Establishes a standard TCP connection 
    memset(&targetAddr, 0, sizeof(targetAddr)); 
    targetAddr.sin_family = AF_INET; 
    targetAddr.sin_port = htons(atoi(port)); 
    bcopy(hostdetails->h_addr, (char *)&targetAddr.sin_addr, hostdetails->h_length); 

    sock = socket(AF_INET, SOCK_STREAM, 0); 
    if (socket < 0) { 
      return -1; 
    } 

    rc = connect(sock, (struct sockaddr *)&targetAddr, sizeof(targetAddr)); 
    if (rc < 0) { 
      close(sock); 
      return -1; 
    } 

客戶端接收

// Receiving spp (server side) known as symProcPacket (client side) 
    // symProcPacket contains the length of the file that will be sent next 
    // Receiving this packet is always successful 
    typedef struct SymProcessPacket { 
     u_int32_t totalDataLen; 
     BOOL processingSuccessful; 
    } SymProcessPacket; 

    tempBuf = (char *)malloc(sizeof(SymProcessPacket)); 
    recvBytes = recv(s, tempBuf, sizeof(SymProcessPacket), 0); 
    if (recvBytes < 0) { 
     goto processingError;  
    } 

    memcpy(&symProcPacket, tempBuf, sizeof(SymProcessPacket)); 
    free(tempBuf); 

    // Receiving the file 
    // Receive chunks and put in a buffer until entire file is received 
    tempBuf = (char*) malloc(sizeof(char)*ntohl(symProcPacket.totalDataLen)); 
    totalRecv = 0; 
    recvBytes = 0; 

    while (totalRecv < ntohl(symProcPacket.totalDataLen)) { 
     recvBytes = recv(sock, tempBuf+totalRecv, (1<<14), 0); 
     if (recvBytes < 0) { 
      // RecvBytes returns -1, which is an error before getting all the data 
      // It gets a "Connection was reset by peer" error here, unless the server 
      // sleeps for a bit. It means the server closed the connection early. 
      printf("Error: %s", errtostr(errno)); 
      goto errorImporting; 
     } 
     totalRecv += recvBytes; 
    } 
+0

sendall將已經阻塞。你的代碼適合我。你怎麼試圖連接到這個? – 2011-04-03 23:28:12

+0

是的,socket.sendall()併發送套接字。send()在技術上應該阻塞,但是當我接收數據時,我仍然收到「Connection was peer by peer」錯誤客戶端 - 因爲某些原因,服務器在客戶端獲得所有內容之前關閉連接數據。 – dpham 2011-04-04 00:23:19

+0

什麼函數實際返回一個錯誤? – 2011-04-04 00:29:11

回答

0

我不明白爲什麼睡覺解決了任何問題。

但我認爲python在發送5個字節和C++正在讀取8個字節。 python中的?需要字節。布爾我相信是typedefed作爲一個整數,可能需要4個字節。

一般來說讀/寫結構到插座是不鼓勵的。

1

我跳過你的代碼部分。我會一直關注你的問題,收到完整的文件。一種很酷的方式是採用HTTP方式。首先,發送要發送的字節數量,以便接收端從套接字接收的數據量最大。所以,僅僅發送一些額外的數據,接收器將做的工作..

發件人:

  • 發送文件的大小要發送
  • 發送文件數據

接收器:

  • 接收文件大小
  • Rec eive數據直到len(數據)== size_received
相關問題