2012-04-24 7 views
11

Das Betriebssystem ist Linux. Ich habe einen Server-Prozess, der seine Port-Echtzeit ändern kann. Ich würde jedoch gerne im Voraus wissen, ob ein Hafen frei ist, bevor er verbindlich ist.Wie finde ich in C, dass ein Port frei ist?

Szenario: Server bindet localhost: 5000 und erhält eine Anforderung zum Binden an localhost: 6000. Der Server muss prüfen, ob der Port frei ist. Diese Frage sucht nach Antworten, die eine Routine bereitstellen, die überprüft, ob ein Port frei ist oder nicht.

Zum Bearbeiten meiner Frage mit einem Code-Snippet, das überprüft, ob ein Port frei ist zu verwenden. Dies bedeutet nicht, dass es verwendet wird. Der folgende Code beantwortet die Frage "Wenn der Port gerade verfügbar ist", er verwendet ihn nicht. Öffnet einen Socket, überprüft, ob die Bindung EADDRINUSE zurückgibt und schließt den Socket.

#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; 


} 

Hier sind einige Beispiele für Läufe (Teil Ausgänge)

[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

Versuchen Sie es verbindlich, wenn es scheitert, es ist nicht kostenlos ... – Nick

+1

Nicks Vorschlag wohl die tragbare Lösung ist, aber es könnte OS-spezifische Weise zu tun, es auch. Welches Betriebssystem werden Sie verwenden? –

+0

@Nick sieht aus, dass die Bindung EADDRINUSE (errno) zurückgibt, wenn der Port nicht frei ist. – cateof

Antwort

12

Dies ist eine offensichtliche race condition, da andere Prozesse auf Ihrem System könnte Ports parallel verbindlich. Also, jede Lösung, die Sie finden, wird unvollkommen sein, und Sie werden immer noch müssen nur schreiben, nach dem "versuchen, bind(), wenn es fehlschlägt, wählen Sie eine neue Portnummer und versuchen Sie es erneut" -Ansatz.

+0

Ich stimme zu, dass eine Wettlaufbedingung auftreten könnte, aber in meinem System ist dieses Szenario nicht möglich. Auch der Ansatz "Wähle den nächsten Hafen" ist nicht akzeptabel. – cateof

+0

@cateof: Was ist nicht möglich?Ist die Race Condition nicht möglich, oder ist es nicht möglich, eine Bindung zu versuchen und dann zu sehen, ob sie fehlschlägt? –

+0

Die Race-Bedingung ist möglich, wenn mindestens zwei Prozesse um eine Ressource konkurrieren. In meinem System versucht nur ein Prozess zu binden. – cateof

7

Wenn Ihr Server war sagte welcher Port zu verwenden, nur bind() es. Ernst.

Sicher, Sie könnten /proc/net/tcp analysieren und sehen, ob der Port in Verwendung ist. Aber was dann? Sie müssen immer noch bind() anrufen, jetzt da Sie wissen, dass Ihr Port frei ist, und es wird Ihnen sagen, ob der Port dann frei war, und so hatte es keinen Sinn, durch /proc/net/tcp zu kriechen und all diese (langsame!) String-Produktion zu machen Parsing und Extra-Kernel stolpern durch nicht-sehr gut-optimierte Diagnosewege (gelesen: super langsam im Vergleich zu bind()), nur um Informationen zu bekommen, die veraltet sein könnten, bevor Sie sie überhaupt analysiert haben. Rufen Sie einfach bind() an und seien Sie glücklich.

4

Ich hatte selbst Probleme damit und habe Ihren Code leicht modifiziert.

Die Lösung ist serv_addr.sin_port = 0 (Auto Assign Port) zu setzen.

Hinweis In den bind() und die getsockname() Linien gibt es unsauber Abgüsse sockaddr_in-sockaddr. Ich habe es an vielen Orten gesehen und suche nach einer sichereren Lösung.

#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; 
} 

Programmausgabe

Opened 4 
port number 59081 
+0

"In der' getsockname (...) 'Zeile gibt es eine gefährliche Umwandlung von' sockaddr_in' nach 'sockaddr'." Wie denkst du, ist es gefährlich? Vielleicht möchten Sie 'sockaddr_storage' verwenden, um wirklich sicher zu sein. – glglgl

+0

Sie haben die gleiche Größe 16 Bytes. Die 'sin_len' und' sin_family' befinden sich in beiden Strukturen an derselben Stelle. Ich mag keine reininterpret_cast-Umwandlungen. – neoneye