调用listen(fd,backlog)后是否可以在套接字上取消监听?
编辑:我没有让自己清楚的错误。我希望能够暂时不听套接字。调用 close() 将使套接字处于 M2LS 状态并阻止我重新打开它(或者更糟糕的是,某些恶意程序可能会绑定到该套接字)
暂时不监听是向上游负载均衡器发出信号表明此应用暂时无法接受更多请求的一种方式(可能不是最好的方式)
关闭套接字后,您的程序可能仍会告诉您套接字正在“使用中”,这是因为一些我不完全了解的怪异。但是关于套接字的手册页显示有一个标志可以重复使用同一个套接字,懒惰地称为:“SO_REUSEADDR”。使用“setsockopt()”设置它。
关闭它。据我回忆;
close(fd);
一些套接字库允许您专门拒绝传入的连接。例如:GNU 的 CommonC++:TCPsocket 类有一个拒绝方法。
BSD Sockets 没有这个功能。您可以接受连接,然后立即关闭它,同时保持套接字打开:
while (running) {
int i32ConnectFD = accept(i32SocketFD, NULL, NULL);
while (noConnectionsPlease) {
shutdown(i32ConnectFD, 2);
close(i32ConnectFD);
break;
}
}
根据您编辑的问题版本,我不确定您是否必须“不听”或关闭()。想到两个选择:
1) 在调用listen() 之后,连接实际上不会被接受,直到(从逻辑上讲)你调用accept()。您可以通过简单地忽略套接字活动并推迟任何 accept() 直到您准备好来“不听”。任何入站连接尝试积压到在侦听模式下打开端口时创建的队列。一旦堆栈中的积压队列已满,进一步的连接尝试就会被简单地丢弃在地板上。当您使用 accept() 恢复时,您将快速将积压工作出列并为更多连接做好准备。
2) 如果您真的希望端口暂时完全关闭,您可以动态地将内核级别的数据包过滤器应用于端口,以防止入站连接尝试到达网络堆栈。例如,您可以在大多数 *nix 平台上使用 Berkeley Packet Filter (BPF)。也就是说,您希望使用平台的防火墙功能丢弃进入感兴趣端口的入站数据包。当然,这因平台而异,但这是一种可能的方法。
我认为这不是向上游负载均衡器发出信号的好方法。在消息通过之前,它实际上必须向您的服务器发送一些连接——这些连接可能会被拒绝。
同样,关闭侦听套接字时挂起的任何连接都将在没有数据的情况下关闭。
如果您想向上游负载均衡器发出信号,您应该有一个协议来执行此操作。不要试图滥用 TCP 来做到这一点。
幸运的是,如果客户端是普通的 Web 浏览器,你可以摆脱很多 - 简单地关闭套接字通常会导致它们对用户透明地重试(到一定程度)。
没有明确的方法可以不听!
您可以close(fd)
或shutdown(fd, how)
fd is the socket file descriptor you want to shutdown, and how is one of the following:
0 Further receives are disallowed
1 Further sends are disallowed
2 Further sends and receives are disallowed (like close())
在基本层面上,套接字要么是打开的,要么是关闭的(我们将在这里忽略 TCP/IP 状态图的细节)。
如果您的套接字已关闭,则无法向其发送数据。如果它是打开的,那么传入的数据将被 TCP/IP 堆栈接受并确认,直到它的缓冲算法发出“够了!”。届时,将不会确认进一步的数据。
我可以看到你有两个选择。当您想“取消侦听”时关闭()套接字,然后稍后重新打开它 - 使用带有 SO_REUSEADDR 标志的 setsockopt(),以允许您在 TIME_WAIT2 到期之前重新绑定到众所周知的端口。
另一种选择是保持套接字打开,但在您“忙”时根本不接受()。假设您对请求有应用程序级别的确认,您的负载均衡器会意识到它没有得到响应并采取相应的行动。
根据您编辑的问题,这是一种相当丑陋的方法:
打开一个套接字以正常积压进行侦听。继续。
当你想“关闭”时,打开一个积压为 1 和 SO_REUSEADDR 的第二个。关闭第一个。当准备好恢复时,对一个正常积压的套接字进行另一个套接字处理。
从您正在关闭的套接字中排出接受队列的挑剔细节将成为这里的杀手。可能足以使这种方法不可行的杀手。
我不一定认为这是一个好主意,但是...
你也许可以再打电话听一下。POSIX 规范并没有说不。当您想“不听”时,也许您可以使用积压参数 0 再次调用它。
当使用积压为 0 调用 listen 时会发生什么似乎是实现定义的。POSIX 规范说它可能允许接受连接,这意味着如果 backlog 参数为 0,某些实现可能会选择拒绝所有连接。但更有可能的是,当您传入 0(可能是 1 或索马克康)。
问题没有说什么样的插座。如果是 unix 套接字,您可以使用 rename(2) 停止并开始侦听。您还可以使用 unlink(2) 永久停止侦听,并且由于套接字保持打开状态,您可以继续为您的积压工作提供服务。这种方法似乎很方便,虽然我以前没有见过使用过,只是自己在探索。
您已经得到了一些关于无法通过套接字 API 执行此操作的答案。
您可以使用其他操作系统方法(即 Host firwewall/iptables/ipfilter)来设置临时拒绝规则。
我发现大多数负载均衡器在识别连接问题的可能性方面都受到限制(大多数负载均衡器仅在连接探测中识别 RST,而不是对合法连接尝试的回答。)
无论如何,如果您受到检测不可用的探针的限制,您可以设置一个应用程序级别的探针,它会执行 HTTP 请求或 FTP 登录或类似的事情,如果您在接受后简单地关闭它就会识别。它甚至可以解释诸如“500 服务不可用”之类的错误消息,无论如何这对我来说似乎更干净。使用 SNMP,一些负载平衡器也可以将结果用作负载提示。