2014-05-14 39 views
1

我想用一個特定本地端口,而不是一個由內核隨機分配到連接到遠程服務器。在呼叫connect()到遠程服務器之前,我可以通過調用bind()來綁定到本地端口。選擇本地源端口已在使用

我的問題是如果我想使用的本地端口已被其他應用程序使用,會發生什麼?我應該可以使用它,只要目標端口或遠程端口不同(與服務器可以在端口80上連接多個連接的方式相同)。但是在這種情況下,我的bind調用不應該失敗,如果是這樣,我該如何設置套接字以使用其他應用程序已在使用的本地端口?

我想這樣做,這是我嘗試寫連接到服務器應用程序誰檢查源端口的本地代理的原因。如果源端口錯誤,服務器將不允許連接。客戶端應用程序連接到我的代理,並且我希望我的代理使用相同的端口連接到服務器 - 但是如果代理位於同一臺計算機上,它將無法工作,因爲端口已被應用程序連接使用到我的代理。

+0

對於「將我的'bind'呼叫失敗」的問題,你應該能夠測試它自己,因爲你打算反正編寫這個程序。 – merlin2011

+0

我真的很想知道我可以依賴的行爲和背後的原因,我不是一個專家,不會相信一個測試用例:) – Flash

+0

我也不是網絡編程方面的專家。我提出了這個建議,因爲那是我爲確定答案所做的。 [這個答案](http://stackoverflow.com/a/3329672/391161)有點泛泛,但它可以幫助你朝正確的方向發展。 – merlin2011

回答

2

你可以說,你應該能夠這樣用的說法,但TCP實現不讓你,除非這兩個結合是不同的。例如,您可能能夠使用不同IP地址綁定到相同的端口。

有兩個問題與允許重疊的結合:

  1. 如果這兩個應用程序調用accept會發生什麼?他們是否爭取接入連接?

  2. 如果這兩個應用程序嘗試對同一IP和端口的出站連接,會發生什麼?這兩個連接如何區分?

現在可以解決這些問題。但我不知道任何麻煩的實現。論點是,應用程序將不得不合作,否則他們會得到令人驚訝的結果。如果他們合作,他們可以共享綁定的套接字。

因此,答案是:如果你不使用具有該端口的其他應用的合作,那麼你有沒有分享它的權利。如果您正在與其他應用程序合作,請讓它使用您的平臺支持的方法爲您提供套接字的副本。

+0

謝謝。爲什麼內核在你綁定的時候會拋出一個錯誤,而不是當你試圖在case 1中偵聽並且在case 2中連接時?條件可以安全地爲「您不能監聽該端口/ IP,因爲其他人已經在監聽」以及「您無法連接到該端口IP,因爲其他人是從同一端口連接的」,並讓「綁定」總是成功。 – Flash

+0

@Andrew那麼如果一個進程綁定和另一個連接從一個IP接收到一個傳入連接,而另一個連接嘗試連接到另一個進程,會發生什麼?如果他們合作,他們不需要兩者都有約束力。如果它們不是,那麼它們不能同時綁定。要解決所有這些問題還有很多麻煩,而且這些過程必須無論如何都要合作。如果他們合作,他們不需要「綁定」 - 他們可以共享綁定的套接字。 –

+0

我想你不能讓'connect'繼續前進,如果有人在'listen',反之亦然,因爲'bind'並不一定意味着'listen'。儘管這很麻煩,但我明白你的意思。如果我不控制的另一個應用程序正在使用端口,那麼您是否說我無法將該端口與不同的目標IP一起使用?我已經添加了一些關於我想要做的事情的說明。 – Flash

-1

bind只知道一個端點。

假設兩個套接字綁定到相同的端口。哪一個傳入的數據包將被路由到?

accept另一方面知道兩個同伴。

+1

如果兩個插座都已連接,則目標可由對等IP和端口確定。如果套接字已綁定但未連接,則不應接收任何數據包。 – Flash

1

>但應該不是我的電話綁定在這種情況下失敗,

是,如果插座沒有SO_REUSEADDR選項集。

>如果是的話我怎麼可以設置使用本地端口被其他應用程序已經在使用的插座?

您的應用程序和其他應用程序都必須在要綁定到本地端口的套接字上設置SO_REUSEADDR選項。

下面的代碼連接到HTTP服務器,給出命令行參數,從端口1111:

#include <sys/socket.h> 
#include <stdio.h> 
#include <netinet/in.h> 
#include <netdb.h> 

#define CLIENT_PORT 1111 
#define SERVER_PORT 80 

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

    struct sockaddr_in client_name, server_name; 
    struct hostent *server_info; 

    if (argc != 2) 
    return printf("Exactly one argument is required: host to connect\n"), 1; 

    int sock_fd = socket(AF_INET, SOCK_STREAM, 0); 
    if (sock_fd < 0) 
    return perror("socket"), 1; 

    /* Without the next 4 lines, bind refuses to use the same port */ 
    int reuseaddr = 1; 
    if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, 
       sizeof(reuseaddr)) < 0) 
    return perror("setsockopt"), 1; 

    client_name.sin_family = AF_INET; 
    client_name.sin_port = htons(CLIENT_PORT); 
    client_name.sin_addr.s_addr = htonl(INADDR_ANY); 

    if (bind(sock_fd, (struct sockaddr *) &client_name, 
      sizeof(struct sockaddr_in)) < 0) 
    return perror("bind"), 1; 

    server_name.sin_family = AF_INET; 
    server_name.sin_port = htons(SERVER_PORT); 
    if ((server_info = gethostbyname(argv[1])) == NULL) 
    return printf("Unknown host: %s\n", argv[1]), 1; 

    server_name.sin_addr = *(struct in_addr *) server_info->h_addr; 

    if (connect(sock_fd, (struct sockaddr *) &server_name, 
       sizeof(server_name)) < 0) 
    return perror("connect"), 1; 

    return 0; 
} 

>如果本地端口我想使用已在使用由其他應用程序會發生什麼?

沒有SO_REUSEADDR(嘗試註釋掉周圍setsockopt 4線),綁定失敗:

$ ./client google.com 
$ ./client stackoverflow.com 
bind: Address already in use 

隨着SO_REUSEADDR,您可以連接到不同的遠程服務器:

$ ./client google.com 
$ ./client stackoverflow.com 

但是然後連接不會允許你打開兩個相同的套接字s烏爾斯河目的地:

$ ./client google.com 
$ ./client google.com 
connect: Cannot assign requested address