操作系统是Linux。 我有一个可以实时更改其端口的服务器进程。但是我想事先知道端口是否在绑定之前是免费的。
场景:服务器绑定localhost:5000并收到在localhost:6000绑定的请求。服务器必须检查端口是否空闲。这个问题寻求提供检查端口是否空闲的例程的答案。
为了记录,我正在使用代码片段来编辑我的问题,该代码片段检查端口是否可以免费使用。这并不意味着它会被使用。下面的代码回答了“如果端口现在可用”的问题,它不会使用它。打开一个套接字,检查bind是否返回EADDRINUSE并关闭套接字。
#include <iostream>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <errno.h>
int main(int argc, char **argv) {
struct sockaddr_in serv_addr;
if( argc < 2 )
return 0;
int port = atoi(argv[1]);
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if( sockfd < 0 ) {
printf("socket error\n");
return 0;
} else {
printf("Opened fd %d\n", sockfd);
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(port);
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
if( errno == EADDRINUSE )
{
printf("the port is not available. already to other process\n");
} else {
printf("could not bind to process (%d) %s\n", errno, strerror(errno));
}
}
if (close (sockfd) < 0 ) {
printf("did not close fd: %s\n", strerror(errno));
return errno;
}
return 0;
}
以下是一些示例运行(部分输出)
[bash{1051}{51}]:[~/some_sources/checkbind]::./a.out 41067
the port is not available. already to other process
[bash{1052}{52}]:[~/some_sources/checkbind]::./a.out 22
could not bind to process (13) Permission denied
[bash{1053}{53}]:[~/some_sources/checkbind]::./a.out 22000
Opened fd 3
答案 0 :(得分:12)
这是显而易见的race condition,因为系统上的其他进程可能并行绑定到端口。所以,你找到的任何解决方案都是不完美的,你仍然需要根据“尝试bind()
来编写它,如果它失败则选择一个新的端口号然后重试”方法
答案 1 :(得分:7)
如果您的服务器被告知要使用的端口,只需bind()
。严重。
当然,您可以解析/proc/net/tcp
并查看端口是否正在使用中。但那又怎样?现在您仍然需要调用bind()
,因为您知道您的端口是免费的,并且无论如何它都会告诉您该端口是否空闲,因此无需通过/proc/net/tcp
进行整理并完成所有操作那个(慢!)字符串生成和解析以及额外的内核跳转通过非常好的优化(读取:与bind()
相比超慢)诊断路径,只是为了获得可能在您之前过期的信息甚至完成解析它。所以,只需致电bind()
并高兴。
答案 2 :(得分:5)
我自己也很努力,并略微修改了你的代码。
解决方案是设置serv_addr.sin_port = 0
(自动分配端口)。
注意在bind()
和getsockname()
行中,有sockaddr_in
到sockaddr
的不洁演员阵容。我已经看到它做了很多地方,我正在寻找一个更安全的解决方案。
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
// ... snip ...
int sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock < 0) {
printf("socket error\n");
return;
}
printf("Opened %d\n", sock);
struct sockaddr_in serv_addr;
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = 0;
if (bind(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
if(errno == EADDRINUSE) {
printf("the port is not available. already to other process\n");
return;
} else {
printf("could not bind to process (%d) %s\n", errno, strerror(errno));
return;
}
}
socklen_t len = sizeof(serv_addr);
if (getsockname(sock, (struct sockaddr *)&serv_addr, &len) == -1) {
perror("getsockname");
return;
}
printf("port number %d\n", ntohs(serv_addr.sin_port));
if (close (sock) < 0 ) {
printf("did not close: %s\n", strerror(errno));
return;
}
Opened 4
port number 59081