多线程服务器执行curl命令时频繁出现对等方拒绝连接问题
问题排查:多线程Python HTTP服务器批量请求出现「对等方拒绝连接」错误
问题描述
基于Python实现的多线程HTTP服务器,在本地多浏览器标签页访问时运行正常,能正常显示线程ID;但在另一设备执行批量curl命令时,80%的请求会返回「connection refused by peer」错误。
批量curl命令:
for i in {1..10} do curl "http://(输入IP):8080/home2.html" & done
服务器代码:
# import socket module from socket import * import threading from datetime import datetime # In order to terminate the program import sys # Prepare a sever socket serverSocket = socket(AF_INET, SOCK_STREAM) serverSocket.bind(('', 8080)) serverSocket.listen(1) def handle(connectionSocket): try: message = connectionSocket.recv(4096) filename = message.split()[1] f = open(filename[1:]) outputdata = f.read() f.close() print(f"server-response,{threading.get_ident()},{datetime.now()}") # Send one HTTP header line into socket header = 'HTTP/1.1 200 OK\r\n' + 'Content-Length: ' + str(len(outputdata)) + '\r\n' + 'Content-Type: text/html\r\n\r\n' encodedMessage = (outputdata + '\r\n').encode() connectionSocket.send(header.encode()) connectionSocket.sendall(encodedMessage) # Close client socket connectionSocket.close() except IOError: # Send response message for file not found connectionSocket.send(("HTTP/1.1 404 Not Found\r\n\r\n").encode()) # Close client socket connectionSocket.close() while True: # Establish the connection print('Ready to serve...') connectionSocket, addr = serverSocket.accept() t = threading.Thread(target=handle,args=(connectionSocket,),daemon=True) t.start() # Close server socket serverSocket.close() # Terminate the program after sending the corresponding data sys.exit()
原因分析
核心问题在于监听套接字的backlog参数设置过小:
- 代码中
serverSocket.listen(1)将TCP监听队列的长度设为1,这个队列用于存放已经完成三次握手但还未被accept()处理的连接。 - 当批量并发请求(如后台执行的10个curl)同时到达时,TCP监听队列会迅速被占满,后续的连接请求会被内核直接拒绝,返回「connection refused」错误。
- 本地浏览器访问时,请求并非严格同时发起,队列有足够时间处理,因此不会触发该问题。
此外还有次要潜在问题:
- 异常处理中直接调用
connectionSocket.send()未做错误判断,如果连接已断开,会抛出未捕获的异常。 - 未正确处理
message.split()可能出现的索引越界(如空请求)。
解决方法
1. 增大监听队列的backlog参数
修改listen()的参数,设置一个合理的数值(根据并发需求调整,比如10或更大):
serverSocket.listen(10)
这个参数定义了内核为监听套接字维护的未完成连接队列的最大长度,足够大的数值可以应对批量并发请求。
2. 完善异常处理与边界情况
- 增加对空请求的判断,避免
message.split()[1]索引越界:
message = connectionSocket.recv(4096) if not message: connectionSocket.close() return parts = message.split() if len(parts) < 2: connectionSocket.send(("HTTP/1.1 400 Bad Request\r\n\r\n").encode()) connectionSocket.close() return filename = parts[1]
- 对send操作添加异常捕获,避免连接断开时抛出未处理异常:
try: connectionSocket.send(header.encode()) connectionSocket.sendall(encodedMessage) except Exception as e: pass connectionSocket.close()
3. 可选:使用上下文管理器管理套接字
用with语句自动管理套接字的关闭,避免手动关闭遗漏:
def handle(connectionSocket): with connectionSocket: try: # 原有处理逻辑 except IOError: # 404处理逻辑
内容的提问来源于stack exchange,提问作者bigolcs
相关产品推荐
相关产品推荐

