2014-06-12 7 views
22

Ich habe meinen eigenen http-Server auf einem Raspberry Pi. Das Problem ist, wenn ich das Programm stoppe und neu starte, ist der Port nicht mehr verfügbar. Manchmal bekomme ich das gleiche Problem, wenn ich viele Anfragen erhalte.
Ich möchte SO_REUSEADDR verwenden, damit ich den Port auch dann weiter verwenden kann, wenn der Fehler auftritt, aber kein Glück hatte, dass er eingerichtet wurde. Unten ist mein Code.
Der Fehler, den ich bekomme, ist "FEHLER bei Bindung: Adresse bereits in Verwendung".Wie verwende ich Setsockopt (SO_REUSEADDR)?

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <unistd.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 

void error(const char *msg) 
{ 
    perror(msg); 
    exit(1); 
} 

int main(int argc, char *argv[]) 
{ 
    printf("Starting Listener\n"); 
    int sockfd, newsockfd, portno; 
    socklen_t clilen; 
    char buffer[256]; 
    struct sockaddr_in serv_addr, cli_addr; 
    int n; 
    if (argc < 2) { 
     fprintf(stderr,"ERROR, no port provided\n"); 
     exit(1); 
    } 
    sockfd = socket(AF_INET, SOCK_STREAM, 0); 
    if (sockfd < 0) 
     error("ERROR opening socket"); 
    bzero((char *) &serv_addr, sizeof(serv_addr)); 
    portno = atoi(argv[1]); 
    serv_addr.sin_family = AF_INET; 
    serv_addr.sin_addr.s_addr = INADDR_ANY; 
    serv_addr.sin_port = htons(portno); 
    if (bind(sockfd, (struct sockaddr *) &serv_addr, 
       sizeof(serv_addr)) < 0) 
       error("ERROR on binding"); 

    printf("about to listen\n"); 
    listen(sockfd,5); 
    printf("finished listening\n"); 
    clilen = sizeof(cli_addr); 
    printf("About to accept\n"); 

    int i; 
    for(i=0; i<100; i++){ 
     newsockfd = accept(sockfd, 
       (struct sockaddr *) &cli_addr, 
       &clilen); 

     if (newsockfd < 0) 
      error("ERROR on accept"); 
     bzero(buffer,256); 
     n = read(newsockfd,buffer,255); 
     if (n < 0) error("ERROR reading from socket"); 
     printf("Here is the message: %s\n",buffer); 
     n = write(newsockfd,"I got your message",18); 
     if (n < 0) error("ERROR writing to socket"); 
     close(newsockfd); 
    } 
    close(sockfd); 
    return 0; 
} 

Antwort

48

Nach:

sockfd = socket(AF_INET, SOCK_STREAM, 0); 
if (sockfd < 0) 
    error("ERROR opening socket"); 

Sie hinzufügen können (mit Standard-C99-Verbindung Litteral Unterstützung):

if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof(int)) < 0) 
    error("setsockopt(SO_REUSEADDR) failed"); 

Oder:

int enable = 1; 
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) 
    error("setsockopt(SO_REUSEADDR) failed"); 
+0

habe ich versucht, beide und bekam das gleiche Ergebnis nach dem Programm zu töten und es wieder nach oben zu starten. Dies ist der Fehler: Starting Listener Fehler bei der Bindung: Adresse bereits in Verwendung über zu hören fertig hören Über die Annahme – user3735849

+0

Es funktioniert nicht richtig, wenn Sie das Programm auf die harte Tour töten. Sie müssen einen sauberen Ausgang durchführen. – Chnossos

+5

Der Zweck von SO_REUSEADDR/SO_REUSEPORT besteht darin, die Wiederverwendung des Ports zuzulassen, auch wenn der Prozess abstürzt oder abgebrochen wurde. – mpromonet

22

auf der libc Release Je es könnte benötigt werden, um beide SO_RE zu setzen USEADDR und SO_REUSEPORT Socket-Optionen wie in socket(7) Dokumentation erläutert:

SO_REUSEPORT (since Linux 3.9) 
      Permits multiple AF_INET or AF_INET6 sockets to be bound to an 
      identical socket address. This option must be set on each 
      socket (including the first socket) prior to calling bind(2) 
      on the socket. To prevent port hijacking, all of the 
      processes binding to the same address must have the same 
      effective UID. This option can be employed with both TCP and 
      UDP sockets. 

Da diese Socket-Option erscheint mit Kernel 3.9 und Himbeere Verwendung 3.12.x, wird es erforderlich sein, SO_REUSEPORT einzustellen.

Sie können festlegen, Thesen zwei Optionen vor binden wie folgt aufrufen:

int reuse = 1; 
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse)) < 0) 
     perror("setsockopt(SO_REUSEADDR) failed"); 

#ifdef SO_REUSEPORT 
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, (const char*)&reuse, sizeof(reuse)) < 0) 
     perror("setsockopt(SO_REUSEPORT) failed"); 
#endif