bind()函数使用SO_REUSEADDR选项失败。

7
我的任务是实现一个通过TCP连接的两台计算机之间进行的双人游戏。其中一个要求是只有赢家才能选择是否再次进行游戏。如果服务器赢了并决定不再继续玩游戏,客户端应该重新启动作为服务器并接受新的连接。
我的方法是:如果在客户端模式下游戏失败,则关闭sockfd并重新创建另一个。然后使用setsockopt允许重新绑定使用SO_REUSEADDR,然后调用bind。
int yes = 1;
if ( setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1 )
{
    perror("setsockopt");
}

if ( bind(sockfd, (struct sockaddr*)&svr, sizeof(svr) ) == -1 )
{
    perror("server: bind");
}

但是,我仍然遇到了相同的“地址已在使用中”的错误。我尝试在重新创建套接字之前休眠150秒钟,这种方法有效。

注意:我正在同一台PC上测试。它可能适用于两个链接的PC,但必须使其在同一台PC上工作。请帮忙。


2
请展示您的代码(特别是setsockopt()部分),以便我们能够提供帮助。 - Greg Hewgill
5
你确定在客户端代码中重新创建之前已经正确关闭了服务器端(监听套接字)吗?在Linux系统上,"netstat -tlp" 命令可以确定是什么正在占用TCP端口并监听它。 - BRPocock
你在同一台机器上尝试两次绑定(bind())相同的{地址,端口号},你会期望发生什么? - wildplasser
好的,在等待了120秒之后,这种方法起作用了。所以我想使用SO_REUSEADDR可以期望它立即起作用。但是,请告诉我如果我错了。 - tecfreak
@BRPocock:你说得对。在套接字关闭之前有一个sleep(1),这就是为什么我无法绑定的原因。现在,它完美地工作了。感谢你的提示! :) - tecfreak
我不确定这两台计算机如何相互信号以确定使用哪个IP和端口。但是假设IP地址不是手动输入的,为什么不选择另一个端口(可能比之前的+1)呢?如果在默认端口上进行的后续重新连接失败,则开始递增并再次尝试。 - selbie
3个回答

4

SO_REUSEADDR只允许你同时绑定到一个更具体的地址,例如第一个服务器监听INADDR_ANY(所有接口),而后续服务器则监听不同的特定接口地址。

第二种情况是当监听TCP套接字接受一个连接,并保持使用,但监听套接字本身被关闭并重新打开 - 比如父服务器进程退出并重新启动。

在这两种情况下,您需要在调用bind(2)之前始终在监听套接字上设置SO_REUSEADDR选项。


那么,还有其他方法吗? 我在两次调用之前都使用了SO_REUSEADDR。 - tecfreak
通常使用此选项是为了避免在服务器监听特定端口时,一个刚刚关闭的套接字处于 TIME_WAIT 状态时需要等待 1-2 分钟才能重新启动。 - Maxim Egorushkin

2

由于您在同一系统上运行此操作,听起来您遇到了竞态条件。客户端试图在服务器关闭它之前(假设服务器和客户端都在其套接字上设置了SO_REUSEADDR)绑定套接字。

您需要实现某种握手,允许服务器在关闭侦听套接字后通知客户端 - 也许服务器应该在关闭最后一个游戏的活动套接字之前关闭侦听套接字?


是的,在套接字关闭之前有1秒的延迟,当我尝试重新绑定时没有延迟。现在它完美地工作了! :) - tecfreak

0

设置此套接字选项允许本地地址重用。如果尝试绑定到已关闭但未释放的端口时遇到问题(根据TIME_WAIT定义可能需要长达2分钟),应用SO_REUSEADDR套接字选项以立即释放资源并绕过TIME_WAIT状态。 0 = 禁用,1 = 启用。

允许其他套接字绑定()到此端口,除非已经绑定到该端口的活动侦听套接字。这使您能够在服务器崩溃后尝试重新启动时绕过那些“地址已在使用”错误消息。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接