最近在学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 函数对套接字状态进行配置:
- int iSockOptVal = 1;
- if (setsockopt(iSockFd, SOL_SOCKET, SO_REUSEADDR, &iSockOptVal, sizeof(iSockOptVal)) == -1) {
- perror("setsockopt fail");
- close(iSockFd);
- exit(EXIT_FAILURE);
- }
上述代码中,函数原型为 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 |
|