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 サーバーを停止したい場合は、うまく無限ループを断ち切ることが不可欠です。

私が考えた 1 つの方法は、特別なソケットを使用して 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非ブロッキング操作用にソケットをマークし、selectまたはpollを使用して、接続が待機しているかどうかを調べることができます。または、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()andを使用します。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);
于 2013-01-28T18:38:30.210 に答える