Linux 套接字编程中 bind 错误:bind fail:Address already in use 解决方法

最近在学Linux 网络编程,调试TCP并发服务器时遇到一个问题,当我连接上一个或多个客户端后,用 CTRL+C 关闭进程后,重新打开进程就发生错误了:bind fail:Address already in use

地址被占用???

开始我以为是套接字描述符未关闭,添加代码在发生错误时关闭掉套接字描述符还是不行。

上网查了才发现是原来是套接字状态未配置,IBM官网上有较为详细的解释Linux 套接字编程中的 5 个隐患

下面截取文中一段相关内容:

隐患 3.地址使用错误(EADDRINUSE)

您可以使用 bind API 函数来绑定一个地址(一个接口和一个端口)到一个套接字端点。可以在服务器设置中使用这个函数,以便限制可能有连接到来的接口。也可以在客户端设置中使用这个函数,以便限制应当供出去的连接所使用的接口。bind 最常见的用法是关联端口号和服务器,并使用通配符地址(INADDR_ANY),它允许任何接口为到来的连接所使用。

bind 普遍遭遇的问题是试图绑定一个已经在使用的端口。该陷阱是也许没有活动的套接字存在,但仍然禁止绑定端口(bind 返回EADDRINUSE),它由 TCP 套接字状态 TIME_WAIT 引起。该状态在套接字关闭后约保留 2 到 4 分钟。在 TIME_WAIT 状态退出之后,套接字被删除,该地址才能被重新绑定而不出问题。

等待 TIME_WAIT 结束可能是令人恼火的一件事,特别是如果您正在开发一个套接字服务器,就需要停止服务器来做一些改动,然后重启。幸运的是,有方法可以避开 TIME_WAIT 状态。可以给套接字应用 SO_REUSEADDR 套接字选项,以便端口可以马上重用。


原来是TCP 套接字状态 TIME_WAIT 引起的,解决方法就是用 setsockopt 函数对套接字状态进行配置:

  1. int iSockOptVal = 1;  
  2. if (setsockopt(iSockFd, SOL_SOCKET, SO_REUSEADDR, &iSockOptVal, sizeof(iSockOptVal)) == -1) {  
  3.     perror("setsockopt fail");  
  4.     close(iSockFd);  
  5.     exit(EXIT_FAILURE);  
  6. }  

上述代码中,函数原型为 setsockopt (int s, int level, int optname, const void *optval, socklen_t optlen),

参数s为 socket 描述符;

level 代表预设置的网络层,一般设置为 SOLSOCKET 以存取 socket 层;

optname 代表预设置的选项,设为 SO_REUSEADDR 表示允许在 bind() 时本地地址可重复复用;

optval 代表预设置的值的指针,在这里传入1表示允许地址重复,传入0表示不允许地址重复; 

optlen 则为 optval 的长度。

关于 setsockopt 函数的配置参数的一些解析及应用可参考这篇文章:setsockopt 用法详解

大笑

