78

I am running my own http server on a raspberry pi. The problem is when I stop the program and restart it, the port is no longer available. Sometimes I get the same issue when receiving lots of requests.
I want to use SO_REUSEADDR so that I can keep using the port even when the error occurs but have had no luck getting it set up. Below is my code.
The error I get is "ERROR on binding:Address already in use".

#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; 
}
mpromonet
  • 11,326
  • 43
  • 62
  • 91
user3735849
  • 893
  • 1
  • 6
  • 6

4 Answers4

109

Set the option after the socket has been successfully initialized. So, after:

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

You can add (with standard C99 compound literal support):

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

Or:

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

Note than in addition to SO_REUSEADDR, you might need to set SO_REUSEPORT to get the desired behavior. This is done exactly the same way for both options.

Chnossos
  • 9,971
  • 4
  • 28
  • 40
  • 1
    I tried both and got the same result after killing the program and starting it back up. This is the error: Starting Listener ERROR on binding: Address already in use about to listen finished listening About to accept – user3735849 Jun 14 '14 at 02:00
  • 11
    The purpose of SO_REUSEADDR/SO_REUSEPORT is to allow to reuse the port even if the process crash or been killed. – mpromonet Aug 08 '14 at 22:07
  • @Chnossos I just test on a raspberry pi with kernel 3.12.25, the bind works well even when the port is still used by TIME_WAIT sockets (what occurs with a kill SIGKILL). Did you insert the setsockopt before the bind call ? – mpromonet Aug 09 '14 at 17:16
  • Could you explain that `&(int){1}`? I can't quite grok that. – Daniel Walker Jan 18 '23 at 03:16
  • 1
    @DanielWalker Sure, follow the link for C99 compound literal in my answer. Once you understand what it is, observe that I simply pass the address of it as an argument. – Chnossos Jan 19 '23 at 10:46
50

Depending on the libc release it could be needed to set both SO_REUSEADDR and SO_REUSEPORT socket options as explained in socket(7) documentation :

   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.

As this socket option appears with kernel 3.9 and raspberry use 3.12.x, it will be needed to set SO_REUSEPORT.

You can set theses two options before calling bind like this :

    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
mpromonet
  • 11,326
  • 43
  • 62
  • 91
5

I think you should use SO_LINGER options (with timeout 0). In this case, you connection will close immediately after closing your program; and next restart will be able to bind again.

example:

linger lin;
lin.l_onoff = 0;
lin.l_linger = 0;
setsockopt(fd, SOL_SOCKET, SO_LINGER, (const char *)&lin, sizeof(int));

see definition: http://man7.org/linux/man-pages/man7/socket.7.html

SO_LINGER
          Sets or gets the SO_LINGER option.  The argument is a linger
          structure.

              struct linger {
                  int l_onoff;    /* linger active */
                  int l_linger;   /* how many seconds to linger for */
              };

          When enabled, a close(2) or shutdown(2) will not return until
          all queued messages for the socket have been successfully sent
          or the linger timeout has been reached.  Otherwise, the call
          returns immediately and the closing is done in the background.
          When the socket is closed as part of exit(2), it always
          lingers in the background.

More about SO_LINGER: TCP option SO_LINGER (zero) - when it's required

Meir Gerenstadt
  • 3,573
  • 1
  • 23
  • 22
  • 2
    I believe it should say `lin.l_onoff = 1;`. Otherwise you're disabling the option altogether, which means it would linger in the background. – dragonroot Mar 08 '19 at 22:00
  • 2
    with either the way it is in the answer or the comment, I get failure to bind if I stop and immediately start my program. If I put SO_REUSEADDR, then it works great. – xaxxon May 22 '19 at 23:06
0

This is how to use in windows os:

To avoid Port Reuse in another application in windows PC: SO_EXCLUSIVEADDRUSE

reference: https://learn.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse

Yunnosch
  • 26,130
  • 9
  • 42
  • 54
srinath R
  • 21
  • 1
  • 6