最近编写了一个简单的小程序,qt做的界面,点击按钮后用多线程启动一个httpserver,用python解释器运行时没有任何问题,但是使用pyinstaller打包成exe后,发现点击按钮,接受到的http请求并未做处理,代码如下
httpserver的post处理如下(处理过程可忽略,特定解析用的):
class HTTPServerRequestHandler(BaseHTTPRequestHandler):
def do_POST(self):
'''
接收POST请求
:return:
'''
print("=====================================================")
print("用户提交post请求,开始处理post请求...")
querypath = urlparse(self.path)
print("当前post的url是" + querypath.path) #
post_type = querypath.path.split("/")[1] #
try: # 先执行try代码,发现的错误执行except代码,程序向下执行
post_params = querypath.path.split("/")[2] #
except:
post_params = None
self.do_post_handle(post_type, post_params) # 处理post请求
print("收到对端请求...")
print("post请求处理完成...")
print("=================================================")
def do_post_handle(self, post_type, post_params): #
print("当前处理的是[%s]" % post_type)
req_datas = self.rfile.read(int(self.headers['content-length'])).decode('utf-8')
print("post请求数据为:%s " % req_datas)
req_name = req_datas.split(',')[0]
req_name = req_name.split(':')[1]
if req_name == self.Name:
if isinstance(self.SWFile, dict):
data = []
for md, urlItem in self.SWFile.items():
urlItem = 'http://192.168.137.80:80/%s' % urlItem
data.append(
{
"ModuleID": md,
"URL": urlItem
}
)
print(data)
self.send_response(200) # 响应码200
self.send_header('Content-type', 'application/json')
self.end_headers()
self.wfile.write(json.dumps(data).encode('utf-8'))
class httpserverThread(Thread): #(使用Thread定义多线程启动入口)
def __init__(self):
super().__init__()
self.SERVER_IP = "192.168.137.80" # 本机地址
self.SERVER_PORT = 80 # 本机端口
server_address = (self.SERVER_IP, self.SERVER_PORT)
self.httpd = HTTPServer(server_address, HTTPServerRequestHandler)
def run(self):
try:
print('server started....')
self.httpd.serve_forever()
except:
print('server start error')
def stop(self):
self.httpd.shutdown()
然后在主程序中启动httpserver
ser = httpserver.httpserverThread()
ser.start()
使用python解释器运行完全没有问题,但是程序是要给别的人使用,所以使用pyinstaller打包成exe
参数是-D -w
pyinstaller -D -w main.py
实际使用中别的功能都没问题,但是httpserver使用不响应http的请求,一开始我以为是多线程未启动,但是增加了print函数后,又打包成带命令行界面的exe,即
pyinstaller -D main.py
重新调试,发现此时httpserver正常响应了,由此判定应该是由于pyinstaller的 -w参数 即 nonconsole引起的httpserver响应异常,于是网上搜索相关内容,发现pyinstaller 的-w(即nonconsole)对httpserver中的send_response有影响,如果导致程序无法往下运行,解决办法有两个
1、要么就使用带命令行的exe,但是有点丑陋,不采用
2、把send_response函数更换成send_response_only即可,如下
def do_post_handle(self, post_type, post_params): #
print("当前处理的是[%s]" % post_type)
req_datas = self.rfile.read(int(self.headers['content-length'])).decode('utf-8')
print("post请求数据为:%s " % req_datas)
req_name = req_datas.split(',')[0]
req_name = req_name.split(':')[1]
if req_name == self.Name:
if isinstance(self.SWFile, dict):
data = []
for md, urlItem in self.SWFile.items():
urlItem = 'http://192.168.137.80:80/%s' % urlItem
data.append(
{
"ModuleID": md,
"URL": urlItem
}
)
print(data)
self.send_response_only(200) # 响应码200
self.send_header('Content-type', 'application/json')
self.end_headers()
self.wfile.write(json.dumps(data).encode('utf-8'))
查看两个函数的区别,send_response需要先将内容写到缓存中,而send_response_only直接将内容输出到output stream中,看一下两个函数的源码
def send_response(self, code, message=None):
"""Add the response header to the headers buffer and log the
response code.
Also send two standard headers with the server software
version and the current date.
"""
self.log_request(code)
self.send_response_only(code, message)
self.send_header('Server', self.version_string())
self.send_header('Date', self.date_time_string())
def send_response_only(self, code, message=None):
"""Send the response header only."""
if self.request_version != 'HTTP/0.9':
if message is None:
if code in self.responses:
message = self.responses[code][0]
else:
message = ''
if not hasattr(self, '_headers_buffer'):
self._headers_buffer = []
self._headers_buffer.append(("%s %d %s\r\n" %
(self.protocol_version, code, message)).encode(
'latin-1', 'strict'))
最大的问题应该就在于,除了缓存之外,还将数据记录了log
self.log_request(code)
看一下源码
def log_request(self, code='-', size='-'):
"""Log an accepted request.
This is called by send_response().
"""
if isinstance(code, HTTPStatus):
code = code.value
self.log_message('"%s" %s %s',
self.requestline, str(code), str(size))
def log_message(self, format, *args):
message = format % args
sys.stderr.write("%s - - [%s] %s\n" %
(self.address_string(),
self.log_date_time_string(),
message.translate(self._control_char_table)))
这样一看问题应该就很明确了,对于pyinstaller nonconsole的打包方式,不带有控制台,所以应该是没有办法处处理stdin、stdout、stderr之类的内容,导致函数运行错误。所以各位老铁也可以尝试把log的内容删除,或者直接用send_response_only就行