2010-05-14 64 views
19

Tôi muốn nhận bất kỳ cổng TCP mở ngẫu nhiên nào trên localhost bằng Python. Cách dễ nhất là gì?mở cổng TCP bằng Python

+4

Điều này giống như chó đuổi theo xe hơi - bạn sẽ làm gì khi có xe? –

+1

Tôi cần một số cổng TCP cho một ứng dụng bàn điều khiển khác cần cổng như một tham số nơi nó sẽ lắng nghe. – Albert

+4

Có một điều kiện đua rõ ràng (mặc dù nhỏ) với cách tiếp cận bạn đang dự tính. Cổng có thể được thực hiện bởi thời gian bạn vượt qua nó quá trình khác. Bạn có sở hữu mã của quy trình khác không? Nếu vậy, có thể tốt hơn là làm cho nó có được một cổng ngẫu nhiên và báo cáo lại cho quy trình đầu tiên. –

Trả lời

37

giải pháp hiện tại của tôi:

def get_open_port(): 
     import socket 
     s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
     s.bind(("",0)) 
     s.listen(1) 
     port = s.getsockname()[1] 
     s.close() 
     return port 

Không phải rất tốt đẹp và cũng không chính xác 100% nhưng nó hoạt động cho bây giờ.

+7

Tại sao cuộc gọi listen()? Đối với tôi, s.getsockname() [1] được điền ngay khi tôi gọi bind() – ncoghlan

+0

Có cần gọi 's.shutdown()' trước 's.close()' trong trường hợp này không? –

2

Tôi thực sự sử dụng sau đây trong một trong những chương trình của tôi:

port = random.randint(10000,60000) 

Tất nhiên, điều này thậm chí còn dễ bị va chạm hơn mã mà bạn có. Nhưng tôi chưa bao giờ gặp vấn đề với nó. Vấn đề là, tại bất kỳ thời điểm nào, hầu hết các cổng số cao đó đều không được sử dụng và nếu bạn chỉ chọn một cổng ngẫu nhiên, có xung đột với một quy trình khác là khá khó xảy ra. Nếu bạn làm một cái gì đó giống như giải pháp bạn đăng trong câu trả lời của bạn (mở một ổ cắm và lấy số cổng của nó), nó gần như chắc chắn rằng các cổng sẽ không xung đột. Vì vậy, nếu đây là một cái gì đó mà bạn sẽ chỉ được sử dụng cho chính mình (như trái ngược với một cái gì đó bạn sẽ phát hành cho công chúng), suy nghĩ về việc liệu nó có giá trị đến với một giải pháp thực sự chống đạn. Tỷ lệ cược là nó sẽ không bao giờ tạo ra sự khác biệt.

Được thúc đẩy bởi bình luận của Marcelo Cantos về câu hỏi của bạn, tôi sẽ thêm giải pháp chuẩn nó. Thông thường nó sẽ làm một cái gì đó như viết một tập tin tạm thời có chứa số cổng đến một số vị trí tiêu chuẩn trong hệ thống tập tin. Vì quá trình bạn đang làm việc không làm điều đó, trong một số ý nghĩa, bất kỳ giải pháp nào bạn đưa ra sẽ là một chút hack. Nhưng một lần nữa, nếu nó chỉ là để sử dụng của riêng bạn, đó là có lẽ tốt.

1

Đây là phiên bản của tôi, tuy nhiên nó không thực sự ngẫu nhiên nếu bạn chỉ định một dải cổng. Điều này cũng sẽ bị điều kiện chủng tộc, nhưng đây là cách tốt nhất mà tôi biết nếu bạn cần biết cổng trước thời hạn.

import socket 
import errno 
import contextlib 

reserved_ports = set() 

def get_open_port(lowest_port = 0, highest_port = None, bind_address = '', *socket_args, **socket_kwargs): 
    if highest_port is None: 
     highest_port = lowest_port + 100 
    while lowest_port < highest_port: 
     if lowest_port not in reserved_ports: 
      try: 
       with contextlib.closing(socket.socket(*socket_args, **socket_kwargs)) as my_socket: 
        my_socket.bind((bind_address, lowest_port)) 
        this_port = my_socket.getsockname()[1] 
        reserved_ports.add(this_port) 
        return this_port 
      except socket.error as error: 
       if not error.errno == errno.EADDRINUSE: 
        raise 
       assert not lowest_port == 0 
       reserved_ports.add(lowest_port) 
     lowest_port += 1 
    raise Exception('Could not find open port') 
+0

Thật không may, đây không phải là 100% không hoạt động và có thể trả về Đúng khi cổng thực sự được ứng dụng khác sử dụng. Tôi đã không đóng đinh lý do tại sao, nhưng.bind() sẽ không phải lúc nào cũng thất bại khi một tiến trình khác đang lắng nghe trên cổng. – Joe

+0

@Joe có khả năng ứng dụng kia bị ràng buộc vào một địa chỉ khác nhưng với cùng một cổng? hoặc gia đình địa chỉ khác nhau? –

+0

Có thể. Tất cả những gì tôi biết là trong trường hợp của tôi, ứng dụng máy chủ mà tôi đang cố gắng khởi chạy không thể mở được cổng mặc dù đoạn mã trên cho biết cổng đã được miễn phí. Các ứng dụng khác là erl.exe (erlang), mà tôi đã thông qua thiết lập của RabbitMQ nếu bạn muốn thử cho chính mình. – Joe

1

Các cổng phù du về cơ bản nằm trong phạm vi 49152 - 65535. nếu bạn muốn kiểm tra các cảng ở phạm vi lớn hơn sau đó chỉ cần thay đổi các giá trị trong randint.

import pustil 
from random import randint 
def getfreeport(): 
    port = randint(49152,65535) 
    portsinuse=[] 
    while True: 
     conns = pstuil.net_connections() 
     for conn in conns: 
      portsinuse.append(con.laddr[1]) 
     if port in portsinuse: 
      port = randint(49152,65535) 
     else: 
      break 
    return port 
4

Có thể tìm thấy cổng miễn phí bằng cách kết nối ổ cắm với cổng được hệ điều hành chọn. Sau khi hệ điều hành chọn một cổng, ổ cắm có thể được xử lý. Tuy nhiên, giải pháp này không chống lại các điều kiện chủng tộc - trong thời gian ngắn giữa việc nhận được số cổng miễn phí và sử dụng cổng này, quá trình khác có thể sử dụng cổng này.

def find_free_port(): 
    s = socket.socket() 
    s.bind(('', 0))   # Bind to a free port provided by the host. 
    return s.getsockname()[1] # Return the port number assigned.