2014-06-12 24 views
22

Sto eseguendo il mio server http su un Raspberry Pi. Il problema è quando interrompo il programma e lo riavvio, la porta non è più disponibile. A volte ho lo stesso problema quando ricevo molte richieste.
Desidero utilizzare SO_REUSEADDR in modo che possa continuare a utilizzare la porta anche quando si verifica l'errore, ma non ho avuto fortuna nell'ottenerlo. Di seguito è il mio codice.
L'errore che ricevo è "ERROR in binding: Address già in uso".Come si usa 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; 
} 

risposta

48

Dopo:

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

È possibile aggiungere (con supporto composto letterale di serie C99):

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

Oppure:

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

Ho provato entrambi ed ho ottenuto lo stesso risultato dopo aver ucciso il programma e iniziare il backup. Questo è l'errore: Starting Listener ERROR in binding: indirizzo già in uso in ascolto terminato in ascolto In attesa di – user3735849

+0

Non funziona correttamente se si uccide il programma nel modo più difficile. Devi eseguire un'uscita pulita. – Chnossos

+5

Lo scopo di SO_REUSEADDR/SO_REUSEPORT è di consentire il riutilizzo della porta anche se il processo si blocca o è stato interrotto. – mpromonet

22

A seconda della versione libc esso potrebbe essere necessario per impostare sia SO_RE USEADDR e SO_REUSEPORT opzioni socket come spiegato nella documentazione socket(7):

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. 

Come appare questa opzione presa con il kernel 3.9 e l'uso di lamponi 3.12.x, che saranno necessari per impostare SO_REUSEPORT.

È possibile impostare tesi due opzioni prima di chiamare bind in questo modo:

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