2012-04-24 28 views
11

Hệ điều hành là Linux. Tôi có một quy trình máy chủ có thể thay đổi thời gian thực của cổng. Tuy nhiên tôi muốn biết trước nếu một cổng là miễn phí trước khi ràng buộc.làm thế nào để tìm thấy trong C mà một cổng là miễn phí để sử dụng?

Tình huống: Máy chủ liên kết localhost: 5000 và nhận yêu cầu liên kết tại localhost: 6000. Máy chủ phải kiểm tra xem cổng có miễn phí hay không. Câu hỏi này tìm kiếm câu trả lời cung cấp một thói quen kiểm tra xem một cổng là miễn phí hay không.

Để lưu nội dung, tôi đang chỉnh sửa câu hỏi của mình bằng đoạn mã để kiểm tra xem có sử dụng cổng hay không. Điều này không có nghĩa là nó sẽ được sử dụng. Mã bên dưới trả lời cho câu hỏi "nếu cổng hiện khả dụng", nó không sử dụng nó. Mở một socket, kiểm tra xem bind có trả về EADDRINUSE và đóng socket không.

#include <iostream> 
#include <sys/socket.h> 
#include <stdlib.h> 
#include <string.h> 
#include <unistd.h> 
#include <arpa/inet.h> 
#include <errno.h> 

int main(int argc, char **argv) { 

    struct sockaddr_in serv_addr; 
    if(argc < 2) 
     return 0; 
    int port = atoi(argv[1]); 

    int sockfd = socket(AF_INET, SOCK_STREAM, 0); 
    if(sockfd < 0) { 
     printf("socket error\n"); 
     return 0; 
    } else { 
     printf("Opened fd %d\n", sockfd); 
    } 

    bzero((char *) &serv_addr, sizeof(serv_addr)); 
    serv_addr.sin_family = AF_INET; 
    serv_addr.sin_addr.s_addr = INADDR_ANY; 
    serv_addr.sin_port = htons(port); 
    if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { 

     if(errno == EADDRINUSE) 
     { 
      printf("the port is not available. already to other process\n"); 
     } else { 
      printf("could not bind to process (%d) %s\n", errno, strerror(errno)); 
     } 
    } 

    if (close (sockfd) < 0) { 
     printf("did not close fd: %s\n", strerror(errno)); 
     return errno; 
    } 

    return 0; 


} 

Dưới đây là một số chạy mẫu (kết quả đầu ra từng phần)

[bash{1051}{51}]:[~/some_sources/checkbind]::./a.out 41067 
the port is not available. already to other process 
[bash{1052}{52}]:[~/some_sources/checkbind]::./a.out 22 
could not bind to process (13) Permission denied 
[bash{1053}{53}]:[~/some_sources/checkbind]::./a.out 22000 
Opened fd 3 
+10

Hãy thử gắn vào nó, nếu nó không thành công, nó không miễn phí ... – Nick

+1

Nicks gợi ý có lẽ là giải pháp di động nhất, nhưng có thể có cách OS cụ thể để làm nó là tốt. Bạn sẽ sử dụng hệ điều hành nào? –

+0

@Nick trông rằng ràng buộc trả về EADDRINUSE (errno) nếu cổng là nó không miễn phí. – cateof

Trả lời

12

Đây là rõ ràng một race condition, vì quá trình khác trên hệ thống của bạn có thể được gắn vào cổng song song. Vì vậy, bất kỳ giải pháp nào bạn tìm thấy sẽ không hoàn hảo và bạn sẽ vẫn còn cần phải viết nó theo "cố gắng bind(), nếu không chọn được số cổng mới và thử lại" phương pháp tiếp cận.

+0

Tôi đồng ý rằng một điều kiện chủng tộc có thể xảy ra, nhưng trong hệ thống của tôi, tình huống này là không thể. Ngoài ra, cách tiếp cận "chọn cổng tiếp theo" là không thể chấp nhận được. – cateof

+0

@cateof: Điều gì là không thể?Là điều kiện chủng tộc không thể, hoặc là nó không thể thử ràng buộc và sau đó xem nếu nó không thành công? –

+0

Điều kiện chủng tộc là có thể nếu bạn có ít nhất hai quy trình cạnh tranh cho một tài nguyên. Trong hệ thống của tôi chỉ có một quá trình cố gắng ràng buộc. – cateof

7

Nếu máy chủ của bạn là yêu cầu sử dụng cổng nào, chỉ cần bind() nó. Nghiêm túc.

Chắc chắn, bạn có thể phân tích cú pháp /proc/net/tcp và xem liệu cổng có đang được sử dụng hay không. Nhưng sau đó thì? Bạn vẫn cần gọi số bind() ngay bây giờ để biết cổng của mình là miễn phí và nó sẽ cho bạn biết liệu cổng đó có miễn phí hay không, và do đó không có điểm nào trong việc thực hiện thông qua /proc/net/tcp và thực hiện tất cả chuỗi sản xuất (chậm!) Và phân tích cú pháp và thêm các chuyến đi hạt nhân thông qua các đường chẩn đoán không được tối ưu hóa rất tốt (đọc: siêu chậm so với bind()), chỉ để nhận thông tin có thể lỗi thời trước khi bạn hoàn thành việc phân tích nó. Vì vậy, chỉ cần gọi bind() và vui vẻ.

4

Tôi đã đấu tranh với chính bản thân mình và đã sửa đổi một chút mã của bạn.

Giải pháp là đặt serv_addr.sin_port = 0 (tự động chỉ định cổng).

Note Trong bind()getsockname() dòng có phôi ô uế sockaddr_in-sockaddr. Tôi đã thấy nó được thực hiện nhiều nơi và tôi đang tìm kiếm một giải pháp an toàn hơn.

#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 

// ... snip ... 

int sock = socket(AF_INET, SOCK_STREAM, 0); 
if(sock < 0) { 
    printf("socket error\n"); 
    return; 
} 
printf("Opened %d\n", sock); 

struct sockaddr_in serv_addr; 
bzero((char *) &serv_addr, sizeof(serv_addr)); 
serv_addr.sin_family = AF_INET; 
serv_addr.sin_addr.s_addr = INADDR_ANY; 
serv_addr.sin_port = 0; 
if (bind(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { 
    if(errno == EADDRINUSE) { 
     printf("the port is not available. already to other process\n"); 
     return; 
    } else { 
     printf("could not bind to process (%d) %s\n", errno, strerror(errno)); 
     return; 
    } 
} 

socklen_t len = sizeof(serv_addr); 
if (getsockname(sock, (struct sockaddr *)&serv_addr, &len) == -1) { 
    perror("getsockname"); 
    return; 
} 

printf("port number %d\n", ntohs(serv_addr.sin_port)); 


if (close (sock) < 0) { 
    printf("did not close: %s\n", strerror(errno)); 
    return; 
} 

đầu ra Chương trình

Opened 4 
port number 59081 
+0

"Trong dòng' getockname (...) 'có một dàn diễn viên nguy hiểm từ' sockaddr_in' đến 'sockaddr'." Bạn nghĩ nó nguy hiểm như thế nào? Có thể bạn muốn sử dụng 'sockaddr_storage' để thực sự an toàn. – glglgl

+0

Chúng có cùng kích thước 16 byte. 'Sin_len' và' sin_family' được đặt cùng một vị trí trong cả hai cấu trúc. Tôi chỉ không thích reinterpret_cast phôi. – neoneye

Các vấn đề liên quan