如何在C中找到可以免费使用的端口?

cat*_*eof 11 c sockets linux network-programming

操作系统是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;


}
Run Code Online (Sandbox Code Playgroud)

以下是一些示例运行(部分输出)

[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
Run Code Online (Sandbox Code Playgroud)

unw*_*ind 11

这是一个明显的竞争条件,因为系统上的其他进程可能并行绑定到端口.因此,您找到的任何解决方案都将是不完美的,您仍然需要根据"尝试bind(),如果它失败选择新的端口号并再试一次"方法来编写它.

  • @cateof如果只有进程尝试绑定,这是否意味着其他端口必须是空闲的? (3认同)
  • @cateof也许两个进程同时启动,想想在程序启动时`sshd`正在重启.如果您无法控制所涉及的所有参数,请始终假设竞争条件适用.所以只要尝试绑定,看看它是否成功.这是迄今为止最好,最安全的方式. (3认同)

bdo*_*lan 7

如果您的服务器被告知要使用哪个端口,那就是bind()它.认真.

当然,您可以解析/proc/net/tcp并查看端口是否正在使用中.但那又怎样?你bind()现在仍然需要打电话,你知道你的端口是免费的,并且它会告诉你端口是否有空,然后无论如何都没有任何意义,所以没有必要/proc/net/tcp去做所有的事情(慢!)字符串生成和解析额外的内核通过非常好的优化(读取:超慢速bind())诊断路径,只是为了获得在解析之前可能已经过时的信息.所以,只要打电话bind(),快乐.


neo*_*eye 6

我自己也在努力,并略微修改了你的代码.

解决方案是设置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;
}
Run Code Online (Sandbox Code Playgroud)

程序输出

Opened 4
port number 59081
Run Code Online (Sandbox Code Playgroud)