我有一個標準的分叉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;
}
sendall將已經阻塞。你的代碼適合我。你怎麼試圖連接到這個? – 2011-04-03 23:28:12
是的,socket.sendall()併發送套接字。send()在技術上應該阻塞,但是當我接收數據時,我仍然收到「Connection was peer by peer」錯誤客戶端 - 因爲某些原因,服務器在客戶端獲得所有內容之前關閉連接數據。 – dpham 2011-04-04 00:23:19
什麼函數實際返回一個錯誤? – 2011-04-04 00:29:11