Python提示[Errno 32]Broken pipe导致线程crash错误解决方法
本文实例讲述了Python提示[Errno32]Brokenpipe导致线程crash错误解决方法。分享给大家供大家参考。具体方法如下:
1.错误现象
ThreadingHTTPServer实现的http服务,如果客户端在服务器返回前,主动断开连接,则服务器端会报[Errno32]Brokenpipe错,并导致处理线程crash.
下面先看个例子,python版本:2.7
示例代码
#!/usr/bin/envpython
#!coding=utf-8
importos
importtime
importsocket
importthreading
fromBaseHTTPServerimportHTTPServer,BaseHTTPRequestHandler
fromSocketServerimportThreadingMixIn
classRequestHandler(BaseHTTPRequestHandler):
defdo_GET(self):
"""
处理get请求
"""
query=self.path
print"query:%sthread=%s"%(query,str(threading.current_thread()))
#ret_str="<html>"+self.path+"<br>"+str(self.server)+"<br>"+str(self.responses)+ "</html>"
ret_str="<html>"+self.path+"<br>"+str(self.server)+ "</html>"
time.sleep(5)
try:
self.send_response(200)
self.send_header('Content-type','text/html')
self.end_headers()
self.wfile.write(ret_str)
exceptsocket.error,e:
print"socket.error:Connectionbroke.Aborting"+str(e)
self.wfile._sock.close() #closesocket
self.wfile._sock=None
returnFalse
print"successprodquery:%s"%(query)
returnTrue
#多线程处理
classThreadingHTTPServer(ThreadingMixIn,HTTPServer):
pass
if__name__=='__main__':
serveraddr=('',9001)
ser=ThreadingHTTPServer(serveraddr,RequestHandler)
ser.serve_forever()
sys.exit(0)运行服务
./thread_http_server_error.py
第1次curl,等待返回
[~]$curl-s'http://10.232.41.142:9001/hello1′ <html>/hello1<br><__main__.ThreadingHTTPServerinstanceat0x37483b0></html>[~]$ 此时服务器端输出日志如下: $./thread_http_server_error.py query:/hello1thread= search041142.sqa.cm4.tbsite.net–-[15/May/201415:02:27]“GET/hello1HTTP/1.1″200- successprodquery:/hello1
第2次curl,不等待返回,ctrl+C来模拟客户端断开
[~]$curl-s'http://10.232.41.142:9001/hello2′ [~]$ctrl+C
此时服务器端输出日志如下:
query:/hello2thread=
search041142.sqa.cm4.tbsite.net–-[15/May/201415:33:10]“GET/hello2HTTP/1.1″200-
socket.error:Connectionbroke.Aborting[Errno32]Brokenpipe
—————————————-
Exceptionhappenedduringprocessingofrequestfrom('10.232.41.142′,48769)
Traceback(mostrecentcalllast):
File“/home/wuzhu/tools/python_2_7_1/lib/python2.7/SocketServer.py”,line582,inprocess_request_thread
self.finish_request(request,client_address)
File“/home/wuzhu/tools/python_2_7_1/lib/python2.7/SocketServer.py”,line323,infinish_request
self.RequestHandlerClass(request,client_address,self)
File“/home/wuzhu/tools/python_2_7_1/lib/python2.7/SocketServer.py”,line639,in__init__
self.handle()
File“/home/wuzhu/tools/python_2_7_1/lib/python2.7/BaseHTTPServer.py”,line337,inhandle
self.handle_one_request()
File“/home/wuzhu/tools/python_2_7_1/lib/python2.7/BaseHTTPServer.py”,line326,inhandle_one_request
self.wfile.flush()#actuallysendtheresponseifnotalreadydone.
File“/home/wuzhu/tools/python_2_7_1/lib/python2.7/socket.py”,line303,inflush
self._sock.sendall(view[write_offset:write_offset+buffer_size])
AttributeError:'NoneType'objecthasnoattribute'sendall'
2.原因分析
“[Errno32]Brokenpipe“产生的原因还是比较明确的,由于client在服务器返回前主动断开连接,所以服务器在返回时写socket收到SIGPIPE报错。虽然在我们的程序中也对异常进行了处理,将handler的wfile._sock对象close掉,但python的库里BaseHTTPServer.py中BaseHTTPRequestHandler类的成员函数handle_one_request还是会直接调用wfile.flush,而没有判断wfile是否已经close。
defhandle_one_request(self):
"""HandleasingleHTTPrequest.
Younormallydon'tneedtooverridethismethod;seetheclass
__doc__stringforinformationonhowtohandlespecificHTTP
commandssuchasGETandPOST.
"""
try:
self.raw_requestline=self.rfile.readline(65537)
iflen(self.raw_requestline)>65536:
self.requestline=''
self.request_version=''
self.command=''
self.send_error(414)
return
ifnotself.raw_requestline:
self.close_connection=1
return
ifnotself.parse_request():
#Anerrorcodehasbeensent,justexit
return
mname='do_'+self.command
ifnothasattr(self,mname):
self.send_error(501,"Unsupportedmethod(%r)"%self.command)
return
method=getattr(self,mname)
method()
#没有判断wfile是否已经close就直接调用flush()
self.wfile.flush()#actuallysendtheresponseifnotalreadydone.
exceptsocket.timeout,e:
#areadorawritetimedout. Discardthisconnection
self.log_error("Requesttimedout:%r",e)
self.close_connection=1
return3.解决办法
只要在RequestHandler重载其基类BaseHTTPRequestHandler的成员函数handle_one_reques(),在调用wfile.flush()前加上wfile是否已经close即可。
#!/usr/bin/envpython #!coding=utf-8
importos importtime importsocket importthreading fromBaseHTTPServerimportHTTPServer,BaseHTTPRequestHandler fromSocketServerimportThreadingMixIn
classRequestHandler(BaseHTTPRequestHandler): defhandle_one_request(self): """HandleasingleHTTPrequest. Younormallydon'tneedtooverridethismethod;seetheclass __doc__stringforinformationonhowtohandlespecificHTTP commandssuchasGETandPOST. """ try: self.raw_requestline=self.rfile.readline(65537) iflen(self.raw_requestline)>65536: self.requestline='' self.request_version='' self.command='' self.send_error(414) return ifnotself.raw_requestline: self.close_connection=1 return ifnotself.parse_request(): #Anerrorcodehasbeensent,justexit return mname='do_'+self.command ifnothasattr(self,mname): self.send_error(501,"Unsupportedmethod(%r)"%self.command) return method=getattr(self,mname) print"beforecalldo_Get" method() #增加debuginfo及wfile判断是否已经close print"aftercalldo_Get" ifnotself.wfile.closed: self.wfile.flush()#actuallysendtheresponseifnotalreadydone. print"afterwfile.flush()" exceptsocket.timeout,e: #areadorawritetimedout. Discardthisconnection self.log_error("Requesttimedout:%r",e) self.close_connection=1 return defdo_GET(self): """ 处理get请求 """ query=self.path print"query:%sthread=%s"%(query,str(threading.current_thread())) ret_str="<html>"+self.path+"<br>"+str(self.server)+ "</html>" time.sleep(5) try: self.send_response(200) self.send_header('Content-type','text/html') self.end_headers() self.wfile.write(ret_str) exceptsocket.error,e: print"socket.error:Connectionbroke.Aborting"+str(e) self.wfile._sock.close() self.wfile._sock=None returnFalse print"successprodquery:%s"%(query) returnTrue #多线程处理 classThreadingHTTPServer(ThreadingMixIn,HTTPServer): pass if__name__=='__main__': serveraddr=('',9001) ser=ThreadingHTTPServer(serveraddr,RequestHandler) ser.serve_forever() sys.exit(0)