2008-10-07 99 views
43

Tôi đã viết một máy chủ trò chơi đa luồng đơn giản trong python tạo ra một chuỗi mới cho mỗi kết nối máy khách. Tôi thấy rằng tất cả bây giờ và sau đó, máy chủ sẽ sụp đổ vì một lỗi-ống/SIGPIPE lỗi. Tôi khá chắc chắn nó đang xảy ra khi chương trình cố gắng gửi một phản hồi lại cho một khách hàng không còn hiện diện nữa.Làm thế nào để xử lý một ống bị hỏng (SIGPIPE) trong python?

Cách tốt nhất để giải quyết vấn đề này là gì? Độ phân giải ưa thích của tôi sẽ chỉ đơn giản là đóng kết nối phía máy chủ cho máy khách và di chuyển, thay vì thoát khỏi toàn bộ chương trình.

PS: This giải đáp thắc mắc/câu trả lời với sự cố theo cách chung chung; tôi nên giải quyết nó như thế nào?

Trả lời

36

Đọc trên câu lệnh try:.

try: 
    # do something 
except socket.error, e: 
    # A socket error 
except IOError, e: 
    if e.errno == errno.EPIPE: 
     # EPIPE error 
    else: 
     # Other error 
+0

Nếu tôi thử: #something ngoại trừ: #anything, nó sẽ chỉ bắt bất cứ điều gì, và không chỉ IOErrors? –

+5

Chăn ngoại trừ một chính sách xấu. Tuy nhiên, nó sẽ bắt bất kỳ loại ngoại lệ. Bạn biết đó là một IOError. Xử lý điều đó. Nếu một cái gì đó khác cây trồng lên, tìm ra lý do tại sao và xử lý nó một cách thích hợp. Bạn không muốn che dấu các lỗi như phân chia bằng không hoặc hết bộ nhớ. –

+1

Nếu bạn đang sử dụng mô-đun socket của Python, bạn sẽ không nhận được một ngoại lệ IOError: bạn sẽ nhận được một ngoại lệ socket.error. – mhawke

3

SIGPIPE (mặc dù tôi nghĩ có lẽ bạn có nghĩa là EPIPE?) Xảy ra trên ổ cắm khi bạn tắt một ổ cắm và sau đó gửi dữ liệu đến nó. Giải pháp đơn giản là không tắt ổ cắm trước khi cố gắng gửi dữ liệu. Điều này cũng có thể xảy ra trên đường ống, nhưng nó không có vẻ như đó là những gì bạn đang gặp phải, vì nó là một máy chủ mạng.

Bạn cũng có thể áp dụng hỗ trợ ban nhạc để bắt ngoại lệ trong một số trình xử lý cấp cao nhất trong mỗi chuỗi.

Tất nhiên, nếu bạn sử dụng Twisted thay vì sinh ra một chuỗi mới cho mỗi kết nối máy khách, có thể bạn sẽ không gặp phải vấn đề này. Nó thực sự khó (có thể là không thể, tùy thuộc vào ứng dụng của bạn) để có được thứ tự các hoạt động đóng và viết đúng nếu nhiều luồng đang xử lý cùng một kênh I/O.

+0

* Giải pháp đơn giản là không tắt ổ cắm trước khi cố gắng gửi dữ liệu. * Bạn giả định ở đây rằng socket đã được tắt cục bộ (ở phía máy chủ) trong khi ở [this] (http://stackoverflow.com/ a/11866962/95735) trả lời chúng tôi đọc rằng * Điều này thường xảy ra khi bạn ghi vào một ổ cắm được đóng hoàn toàn ở phía bên kia (khách hàng). * Bạn đã bỏ qua trường hợp này theo mục đích hoặc bạn không đồng ý với tuyên bố này? –

-3

câu trả lời của tôi là rất gần với S. Lott, ngoại trừ tôi thậm chí còn đặc biệt hơn:

try: 
    # do something 
except IOError, e: 
    # ooops, check the attributes of e to see precisely what happened. 
    if e.errno != 23: 
     # I don't know how to handle this 
     raise 

nơi "23" là số lỗi mà bạn nhận được từ EPIPE. Bằng cách này, bạn sẽ không cố gắng xử lý lỗi quyền hoặc bất kỳ điều gì khác mà bạn không được trang bị.

+2

Các errno sẽ là 32, không 23. – mhawke

+2

Tôi nên làm rõ rằng tôi có nghĩa là "23" như một trình giữ chỗ. Có thật không? 32? Tôi đã gần hơn tôi đoán. :-) –