附上我的TCP并发服务器测试代码:

   1
   2
   3
   4
   5
   6
   7
   8
   9
  10
  11
  12
  13
  14
  15
  16
  17
  18
  19
  20
  21
  22
  23
  24
  25
  26
  27
  28
  29
  30
  31
  32
  33
  34
  35
  36
  37
  38
  39
  40
  41
  42
  43
  44
  45
  46
  47
  48
  49
  50
  51
  52
  53
  54
  55
  56
  57
  58
  59
  60
  61
  62
  63
  64
  65
  66
  67
  68
  69
  70
  71
  72
  73
  74
  75
  76
  77
  78
  79
  80
  81
  82
  83
  84
  85
  86
  87
  88
  89
  90
  91
  92
  93
  94
  95
  96
  97
  98
  99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
      
      
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <time.h>
#include <pthread.h>
#include <unistd.h>
static void * clientHandle ( void * arg );
int main ( int argc , char * argv [])
{
int iNetPort = 0 ;
int iSockFd = 0 ;
int iClientFd = 0 ;
int iSerAddrLen = 0 ;
int iSockOptVal = 1 ;
pthread_t iThreadId = 0 ;
struct sockaddr_in SERVERADDR ;
/*
* 输入参数检查
*/
if ( argc != 2 ) {
printf ( "input one arg \r\n " );
exit ( EXIT_FAILURE );
}
iNetPort = atoi ( argv [ 1 ]);
/*
* 建立套接字描述符
*/
if (( iSockFd = socket ( AF_INET , SOCK_STREAM , 0 )) == - 1 ) {
perror ( "socket fail" );
exit ( EXIT_FAILURE );
}
/*
* 设置套接字状态
*/
if ( setsockopt ( iSockFd , SOL_SOCKET , SO_REUSEADDR , & iSockOptVal , sizeof ( iSockOptVal )) == - 1 ) {
perror ( "setsockopt fail" );
close ( iSockFd );
exit ( EXIT_FAILURE );
}
/*
* 绑定服务器与套接字
*/
bzero ( & SERVERADDR , sizeof ( SERVERADDR ));
SERVERADDR . sin_family = AF_INET ;
SERVERADDR . sin_port = htons ( iNetPort );
SERVERADDR . sin_addr . s_addr = htonl ( INADDR_ANY );
if ( bind ( iSockFd , ( struct sockaddr * ) & SERVERADDR , sizeof ( SERVERADDR )) == - 1 ) {
perror ( "bind fail" );
close ( iSockFd );
exit ( EXIT_FAILURE );
}
/*
* 监听指定端口,最大连接5个客户端
*/
if ( listen ( iSockFd , 5 ) == - 1 ) {
perror ( "listen fail" );
close ( iSockFd );
exit ( EXIT_FAILURE );
}
/*
* 为每个连接的客户端建立一个线程
*/
while ( 1 ) {
iSerAddrLen = sizeof ( SERVERADDR );
if (( iClientFd = accept ( iSockFd , ( struct sockaddr * ) & SERVERADDR , & iSerAddrLen )) == - 1 ) {
if ( errno == EINTR ) {
close ( iSockFd );
continue ;
}
else {
perror ( "accept fail" );
close ( iSockFd );
exit ( EXIT_FAILURE );
}
}
/*
* 打印客户地址
*/
printf ( "Client IP:%s \r\n " , inet_ntoa ( SERVERADDR . sin_addr ));
printf ( "Client PORT:%d \r\n " , ntohs ( SERVERADDR . sin_port ));
/*
* 新建一个线程
*/
if ( pthread_create ( & iThreadId , NULL , clientHandle , & iClientFd ) == - 1 ) {
perror ( "pthread_create fail" );
close ( iSockFd );
close ( iClientFd );
exit ( EXIT_FAILURE );
}
}
close ( iSockFd );
close ( iClientFd );
exit ( EXIT_SUCCESS );
}
static void * clientHandle ( void * arg )
{
int iClientFd = * ( int * ) arg ;
int iReadByteSize = 0 ;
char cRcvSndBuf [ 100 ];
time_t tTime ;
printf ( "Client Fd:%d \r\n " , iClientFd );
while ( 1 ) {
/*
* 接受客户端信息
*/
memset ( cRcvSndBuf , 0 , sizeof ( cRcvSndBuf ));
if (( iReadByteSize = read ( iClientFd , cRcvSndBuf , sizeof ( cRcvSndBuf ))) == - 1 ) {
perror ( "read fail" );
close ( iClientFd );
return 0 ;
}
else if ( iReadByteSize == 0 ) {
printf ( "Client not connect" );
close ( iClientFd );
return 0 ;
}
if ( strncmp ( cRcvSndBuf , "end" , 3 ) == 0 ) {
printf ( "thread close:%d \r\n " , iClientFd );
close ( iClientFd );
return 0 ;
}
else if ( strncmp ( cRcvSndBuf , "time" , 4 ) == 0 ) {
tTime = time ( NULL );
sprintf ( cRcvSndBuf , "TIME:%s" , ctime ( & tTime ));
}
else {
cRcvSndBuf [ iReadByteSize ] = '\n' ;
cRcvSndBuf [ iReadByteSize + 1 ] = '\0' ;
}
write ( iClientFd , cRcvSndBuf , strlen ( cRcvSndBuf ));
}
return 0 ;
} 转载:http://blog.csdn.net/bruno_mars/article/details/52384623
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值