我是一名新的Python学习者,我想实现一个可以处理多线程的简单Web服务器。如果我不对行conn.close()
进行注释,则一切正常。如果我对conn.close()
发表评论,则会出现问题。客户端可以在第一个请求后成功获得响应,但是当我刷新网页时,浏览器将无法接收响应。有谁能告诉我如何解决这个问题?我的代码有问题吗?
import socket
import threading
import time
class MyServer:
def __init__(self, port, doc_root):
self.port = port
self.doc_root = doc_root
self.host = "localhost"
def run(self):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as soc:
soc.bind((self.host, self.port))
soc.listen(5)
req = b''
while True:
conn, addr = soc.accept()
threading.Thread(target=self.handle_connection, args=(conn, addr)).start()
def handle_connection(self, conn, addr):
assert isinstance(conn, socket.socket)
req = b''
while b'\r\n\r\n' not in req:
req += conn.recv(1024)
print(addr)
print(req.decode())
time.sleep(0.5)
conn.sendall(b'HTTP/1.1 200 OK\r\n\r\nHello World')
print(addr, 'response sent')
# conn.close()
if __name__ == '__main__':
input_port = 8006
input_doc_root = r'/'
server = MyServer(input_port, input_doc_root)
server.run()
答案 0 :(得分:0)
您需要conn.close()
,因为在没有服务器关闭连接的情况下刷新浏览器会导致该请求沿着该相同的连接发送。发送一个响应后,句柄结束;在代码中的第一次请求-响应对话之后,根本没有读取和写入该套接字的信息。
另一方面,至少在我测试过的浏览器上,我在客户端省略conn.close()
时也遇到了一些问题。由于您不会告诉客户什么时候可以停止从其侧面的套接字读取数据,因此即使呈现完整的响应后,FF也会继续读取其端点。
如果您将recv
和sendall
放入某个循环中,并向客户端提示何时完全接收到数据,它将起作用:
def handle_connection(self, conn, addr):
assert isinstance(conn, socket.socket)
while conn:
req = b''
while b'\r\n\r\n' not in req:
req += conn.recv(1024)
print(addr)
print(req.decode())
time.sleep(0.5)
# Note Content-Lenght in the 'header' below
# to let the clients know when to stop reading
conn.sendall(b'HTTP/1.1 200 OK\r\nContent-Length: 11\r\n\r\nHello World')
print(addr, 'response sent')
# conn.close()
但是,恕我直言,在发送响应后关闭连接是正确的做法。您不能依靠客户端始终正确地关闭连接,因此您可能会浪费资源以有效地进行过时的会话。通过上面概述的解决方案,线程倾向于停留一段时间,基本上什么也没有等待。
最后,HTTP是无状态的,因此除了发送响应之外,维持连接没有任何实际意义。