Python简易Web服务器出现OSError: [Errno 9]错误求助
问题
用Python编写简易Web服务器,目标是通过浏览器访问HTML文件,目前功能可实现,但每次运行都会弹出OSError: [Errno 9] Bad file descriptor错误。
服务器代码
from socket import * serverPort = 1200 serverSocket: socket = socket(AF_INET, SOCK_STREAM) serverSocket.bind(('', serverPort)) serverSocket.listen(1) while True: print('Ready to serve...') connectionSocket, addr = serverSocket.accept() print('connectionSocket is:', connectionSocket) try: message = connectionSocket.recv(1024) filename: bytes = message.split()[1] f = open(filename[1:]) outputdata = f.read() connectionSocket.send('HTTP/1.1 200 OK\r\n\r\n'.encode('UTF-8')) for i in range(0, len(outputdata)): connectionSocket.send(outputdata[i].encode()) connectionSocket.send("\r\n".encode()) connectionSocket.close() except IOError: connectionSocket.send('HTTP/1.1 404 Not Found'.encode('UTF-8')) connectionSocket.send("<html><head></head><body><h1>404 Not Found</h1></body></html> ".encode('UTF-8')) serverSocket.close() sys.exit()
报错信息
Traceback (most recent call last): File "/Users/me/PycharmProjects/firstPy/proj1.py", line 15, in <module> connectionSocket, addr = serverSocket.accept() File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/socket.py", line 293, in accept fd, addr = self._accept() OSError: [Errno 9] Bad file descriptor
已知该错误通常因访问未打开的文件导致,推测是socket连接存在问题,但困惑的是仍能正常访问HTML,也排查过Python3.9的语法适配问题,未发现异常,希望得到错误原因及解决办法。
错误原因
核心问题:监听Socket被提前关闭
你把serverSocket.close()放在了while True循环内部,第一次处理完请求后,负责监听的serverSocket就被关闭了。当循环再次执行到serverSocket.accept()时,这个Socket已经处于关闭状态,对应的文件描述符无效,因此抛出Bad file descriptor错误。
第一次能正常访问HTML,是因为第一次循环时serverSocket还处于正常监听状态,能接受连接并处理请求,处理完才关闭监听Socket,第二次循环才触发错误。其他潜在问题
- 打开文件后未调用
f.close(),可能导致资源泄漏; - 异常分支中未关闭
connectionSocket,会导致客户端连接无法正常释放; - 逐个字符发送
outputdata效率极低,完全没必要循环单个字符发送。
- 打开文件后未调用
解决办法
修正后的代码
from socket import * import sys serverPort = 1200 serverSocket: socket = socket(AF_INET, SOCK_STREAM) serverSocket.bind(('', serverPort)) serverSocket.listen(1) print('Server is running on port', serverPort) while True: print('Ready to serve...') connectionSocket, addr = serverSocket.accept() print('Connected from:', addr) try: message = connectionSocket.recv(1024) # 处理空请求(比如浏览器/favicon.ico请求或异常请求) if not message: connectionSocket.close() continue filename: bytes = message.split()[1] # 打开文件时指定编码,避免读取中文等字符出现乱码 with open(filename[1:], encoding='utf-8') as f: outputdata = f.read() # 发送响应头和响应体 response_header = 'HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\n\r\n' connectionSocket.send(response_header.encode('UTF-8')) # 直接发送整个响应体,无需循环单个字符 connectionSocket.send(outputdata.encode('UTF-8')) except IOError: # 发送404响应 response = 'HTTP/1.1 404 Not Found\r\nContent-Type: text/html; charset=utf-8\r\n\r\n' response += '<html><head></head><body><h1>404 Not Found</h1></body></html>' connectionSocket.send(response.encode('UTF-8')) finally: # 无论是否异常,都关闭连接Socket connectionSocket.close() # 以下代码只有在循环被手动终止时才会执行 serverSocket.close() sys.exit()
关键修改点说明
- 将
serverSocket.close()和sys.exit()移到while True循环外部,确保监听Socket在服务器运行期间始终保持打开状态,能持续接受新的连接请求; - 使用
with open(...)语句自动管理文件资源,无需手动调用f.close(); - 增加空请求的判断,避免
message.split()抛出索引错误; - 优化响应头,添加
Content-Type指定编码,避免页面乱码; - 直接发送整个响应体,提升传输效率;
- 使用
finally块确保connectionSocket无论是否发生异常都会被关闭,避免连接资源泄漏。
内容的提问来源于stack exchange,提问作者erem
相关产品推荐
相关产品推荐

