我在覆盆子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;
}
答案 0 :(得分:68)
之后:
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
您可以添加(使用标准C99复合材料支持):
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof(int)) < 0)
error("setsockopt(SO_REUSEADDR) failed");
或者:
int enable = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0)
error("setsockopt(SO_REUSEADDR) failed");
答案 1 :(得分:35)
根据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使用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
答案 2 :(得分:4)
我认为您应该使用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