2013-03-06 36 views
9

Đây là mã của tôi để chạy các máy chủ:python địa chỉ TCPServer đã được sử dụng nhưng tôi đóng máy chủ và tôi sử dụng `allow_reuse_address`

class MyRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): 
    #.... 

PORT = 8089 

httpd = SocketServer.TCPServer(("", PORT), MyRequestHandler) 
httpd.allow_reuse_address = True 

print "Serving forever at port", PORT 
try: 
    httpd.serve_forever() 
except: 
    print "Closing the server." 
    httpd.server_close() 
    raise 

Tuy nhiên, đây là những gì sẽ xảy ra:

^CClosing the server. 
Traceback (most recent call last): 
    File "server.py", line 118, in <module> 
    self.send_error(400, "Unimplemented GET command: %s" % (self.path,)) 
    File "/home/claudiu/local/lib/python2.6/SocketServer.py", line 224, in serve_forever 
    r, w, e = select.select([self], [], [], poll_interval) 
KeyboardInterrupt 
(.virtualenv)[email protected]:~/xxx$ python server.py 
Traceback (most recent call last): 
    File "server.py", line 122, in <module> 
    httpd = SocketServer.TCPServer(("", PORT), MyRequestHandler) 
    File "/home/claudiu/local/lib/python2.6/SocketServer.py", line 402, in __init__ 
    self.server_bind() 
    File "/home/claudiu/local/lib/python2.6/SocketServer.py", line 413, in server_bind 
    self.socket.bind(self.server_address) 
    File "<string>", line 1, in bind 
socket.error: [Errno 98] Address already in use 

Tại sao ? Tôi đóng máy chủ và đặt allow_reuse_address thành True ... Sử dụng python 2.6.8.

Trả lời

13

Nhờ các câu trả lời khác, tôi đã tìm ra câu trả lời. allow_reuse_address nên được trên lớp, không phải trên Ví dụ:

SocketServer.TCPServer.allow_reuse_address = True 
httpd = SocketServer.TCPServer(("", PORT), MyRequestHandler) 

Tôi vẫn không chắc chắn lý do tại sao đóng ổ cắm không giải phóng nó lên cho các hoạt động tiếp theo của máy chủ, mặc dù.

3

Đó là vì TCP TIME_WAIT.

Somebody discovered this exact problem.

However, if I try to stop and start the server again to test any modifications, I get a random “socket.error: [Errno 98] Address already in use” error. This happens only if a client has already connected to the server.

Checking with netstat and ps, I found that although the process it self is no longer running, the socket is still listening on the port with status “TIME_WAIT”. Basically the OS waits for a while to make sure this connection has no remaining packets on the way.

+0

Không phải là một trích dẫn tuyệt vời. * Cổng * vẫn * hiện diện * trong trạng thái TIME_WAIT. Không có ổ cắm, và không nghe. – EJP

+0

Tôi hiểu, nhưng tôi muốn biết tại sao các giải pháp của tôi (đặt 'allow_reuse_address' và đóng socket khi tắt máy) không khắc phục được. – Claudiu

0

Đó là bởi vì bạn phải thiết lập SO_REUSEADDRESS trước bạn gắn ổ cắm. Khi bạn đang tạo và ràng buộc socket tất cả trong một bước và sau đó thiết lập nó, nó đã quá muộn.

+0

Vì vậy, cho nên 'allow_reuse_address' về cơ bản không làm gì để triển khai' TCPServer' mặc định? Cũng không nên đóng socket ở cuối chương trình để tránh lỗi này? – Claudiu

3

[Err 98] Address already in use là do ổ cắm là .close() nhưng vẫn chờ đủ thời gian để đảm bảo TCP từ xa nhận được xác nhận yêu cầu chấm dứt kết nối của nó (xem TIME_WAIT). Theo mặc định bạn không được phép để ràng buộc một ổ cắm nếu có một ổ cắm bị ràng buộc vào cổng đó nhưng bạn có thể ghi đè lên rằng với allow_reuse_address (SO_REUSEADDR)

Mặc dù có thể đột biến TCPServer.allow_reuse_addr (như đề xuất trong this other answer), tôi nghĩ là sạch hơn để phân lớp của bạn riêng của TCPServer nơi allow_reuse_address được thiết lập để True:

import SocketServer 
import SimpleHTTPServer 
import time 

class MyRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): 
    def do_GET(): 
     time.sleep(60) 
     self.request.sendall("I'm here!") 

class ReuseAddrTCPServer(SocketServer.TCPServer): 
    allow_reuse_address = True 

PORT = 8089 

httpd = ReuseAddrTCPServer(("", PORT), MyRequestHandler) 
httpd.daemon_threads = True 


print "Serving forever at port", PORT 
try: 
    httpd.serve_forever() 
except: 
    print "Closing the server." 
    httpd.server_close() 
    raise 

bạn có thể sử dụng chắc chắn thiết allow_reuse_address trên dụ bản thân (mà không phiền với lớp) nhưng bạn cần phải sử dụng TCPServer(..., bind_and_activate=False), nếu không ổ cắm sẽ bị ràng buộc trước bạn có cơ hội thay đổi cài đặt allow_reuse_address. Sau đó, bạn cần phải tự gọi .server_bind().server_activate() trước serve_forever():

... 
httpd = SocketServer.TCPServer(("", PORT), MyRequestHandler, bind_and_activate=False) 
httpd.allow_reuse_address = True 
httpd.daemon_threads = True 
... 
httpd.server_bind() 
httpd.server_activate() 
httpd.serve_forever() 
Các vấn đề liên quan