1

我有一个 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);
    }
}

这种解决方案可行吗?

4

2 回答 2

2

accept ( man 2 accept) 的手册页中所述,您可以将套接字标记为非阻塞操作,并使用selectpoll来确定连接是否正在等待。甚至重复调用 accept 并检查 EAGAIN 或 EWOULDBLOCK 作为返回值,中间有足够的停顿。

当然 select 或 poll 是更好的选择,因为您不需要“忙等待”并且您会收到新连接的即时通知(较低的延迟)。与阻塞代码并执行一些晦涩的套接字魔术来关闭服务器相比,这无疑是一个更好的解决方案。

于 2013-01-26T07:26:39.913 回答
0

不要尝试使用“特殊”地址,只需在应用程序的内存中设置一个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()andWSAWaitForMultipleEvents()代替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);
于 2013-01-28T18:38:30.210 回答