zl程序教程

您现在的位置是:首页 >  后端

当前栏目

Python提示[Errno32]Brokenpipe导致线程crash错误解决方法

Python错误方法线程 解决 提示 导致 Crash
2023-06-13 09:15:31 时间

本文实例讲述了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)


运行服务
./thread_http_server.py
curl,不等待返回,ctrl+C来模拟客户端断开
复制代码代码如下:[~]$curl-s"http://10.232.41.142:9001/hello2"
[~]$ctrl+C
此时服务器端输出日志如下:
复制代码代码如下:$./thread_http_server.pybeforecalldo_Get
query:/hello2thread=<Thread(Thread-1,started1103210816)>
search041142.sqa.cm4.tbsite.net--[15/May/201415:54:09]"GET/hello2HTTP/1.1"200-
socket.error:Connectionbroke.Aborting[Errno32]Brokenpipe
aftercalldo_Get
afterwfile.flush()
希望本文所述对大家的Python程序设计有所帮助。