我可以将任意源 IP 绑定到套接字吗?
Can I bind an arbitrary source IP to a socket?
我有一个在无限循环中运行的TCP服务器,如以下伪代码:
while (true)
{
auto new_connected_socket = accept(listening_socket,...);
// Here it may block for a long time
std::thread(fn_new_connection_handler, new_connected_socket);
}
如果管理员想要停止TCP服务器,则必须很好地打破无限循环。
我想到的一种方法是使用特殊的套接字连接到TCP服务器。套接字之所以"特殊",是因为它的源IP是"特殊的",例如,255.255.255.255显然不是有效的源IP。
如果它是合法的,那么客户端伪代码可能如下所示:
auto special_socket = socket(...);
bind(special_socket, "255.255.255.255", ...);
connect(special_socket, tcp_server_addr, ...);
服务器端伪代码可以如下所示:
while (true)
{
auto new_connected_socket = accept(listening_socket,...);
// Here it may block for a long time
if (IsSpecialSourceIp(new_connected_socket))
{
break; // Exit the server thread.
}
else
{
std::thread(fn_new_connection_handler, new_connected_socket);
}
}
这个灵魂可行吗?
如接受手册页中所述 ( man 2 accept
) 您可以将套接字标记为非阻塞操作,并使用选择或轮询来确定连接是否正在等待。甚至反复调用 accept 并检查 EAGAIN 或 EWILLBLOCK 作为返回值,中间有足够的停顿。
或轮询是更好的选择,因为您不需要"忙于等待",并且您会收到新连接的即时通知(延迟更低)。与阻止代码ond做一些晦涩的套接字魔术来关闭服务器相比,这当然是一个更好的解决方案。
与其尝试使用"特殊"地址,不如在应用内存中while
循环可以查看的地方设置一个标志,然后connect()
绑定到任何服务器机器本地 IP 的普通套接字(让connect()
为您处理绑定,您无需手动调用bind()
), 例如:
bool shutting_down = false;
.
while (!shutting_down)
{
auto new_connected_socket = accept(listening_socket,...);
// Here it may block for a long time
if (shutting_down)
{
closesocket(new_connected_socket);
break;
}
std::thread(fn_new_connection_handler, new_connected_socket);
}
.
shutting_down = true;
if (server is listening)
{
sockaddr_in server_addr = {0};
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(my server port);
server_addr.sin_addr.s_ddr = inet_addr("127.0.0.1");
SOCKET wakeup_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
connect(wakeup_socket, (sockaddr*)&server_addr, sizeof(server_addr));
closesocket(wakeup_socket);
}
更好的方法是简单地将侦听套接字置于非阻塞模式,并在循环中使用超时select()
,以便它可以定期唤醒:
while (!shutting_down)
{
fd_set rds;
FD_ZERO(&rds);
FD_SET(listening_socket, &rds);
timeval t;
t.tv_sec = 5;
t.tv_usec = 0;
if ((select(0, &rds, NULL, NULL, &t) > 0) && (!shutting_down))
{
auto new_connected_socket = accept(listening_socket,...);
std::thread(fn_new_connection_handler, new_connected_socket);
}
}
更好的是,使用 WSACreateEvent()
和 WSAWaitForMultipleEvents()
而不是 select()
:
WSAEVENT hShutdownEvent = WSACreateEvent();
.
WSAEVENT hAcceptEvent = WSACreateEvent();
WSAEventSelect(listening_socket, hAcceptEvent, FD_ACCEPT);
while (true)
{
WSAEVENT hEvents[2];
hEvents[0] = hAcceptEvent;
hEvents[1] = hShutdownEvent;
DWORD dwRet = WSAWaitForMultipleEvents(2, hEvents, FALSE, INFINITE, FALSE);
if (dwRet == WSA_WAIT_EVENT_0)
{
auto new_connected_socket = accept(listening_socket,...);
std::thread(fn_new_connection_handler, new_connected_socket);
}
else
break;
}
WSACloseEvent(hAcceptEvent);
.
if (server is listening)
WSASetEvent(hShutdownEvent);
相关文章:
- 无法将套接字绑定到地址
- 无法将套接字绑定到 IPv4 和 IPv6 中的端口
- 谁可以发送到绑定到INADDR_LOOPBACK的套接字?
- 为什么在此 UDP 客户端/服务器示例中没有必要绑定客户端套接字?
- QUdp套接字绑定失败
- 客户端套接字是可绑定的,但不可连接,因为已在使用中
- 在 ZeroMQ 中绑定订阅者套接字并连接发布者套接字会在代码运行时给出错误.为什么
- 使用 Boost.Asio 将 UDP 套接字绑定到特定网络接口
- 用于枚举绑定但未连接的套接字的 API
- udp套接字sendto隐式绑定
- QTcp服务器:使用自定义选项绑定套接字
- 当我尝试将UDP套接字绑定到带有SFML的端口时,为什么会遇到错误
- C 窗口无法绑定套接字
- Linux 拒绝使用 boost asio 的套接字绑定权限
- 将套接字绑定到具有未知 IP 的特定接口
- 如何强制 udp 服务器接收的 udp 客户端套接字绑定到特定客户端端口
- 将套接字绑定到特定网络
- 套接字绑定函数编译错误
- 套接字绑定不会返回 int
- 如何使用客户端套接字绑定