1

3000 以上の接続を処理する必要があるゲームサーバー (TCP) を作成しています。現在、50 の接続があり、既に遅延が発生しています。

winsock send() 呼び出しが返されるのにそれぞれ約100〜300ミリ秒かかることがわかりました。これは、シングルスレッドサーバーであるため、サーバー全体の速度がかなり低下します。

これまでのところ、私は2つの解決策について考えてきました。

  1. クライアントごとにスレッドを作成するようにサーバーを再設計します (3000 クライアントに対して 3000 以上のスレッドを作成するのは本当に安定していますか?)。
  2. send() 呼び出しをすぐに返す方法を見つけてください。

これは私のソケットの初期化コードです:

int ret = WSAStartup(MAKEWORD(2,2), &wsaData);
if(ret != 0) 
{ 
    printf("Winsock failed to start.\n");
    system("pause");
    return; 
}

server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(52000);

sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock == INVALID_SOCKET) 
{ 
    printf("Invalid Socket.\n");
    system("pause");
    return; 
}

if(bind(sock, (sockaddr*)&server, sizeof(server)) != 0)
{ 
    printf("error");
    return; 
}

if(listen(sock, 5) != 0)
{
    printf("error");
    return;
}

別のスレッドでコードを受け入れる

sockaddr_in from;
int fromlen = sizeof(from);
SOCKET sTmpSocket = accept(ServerSock, (struct sockaddr*)&from, &fromlen);

私の送信機能

void CClient::SendPacket(BYTE* pPacket, DWORD Len)
{
    DWORD ToSendLen = Len;
    while (ToSendLen)
    {
        int iResult = send(this->ClientSocket, (char*)(pPacket + Len - ToSendLen), ToSendLen, 0);

        if (iResult == SOCKET_ERROR)
            return;

        ToSendLen -= iResult;
    }
}

これは私のサーバー スレッドです (不完全、関連部分のみ)

while (1)
{
    for (int i = 0; i < MAX_CLIENT; i++)
    {
        if (Clients[i].bConnected == false)
            continue;

        timeval timeout;
        timeout.tv_sec = 0;
        timeout.tv_usec = 1;
        fd_set socketset;
        socketset.fd_count = 1;
        socketset.fd_array[0] = Clients[i].ClientSocket;
        if (select(0, &socketset, 0, 0, &timeout))
        {
            int RecvLen = recv(Clients[i].ClientSocket, (char*)pBuffer, 10000, MSG_PEEK);
            if (RecvLen == SOCKET_ERROR)
            {
                Clients[i].bConnected = false;
                iNumClients--;
            }
            else if (RecvLen == 0)
            {
                Clients[i].bConnected = false;
                iNumClients--;
            }
            else if (RecvLen > 0)
            {
                // Packet handling here
                recv(Clients[i].ClientSocket, (char*)pBuffer, dwDataLen, MSG_WAITALL);

                //...
            }
        }
    }

    Sleep(1);
}

どんな助けでも大歓迎です。ありがとうございました。

4

4 に答える 4

4

私はIOCP ( IO 完了ポート) を使用します。これは、必要なパフォーマンスを実現でき、拡張性に優れています。内部で (Windows 上で) IOCPを使用しているBoost.Asioを見てください。

3000 以上のスレッドを持つという考えは非常に良くなく、(メモリ消費とコンテキスト スイッチの観点から) 実際には拡張できません!

于 2012-07-19T13:19:30.683 に答える
0

考慮すべきもう1つのアプローチは、アクティブな接続の一部を処理する複数のワーカースレッドを用意し、これを構成可能にすることです。たとえば、コアごとにn個のスレッドを作成してから、n個を試すことができます。

これにより、クライアントごとに1つのスレッドという極端なことをせずに、マルチスレッドの多くの利点が得られるはずです。

于 2012-07-19T14:28:57.940 に答える
0

すぐに思いつくことが 1 つあります。それは、ノンブロッキング ソケットを使用し、ソケットが .xml で書き込み可能かどうかを確認することですselect

select補足として、間違った戻り値を使用しています。0いずれかのセットにソケットの準備ができている場合、およびエラーがある場合は、タイムアウト時に正の数を返し-1ます。エラーをチェックしません。また、winsock バージョンでは必要ありませんが、最初の引数selectは実際には最大のソケット番号に 1 を加えたものにする必要があります。

別のポイントとして、一度に 1 つのクライアントに対してのみ使用し、すべてのクライアントをセットselectに追加してから、次のことを行います。select

fd_set readset;
FD_ZERO(&readset);
SOCKET max_socket = 0;

for (int i = 0; i < MAX_CLIENT; i++)
{
    if (Clients[i].bConnected)
    {
        FD_SET(Clients[i].ClientSocket, &readset);
        max_socket = std::max(max_socket, Clients[i].ClientSocket);
    }
}

int res = select(max_socket + 1, &readset, 0, 0, &timeout);
if (res == SOCKET_ERROR)
    std::cout << "Error #" << WSAGetLastError() << '\n';
else if (res > 0)
{
    for (int i = 0; i < MAX_CLIENT; i++)
    {
        if (Clients[i].bConnected && FD_ISSET(Clients[i].ClientSocket, &readset))
        {
            // Read from client
        }
    }
}
于 2012-07-19T13:20:35.093 に答える
0

小さなパケットのみを送信する場合は、最初に setsockopt() で TCP_NODELAY オプションを設定します。

于 2012-07-19T16:52:04.047 に答える