在linux上,当使用C执行与端口0(选择随机端口)的套接字绑定时,我
因此,我们有一个长期存在的商业产品,这已经很成熟,我以前从未见过这类问题.我们使用客户端程序将数据发送到服务器.有时,由于客户环境中的防火墙,我们允许最终用户指定要绑定的出站端口范围,但是,在我看到的这个特定问题中,我们没有这样做,并且正在使用端口0来执行绑定.从我读过的所有内容来看,这意味着选择一个随机端口.但我无法找到的是,这对内核/操作系统意味着什么.如果我要求一个随机端口,那么它是如何使用的呢?
严格来说,只有src ip / src端口的唯一配对. dst ip / port使连接唯一.我相信可以使用相同的端口,如果与另一个目标IP通信,但也许这与此无关. 此外,这不会发生在所有客户的系统上,只有一些.因此,这可能是某种形式的负载相关问题.据我所知,系统相当繁忙. 这是我们正在使用的代码.我遗漏了一些用于windows的ifdef代码,并在绑定后省略了我们的操作. _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,"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,"Value of sock->s_sAddr6.sin6_addr from GetINAddr6: %s",buf); LogWrite (LogF,"Value of sock->s_sAddr6.sin6_scope_id from if_nametoindex: %d",sock->s_sAddr6.sin6_scope_id); LogWrite (LogF,"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,0); if ((socketsuccess) == INVALID_SOCKET) { GETLASTERROR; LogWrite(LogF,"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,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,"unable to bind port %d to socket: Error %d: %s. Will attempt next port if protomgr port rules configured(EAV_PORTS).",port,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,"bound outbound address %s:%d to socket",sInfo->si_sourcehostName,port); } else { LogWrite(LogF,"bound outbound port %d to socket",port); } } success = TRUE; } } } } } return(sock); } 我们在日志文件中看到的错误如下所示.它正在进行2次尝试并且都失败了: protomgr [628453]:错误:无法将端口0绑定到套接字:错误98:地址已在使用中.如果配置了protomgr端口规则(EAV_PORTS),将尝试下一个端口. protomgr [628453]:错误:无法将端口绑定到套接字:错误98:地址已在使用中.如果此消息来自protomgr,请考虑增加EAV_PORTS的数量. protomgr [628453]:错误:无法将端口0绑定到套接字:错误98:地址已在使用中.如果配置了protomgr端口规则(EAV_PORTS),请考虑增加EAV_PORTS的数量. 解决方法
因此,看起来这与系统耗尽可用端口有关,并且它被配置为只有大约9000个端口可用.
/etc/sysctl.conf中的此设置控制可用端口: 第一个数字是起始端口,第二个是最大数字.这个例子是从未经改动的Suse Enterprise Linux服务器11.0中提取的. 希望这对未来的其他人有所帮助. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |