Почему поведение файлового объекта, подключенного к сокетам, отличается на стороне клиента и сервера.
Предпосылки: мы работаем над созданием графической библиотеки, в которой есть сетевой компонент, в котором сервер может выполнять абстракцию данных с интенсивными вычислениями и отправлять только соответствующие данные клиенту. Поскольку мы хотим отправлять произвольные структуры данных, мы используем pickle для преобразования экземпляров объектов Python в строки, которые отправляются через соединение с сокетом.
Код:
import socket
import threading
import SocketServer
import pickle
class Signal():
allowed_typ = ['status','request','answer']
def __init__(self,header,content) :
if header in Signal.allowed_typ:
self.header = header
else:
print('unknown header')
raise UserWarning
self.content = content
class ThreadedTCPRequestHandler(SocketServer.StreamRequestHandler):
def __init__(self,*args, **kwargs):
SocketServer.StreamRequestHandler.__init__(self,*args, **kwargs)
def handle(self):
# self.rfile is a file-like object created by the handler;
# we can now use e.g. readline() instead of raw recv() calls
## self.data = self.rfile.readline()#.strip()
data = pickle.load(self.rfile)
if data.header in Signal.allowed_typ:
if data.header == 'answer':
answer = Signal('answer',data.content + " pong ")
pickle.dump(answer,self.wfile)
data = pickle.load(self.rfile)
answer = Signal('answer',data.content + " pong ")
pickle.dump(answer,self.wfile)
else :
print('server received unknown signal')
self.rfile.close()
self.wfile.close()
class ThreadedTCPServer(SocketServer.TCPServer):
def __init__(self,*args,**kwargs):
SocketServer.TCPServer.__init__(self,*args,**kwargs)
self.status = True
if __name__ == "__main__":
# Port 0 means to select an arbitrary unused port
HOST, PORT = "localhost", 0
server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
ip, port = server.server_address
print('server running at:',ip,':',port)
# Start a thread with the server -- that thread will then start one
# more thread for each request
server_thread = threading.Thread(target=server.serve_forever)
# Exit the server thread when the main thread terminates
server_thread.daemon = True
server_thread.start()
print("Server loop running in thread:", server_thread.name)
# sending stuff to the server
sig = Signal('answer',' ping ')
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((ip, port))
wf = sock.makefile(mode='wb')
rf = sock.makefile(mode='rb')
print('CLIENT sending \"',sig.content, '\" to server:',ip,':',port )
pickle.dump(sig,wf)
wf.close() # <----- This line
print('CLIENT sent ',sig.content)
try :
response = pickle.load(rf)
print("CLIENT Received: {} of type {} and content {}".format(response.header,type(response.content),response.content))
except EOFError :
print('no answer received')
rf.close() # <----- This line
sig.content = response.content + ' ping '
wf = sock.makefile(mode='wb')
rf = sock.makefile(mode='rb')
print('CLIENT sending \"',sig.content, '\" to server:',ip,':',port )
pickle.dump(sig,wf)
wf.close() # <----- This line
print('CLIENT sent ',sig.content)
try :
response = pickle.load(rf)
print("CLIENT Received: {} of type {} and content {}".format(response.header,type(response.content),response.content))
except EOFError :
print('no answer received')
rf.close() # <----- This line
server.shutdown()
server.server_close()
Вопрос: Почему я должен закрывать файловые объекты на стороне клиента, в то время как на стороне сервера я могу оставить их открытыми, пока не будет выполнено несколько циклов обмена данными? Соответствующие строки отмечены как "#<----- Эта строка". Если я прокомментирую одну из них, программа зависнет, потому что pickle ожидает EOF. Каков рекомендуемый вариант использования в этом сценарии? Я закрываю объект файла после каждого дампа? Почему это поведение не симметрично на стороне сервера? Как я понимаю из рассмотрения кода сокета, сокет остается открытым в любом случае, пока все объекты файла не будут закрыты.
Большое спасибо за ваше объяснение того, что происходит. С уважением. Фил