47

Giả sử bạn đang sử dụng mô-đun ổ cắm tiêu chuẩn, bạn nên bắt gặp ngoại lệ socket.error: (32, 'Broken pipe') (không phải IOError như những người khác đã đề xuất). Điều này sẽ được nêu ra trong trường hợp bạn đã mô tả, tức là gửi/ghi vào một ổ cắm mà phía xa đã bị ngắt kết nối.

import socket, errno, time 

# setup socket to listen for incoming connections 
s = socket.socket() 
s.bind(('localhost', 1234)) 
s.listen(1) 
remote, address = s.accept() 

print "Got connection from: ", address 

while 1: 
    try: 
     remote.send("message to peer\n") 
     time.sleep(1) 
    except socket.error, e: 
     if isinstance(e.args, tuple): 
      print "errno is %d" % e[0] 
      if e[0] == errno.EPIPE: 
       # remote peer disconnected 
       print "Detected remote disconnect" 
      else: 
       # determine and handle different error 
       pass 
     else: 
      print "socket error ", e 
     remote.close() 
     break 
    except IOError, e: 
     # Hmmm, Can IOError actually be raised by the socket module? 
     print "Got IOError: ", e 
     break 

Lưu ý rằng ngoại trừ điều này sẽ không luôn luôn được nâng lên trên ghi đầu tiên vào một ổ cắm đóng cửa - thường càng ghi giây (trừ số byte ghi trong ghi đầu tiên là lớn hơn so với kích thước bộ đệm của ổ cắm). Bạn cần ghi nhớ điều này trong trường hợp ứng dụng của bạn nghĩ rằng đầu cuối từ xa nhận được dữ liệu từ lần ghi đầu tiên khi nó có thể đã bị ngắt kết nối.

Bạn có thể giảm tỷ lệ (nhưng không hoàn toàn loại bỏ) điều này bằng cách sử dụng select.select() (hoặc poll). Kiểm tra dữ liệu đã sẵn sàng để đọc từ peer trước khi thử viết. Nếu select báo cáo rằng có sẵn dữ liệu để đọc từ ổ cắm ngang hàng, hãy đọc nó bằng cách sử dụng socket.recv(). Nếu điều này trả về một chuỗi rỗng, từ xa ngang hàng đã đóng kết nối. Bởi vì vẫn còn một điều kiện chủng tộc ở đây, bạn vẫn sẽ cần phải nắm bắt và xử lý các ngoại lệ.

Xoắn là điều tuyệt vời cho loại điều này, tuy nhiên, có vẻ như bạn đã viết một chút mã công bằng.

+0

Điều này có vẻ lạ 'nếu isinstance (e.args, tuple): '. Ai đó có thể giải thích điều này? – guettli

+0

Điều đó có nghĩa là, "là e.args một tuple?" – mjz19910

0

Tôi phải đối mặt với cùng một câu hỏi. Nhưng tôi gửi mã tương tự vào lần sau, nó chỉ hoạt động. Lần đầu tiên nó đã phá vỡ:

$ packet_write_wait: Connection to 10.. port 22: Broken pipe 

Lần thứ hai hoạt động:

[1] Done     nohup python -u add_asc_dec.py > add2.log 2>&1 

Tôi đoán lý do có thể về môi trường máy chủ hiện tại.