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 return
3.解决办法
只要在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)