![](/img/trans.png)
[英]What do I change in this code to not get “Bind: address already in use” perror? Socket/Linux/C
[英]On linux, when performing a socket bind with port 0 (pick a random port) using C, I get errno 98, Address already in use. How is that possible?
因此,我們有一個長期存在的商業產品,它已經建立了很好的地位,而且我以前從未見過此類問題。 我們使用客戶端程序將數據發送到服務器。 有時,由於客戶環境中的防火牆,我們允許最終用戶指定要綁定的出站端口范圍,但是,在我看到的這個特定問題中,我們沒有這樣做,而是使用端口0進行綁定。 從我閱讀的所有內容中,這意味着選擇一個隨機端口。 但是我不知道的是,這對內核/操作系統意味着什么。 如果我要一個隨機端口,那該如何使用呢? 嚴格來說,只有src ip / src端口和dst ip /端口的唯一配對才能使連接唯一。 我認為,如果要與另一個目標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,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綁定到套接字:錯誤98:地址已在使用中。 如果配置了protomgr端口規則(EAV_PORTS),將嘗試下一個端口。
protomgr [628453]:錯誤:無法將端口綁定到套接字:錯誤98:地址已在使用中。 如果此消息來自protomgr,請考慮增加EAV_PORTS的數量。
protomgr [628453]:錯誤:無法將端口0綁定到套接字:錯誤98:地址已在使用中。 如果配置了protomgr端口規則(EAV_PORTS),將嘗試下一個端口。
protomgr [628453]:錯誤:無法將端口綁定到套接字:錯誤98:地址已在使用中。 如果此消息來自protomgr,請考慮增加EAV_PORTS的數量。
因此,這似乎與系統用盡可用端口有關,並且已將其配置為僅具有約9000個可用端口。
/etc/sysctl.conf中的此設置控制可用端口:net.ipv4.ip_local_port_range = 9000 65500
第一個數字是起始端口,第二個是最大值。 本示例是從未更改的Suse Enterprise linux服務器11.0中提取的。 報告此問題的客戶對他們的配置方式如下:在他們定義的范圍內,只有大約9000個可用端口,並且所有端口都在系統上使用。
希望這對將來的人有所幫助。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.