因此,我们有一个长期存在的商业产品,它已经很成熟,我以前从未见过这种类型的问题。我们使用客户端程序向服务器发送数据。有时,由于客户环境中的防火墙,我们允许最终用户指定要绑定(bind)的出站端口范围,但是,在我看到的这个特定问题中,我们没有这样做,而是使用端口 0 执行绑定(bind)。从我读过的所有内容来看,这意味着选择一个随机端口。但我不知道的是,这对内核/操作系统意味着什么。如果我要求一个随机端口,它怎么可能已经在使用中了? 严格来说,只有 src ip/src port & dst ip/port 的唯一配对才能使连接唯一。我相信如果与另一个目标 ip 通信,可以使用相同的端口,但也许这与这里无关。
此外,这不会发生在所有客户的系统上,只会发生在部分系统上。因此,这可能是某种形式的负载相关问题。有人告诉我,系统相当繁忙。
这是我们正在使用的代码。我省略了一些用于 windows 的 ifdef 代码,并且为了简洁省略了我们在绑定(bind)之后所做的事情。
_SocketCreateClient(Socket_pwtP sock, SocketInfoP sInfo )
{
int nRetries; /* number of times to try connect() */
unsigned short port;
BOOL success = FALSE;
BOOL gotaddr = FALSE;
char buf[INET6_ADDRSTRLEN] ="";
int connectsuccess =1;
int ipv6compat =0;
#ifdef SOCKET_SEND_TIMEOUT
struct timeval time;
#endif /* SOCKET_SEND_TIMEOUT */
nRetries = sInfo->si_nRetries;
sock->s_hostName = strdup(sInfo->si_hostName);
#ifdef DEBUG_SOCKET
LogWrite(LogF,LOG_WARNING,"Socket create client");
LogWrite(LogF,LOG_WARNING,"Number of retries = %d", nRetries);
#endif
ipv6compat = GetIPVer();
if (ipv6compat == -1) /* ipv6 not supported */
gotaddr = GetINAddr(sInfo->si_hostName, &sock->s_sAddr.sin_addr);
else
gotaddr = GetINAddr6(sInfo->si_hostName, &sock->s_sAddr6.sin6_addr);
/* translate supplied host name to an internet address */
if (!gotaddr) {
/* print this message only once */
if ( sInfo->si_logInfo && ( sInfo->si_nRetries == 1 ) )
{
LogWrite(LogF, LOG_ERR,
"unable to resolve ip address for host '%s'", sInfo->si_hostName);
}
sock = _SocketDestroy(sock);
}
else {
if (ipv6compat == 1) /* ipv6 supported */
{
/* try to print the address in sock->s_sAddr6.sin6_addr to make sure it's good. from call above */
LogWrite(LogF, LOG_DEBUG2, "Before call to inet_ntop");
inet_ntop(AF_INET6, &sock->s_sAddr6.sin6_addr, buf, sizeof(buf));
LogWrite (LogF, LOG_DEBUG2, "Value of sock->s_sAddr6.sin6_addr from GetINAddr6: %s", buf);
LogWrite (LogF, LOG_DEBUG2, "Value of sock->s_sAddr6.sin6_scope_id from if_nametoindex: %d", sock->s_sAddr6.sin6_scope_id);
LogWrite (LogF, LOG_DEBUG2, "Value of sock->s_type: %d", sock->s_type);
}
/* try to create the socket nRetries times */
while (sock && sock->s_id == INVALID_SOCKET) {
int socketsuccess = FALSE;
/* create the actual socket */
if (ipv6compat == -1) /* ipv6 not supported */
socketsuccess = sock->s_id = socket(AF_INET, sock->s_type, 0);
else
socketsuccess = sock->s_id = socket(AF_INET6, sock->s_type, 0);
if ((socketsuccess) == INVALID_SOCKET) {
GETLASTERROR;
LogWrite(LogF, LOG_ERR, "unable to create socket: Error %d: %s", errno,
strerror(errno) );
sock = _SocketDestroy(sock);
}
else
{
/* cycle through outbound port range for firewall support */
port = sInfo->si_startPortRange;
while ( !success && port <= sInfo->si_endPortRange ) {
int bindsuccess = 1;
/* bind to outbound port number */
if ( ipv6compat == -1) /* ipv6 not supported */
{
sock->s_sourceAddr.sin_port = htons(port);
bindsuccess = bind(sock->s_id, (struct sockaddr *) &sock->s_sourceAddr,
sizeof(sock->s_sourceAddr));
}
else {
sock->s_sourceAddr6.sin6_port = htons(port);
inet_ntop(AF_INET6, &sock->s_sourceAddr6.sin6_addr, buf, sizeof(buf));
LogWrite(LogF, LOG_DEBUG,
"attempting bind to s_sourceAddr6 %s ", buf);
bindsuccess = bind(sock->s_id, (struct sockaddr *) &sock->s_sourceAddr6,
sizeof(sock->s_sourceAddr6));
}
if (bindsuccess == -1) {
GETLASTERROR;
LogWrite(LogF, LOG_ERR,
"unable to bind port %d to socket: Error %d: %s. Will attempt next port if protomgr port rules configured(EAV_PORTS).", port, errno, strerror(errno) );
/* if port in use, try next port number */
port++;
}
else {
/* only log if outbound port was specified */
if (port != 0)
{
if ( sInfo->si_sourcehostName ) {
LogWrite(LogF, LOG_DEBUG,
"bound outbound address %s:%d to socket",
sInfo->si_sourcehostName, port);
}
else {
LogWrite(LogF, LOG_DEBUG,
"bound outbound port %d to socket", port);
}
}
success = TRUE;
}
}
}
}
}
return(sock);
}
我们在日志文件中看到的错误如下所示。它进行了 2 次尝试,但都失败了:
protomgr[628453]:错误:无法将端口 0 绑定(bind)到套接字:错误 98:地址已在使用中。如果配置了 protomgr 端口规则 (EAV_PORTS),将尝试下一个端口。
protomgr[628453]:错误:无法将端口绑定(bind)到套接字:错误 98:地址已在使用中。如果此消息来自 protomgr,请考虑增加 EAV_PORTS 的数量。
protomgr[628453]:错误:无法将端口 0 绑定(bind)到套接字:错误 98:地址已在使用中。如果配置了 protomgr 端口规则 (EAV_PORTS),将尝试下一个端口。
protomgr[628453]:错误:无法将端口绑定(bind)到套接字:错误 98:地址已在使用中。如果此消息来自 protomgr,请考虑增加 EAV_PORTS 的数量。
最佳答案
因此,这看起来与系统用尽可用端口有关,并且它被配置为只有大约 9000 个可用端口。
此设置在/etc/sysctl.conf 中控制可用端口: net.ipv4.ip_local_port_range = 9000 65500
第一个数字是起始端口,第二个是最大端口。此示例取自未更改的 Suse Enterprise linux 服务器 11.0。 报告此问题的我们的客户以这样的方式进行配置,即在他们定义的范围内只有大约 9000 个端口可用,并且所有端口都在系统上使用。
希望这对将来的其他人有所帮助。
关于c - 在 Linux 上,当使用 C 执行与端口 0(选择一个随机端口)的套接字绑定(bind)时,我得到 errno 98,Address already in use。这怎么可能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33813701/