0

TCP レベルでネットと連携するツールの開発を開始しました。このツールは、Web サーバーの単純な機能を提供します。

私のプログラムをテストする際に、私は非常に悪い間違いを犯しました:

  • メモリリーク
  • 何千ものスレッドをすぐに作成する

taskmgr.exe では、約 1.5 個のスレッドと約 50kb の割り当てられたメモリが表示される場合があります。また、プログラムを 32 ビットとしてコンパイルしましたが、vmmap ユーティリティでは、多くの 64 ビット スタックが表示される場合があります。私のOSは64ビットですが、taskmgr.exeに *32 が表示される場合があります.32ビットプログラムが64ビットスタックをどのように使用するかわかりません.64ビットOSで32ビットプログラムを起動するのは正常かもしれませんが、このOSの設計は非常に気に入っていますので、この質問についてアドバイスをいただければ幸いです。

では、なぜ私のプログラムはすぐに多くのスレッドを作成したのでしょうか? (たぶん、while(true) ブロックの原因) .

しかし、私は次が欲しい:

  • 新しいリクエストごとに各スレッドを作成する
  • リクエストが処理されたら、スレッドを終了してメモリを解放します

コードをどのように作り直す必要がありますか?

ありがとう!

これが私のコードです(MS VC ++ 9):

#include <iostream>
#include <Windows.h>

#pragma comment(lib, "Ws2_32.lib")

typedef struct Header
{
friend struct Net;

private:
    WORD wsa_version;
    WSAData wsa_data;

    SOCKET sock;
    SOCKADDR_IN service;

    char *ip;
    unsigned short port;

public:
    Header(void)
    {
        wsa_version = 0x202;

        ip = "0x7f.0.0.1";
        port = 0x51;

        service.sin_family = AF_INET;
        service.sin_addr.s_addr = inet_addr(ip);
        service.sin_port = htons(port);
    }

} Header;

typedef struct Net
{
private:
    int result;

    HANDLE thrd;
    DWORD exit_code;

    void WSAInit(WSAData *data, WORD *wsa_version)
    {
        result = WSAStartup(*wsa_version, &(*data));

        if(result != NO_ERROR)
        {
            std::cout << "WSAStartup() failed with the error: " << result << std::endl;
        }
        else
        {
            std::cout << (*data).szDescription << " " << (*data).szSystemStatus << std::endl;
        }
    }

    void SocketInit(SOCKET *my_socket)
    {
        (*my_socket) = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

        if((*my_socket) == INVALID_SOCKET)
        {
            std::cout << "Socket initialization failed with the error: " << WSAGetLastError() << std::endl;
            WSACleanup();
        }
        else
        {
            std::cout << "Socket initialization successful!" << std::endl;
        }
    }

    void SocketBind(SOCKET *my_socket, SOCKADDR_IN *service)
    {
        result = bind((*my_socket), (SOCKADDR*)&(*service), sizeof(*service));

        if(result == SOCKET_ERROR)
        {
            std::cout << "Socket binding failed with the error: " << WSAGetLastError() << std::endl;
            closesocket((*my_socket));
            WSACleanup();
        }
        else
        {
            std::cout << "Socket binding successful!" << std::endl;
        }

        result = listen(*my_socket, SOMAXCONN);

        if(result == SOCKET_ERROR)
        {
            std::cout << "Socket listening failed with the error: " << WSAGetLastError() << std::endl;
        }
        else
        {
            std::cout << "Listening to the socket..." << std::endl;
        }
    }

    static void SocketAccept(SOCKET *my_socket)
    {
        SOCKET sock_accept = accept((*my_socket), 0, 0);

        if(sock_accept == INVALID_SOCKET)
        {
            std::cout << "Accept failed with the error: " << WSAGetLastError() << std::endl;
            closesocket(*my_socket);
            WSACleanup();
        }
        else
        {
            std::cout << "Client socket connected!" << std::endl;
        }

        char data[0x400];
        int result = recv(sock_accept, data, sizeof(data), 0);
        HandleRequest(data, result);
        char *response = "HTTP/1.1 200 OK\r\nServer: Amegas.sys-IS/1.0\r\nContent-type: text/html\r\nSet-Cookie: ASD643DUQE7423HFDG; path=/\r\nCache-control: private\r\n\r\n<h1>Hello World!</h1>\r\n\r\n";
        result = send(sock_accept, response, (int)strlen(response), 0);

        if(result == SOCKET_ERROR)
        {
            std::cout << "Sending data via socket failed with the error: " << WSAGetLastError() << std::endl;
            closesocket(sock_accept);
            WSACleanup();
        }
        else
        {
            result = shutdown(sock_accept, 2);
        }
    }

    static void HandleRequest(char response[], int length)
    {
        std::cout << std::endl;

        for(int i = 0; i < length; i++)
        {
            std::cout << response[i];
        }

        std::cout << std::endl;
    }

    static DWORD WINAPI Threading(LPVOID lpParam)
    {
        SOCKET *my_socket = (SOCKET*)lpParam;
        SocketAccept(my_socket);

        return 0;
    }

public:
    Net(void)
    {
        Header *obj_h = new Header();

        WSAInit(&obj_h->wsa_data, &obj_h->wsa_version);

        SocketInit(&obj_h->sock);
        SocketBind(&obj_h->sock, &obj_h->service);

        while(true)
        {
            thrd = CreateThread(NULL, 0, &Net::Threading, &obj_h->sock, 0, NULL);

            //if(GetExitCodeThread(thrd, &exit_code) != 0)
            //{
            //  ExitThread(exit_code);
            //}
        }

        delete &obj_h;
    }
} Net;

