如何使用setsockopt(SO_REUSEADDR)?

qybjjes1  于 2022-12-03  发布在  其他
关注(0)|答案(4)|浏览(120)

我在Raspberry pi上运行我自己的http服务器。问题是当我停止程序并重新启动它时,端口不再可用。有时我在收到大量请求时会遇到同样的问题。
我想使用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; 
}
pkwftd7m

pkwftd7m1#

在成功初始化套接字后设置该选项。因此,在:

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

您可以添加(具有标准C99 compound literal支持):

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

或者:

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

请注意,除了SO_REUSEADDR之外,您可能还需要设置SO_REUSEPORT以获得所需的行为。

0sgqnhkj

0sgqnhkj2#

根据libc发行版的不同,可能需要设置SO_REUSEADDR和SO_REUSEPORT套接字选项,如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.

由于此套接字选项出现在内核3.9和raspberry use 3.12.x中,因此需要设置SO_REUSEPORT。
可以在调用bind之前设置这两个选项,如下所示:

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
alen0pnh

alen0pnh3#

我认为你应该使用SO_LINGER选项(超时为0)。在这种情况下,你的连接会在关闭程序后立即关闭;并且下次重启将能够再次绑定。
例如:

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

参见定义: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.

关于SO_LINGER的更多信息:TCP option SO_LINGER (zero) - when it's required

2fjabf4q

2fjabf4q4#

这是如何在windows操作系统中使用:
要避免在Windows PC中的其他应用程序中重复使用端口:SO_独占地址
参考:https://learn.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse

相关问题