int main(void)
{
    Net *obj_net = new Net();

    delete &obj_net;

    return 0;
}
4

2 に答える 2

3

接続を受け入れる前ではなく、受け入れた後にスレッドを作成する必要があります。

あなたがしているのは、大量のスレッドを作成し、それぞれに接続を待機させることです。それらの多くは何の関係もありません。Windows の accept 呼び出しがスレッド セーフかどうかさえわかりません。複数のスレッドが同じ接続を処理することになるかもしれません。

代わりに、メイン ループ (Net のコンストラクター while(true)) で、accept() を呼び出す必要があります。accept()は接続が確立されるまでブロックされるため、誰かが接続を試みるまでメインスレッドが待機することになります。次に、その接続を処理する別のスレッド (またはプロセス - UNIX の場合が多い) を作成します。したがって、ループは次のようになります。

SOCKET sock_accept = accept((*my_socket), 0, 0);

if(sock_accept == INVALID_SOCKET)
{
    std::cout << "Accept failed with the error: " << WSAGetLastError() << std::endl;
    closesocket(*my_socket);
    WSACleanup();
}
else
{
    std::cout << "Client socket connected!" << std::endl;
}
thrd = CreateThread(NULL, 0, &Net::Threading, &obj_h->sock, 0, NULL);
//push back thrd into a std::vector<HANDLE> or something like that
//if you want to keep track of it for later: there's more than one thread

次に、SocketAccept からこのループに移動したコードを削除します。次に、見た目を良くするために、SocketAccept の名前を SocketHandleConnection に変更します。

これで、スレッドが開始された時点ですでに接続が確立されており、必要なのはデータを処理することだけです (たとえば、char data[0x400] から開始すること)。

接続のクリーンアップを処理する場合は、いくつかの方法があります。1 つ目は、スレッド化されているため、スレッドに独自のクリーンアップを実行させることができます。メインプロセスとメモリを共有するので、これを行うことができます。しかし、この例では、クリーンアップする必要があるものは何もありません。

最後に、ExitThread の機能を理解していないと思います。MSDNによると:

ExitThread は、C コードでスレッドを終了するための推奨される方法です。ただし、C++ コードでは、デストラクタが呼び出される前、またはその他の自動クリーンアップが実行される前に、スレッドが終了します。したがって、C++ コードでは、スレッド関数から戻る必要があります。

したがって、ExitThread を呼び出す必要はないようです。関数から戻るだけで、スレッドは自動的に終了します。メインスレッドから呼び出す必要はありません。

最後に、(可能であれば) c++11 で新しい標準 C++ スレッドを実際に使用する必要があります。その後、コードを boost::asio に移植するために少し努力すれば、完全にWindows API Cの醜さを必要としないクロスプラットフォームアプリケーション:D

免責事項: 私の経験のほとんどは UNIX に関連しているため、Windows についてはある程度しか理解していません。私はできる限り正確であるように努めましたが、この知識が Windows にどのように変換されるかについて誤解がある場合は、注意してください。

于 2012-04-29T14:05:21.507 に答える
2

無限ループでスレッドを作成しているのはなぜですか? もちろん、これにより大量のスレッドが作成されます。私はこのコードを参照しています:

while(true)
    {
        thrd = CreateThread(NULL, 0, &Net::Threading, &obj_h->sock, 0, NULL);
}
于 2012-04-29T14:25:38.010 に答える