0

これが私が問題を抱えているチャットアプリケーションコードです。

チャットシステムは、すべてのクライアントが接続するマスターサーバーを持つことで機能します。それで、これがマスターサーバーのコードです。

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

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

using namespace std;

SOCKADDR_IN addr;

SOCKET sListen;
SOCKET sConnect;
SOCKET* Connections;

int addrlen = sizeof(addr);
int ConCounter = 0;

struct Buffer
{
    int ID;
    char Message[256];
};

int ServerThread(int ID)
{
    Buffer sbuffer;

    char* Recv = new char[256];
    ZeroMemory(Recv, 256);

    char* Send = new char[sizeof(Buffer)];
    ZeroMemory(Send, sizeof(Buffer));

    for(;; Sleep(10))
    {
        if(recv(Connections[ID], Recv, 256, NULL))
        {
            sbuffer.ID = ID;
            memcpy(sbuffer.Message, Recv, 256);
            memcpy(Send, &sbuffer, sizeof(Buffer));

            for(int a = 0; a != ConCounter; a++)
            {
                if(Connections[a] == Connections[ID])
                {

                }
                else
                {
                    send(Connections[a], Send, sizeof(Buffer), NULL);
                }
            }
            ZeroMemory(Recv, 256);
        }
    }

    return 0;
}

int InitWinSock()
{
    int RetVal = 0;
    WSAData wsaData;
    WORD DllVersion = MAKEWORD(2,1);
    RetVal = WSAStartup(DllVersion, &wsaData);

    return RetVal;
}

int main()
{
    int RetVal = 0;
    RetVal = InitWinSock();
    if(RetVal != 0)
    {
        MessageBoxA(NULL, "Winsock startup failed", "Error", MB_OK | MB_ICONERROR);
        exit(1);
    }

    Connections = (SOCKET*)calloc(64, sizeof(SOCKET));

    sListen = socket(AF_INET, SOCK_STREAM, NULL);
    sConnect = socket(AF_INET, SOCK_STREAM, NULL);

    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    addr.sin_port        = htons(1234);
    addr.sin_family      = AF_INET;

    bind(sListen, (SOCKADDR*)&addr, sizeof(addr));

    listen(sListen, 64);

    for(;; Sleep(50))
    {
        if(sConnect = accept(sListen, (SOCKADDR*)&addr, &addrlen))
        {
            Connections[ConCounter] = sConnect;

            char* ID = new char[64];
            ZeroMemory(ID, sizeof(ID));

            itoa(ConCounter, ID, 10);
            send(Connections[ConCounter], ID, sizeof(ID), NULL);

            ConCounter = ConCounter + 1;
            CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE) ServerThread, (LPVOID)(ConCounter - 1), NULL, NULL);
        }
    }

    return 0;
}

クライアントチャットのソースは次のとおりです。

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

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

using namespace std;


SOCKADDR_IN addr;

SOCKET sConnect;

// For this we need to send two information at one time:
// 1. The main message
// 2. The ID

// To send more than one information I will use a struct
struct Buffer
{
    int ID;
    char Message[256];
};

int ClientThread()
{
    Buffer sbuffer;

    char buffer[sizeof(sbuffer)] = {0};

    for(;; Sleep(10))
    {
        // The server will send a struct to the client
        // containing message and ID
        // But send only accepts a char as buffer parameter
        // so here we need to recv a char buffer and then
        // we copy the content of this buffer to our struct
        if(recv(sConnect, buffer, sizeof(sbuffer), NULL))
        {
            memcpy(&sbuffer, buffer, sizeof(sbuffer));
            cout << "<Client " << sbuffer.ID << ":> " << sbuffer.Message <<endl;
        }
    }

    return 0;
}

int main()
{
    system("cls");

    int RetVal = 0;

    WSAData wsaData;
    WORD DllVersion = MAKEWORD(2,1);
    RetVal = WSAStartup(DllVersion, &wsaData);
    if(RetVal != 0)
    {
        MessageBoxA(NULL, "Winsock startup failed", "Error", MB_OK | MB_ICONERROR);
        exit(1);
    }

    sConnect = socket(AF_INET, SOCK_STREAM, NULL);

    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    addr.sin_port        = htons(1234);
    addr.sin_family      = AF_INET;

    cout << "Connect to Masterserver? [ENTER]" <<endl;
    getchar();
    RetVal = connect(sConnect, (SOCKADDR*)&addr, sizeof(addr));

    if(RetVal != 0)
    {
        MessageBoxA(NULL, "Could not connect to server", "Error", MB_OK | MB_ICONERROR);
        main();
    }
    else
    {
        int ID;
        char* cID = new char[64];
        ZeroMemory(cID, 64);

        recv(sConnect, cID, 64, NULL);
        ID = atoi(cID);

        cout << "Connected" <<endl;
        cout << "You are Client No: " << ID <<endl;

        CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE) ClientThread, NULL, NULL, NULL);

        for(;; Sleep(10))
        {
            char* buffer = new char[256];
            ZeroMemory(buffer, 256);

            cin >> buffer;
            getchar();

            send(sConnect, buffer, 256, NULL);
        }
    }

    return 0;
}

これで、たとえば2つのクライアントに接続して(アプリを2回実行)、そのうちの1つを閉じる場合を除いて、すべてが正常に機能します。アプリケーションを閉じたクライアントは、止まることのない無限のメッセージでチャットをスパムします。それを修正する助けはありますか?

可能であれば誰かにソースコードのコメントを手伝ってもらいたいです!

更新されたコード:

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

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

using namespace std;


SOCKADDR_IN addr;

SOCKET sConnect;

struct Buffer
{
    int ID;
    char Message[256];
};

int ClientThread()
{
    Buffer sbuffer;

    string buffer;
    //char buffer[sizeof(sbuffer)] = {0};

    for(;; Sleep(10))
    {
        if(recv(sConnect, buffer.c_str(), sizeof(sbuffer), NULL)!=SOCKET_ERROR)
        {
            strncpy(sbuffer.Message, buffer.c_str(), sizeof(sbuffer.Message));
            cout << "<Client " << sbuffer.ID << ":> " << sbuffer.Message <<endl;
        }
    }

    return 0;
}

int main()
{
    system("cls");

    int RetVal = 0;

    WSAData wsaData;
    WORD DllVersion = MAKEWORD(2,1);
    RetVal = WSAStartup(DllVersion, &wsaData);
    if(RetVal != 0)
    {
        MessageBoxA(NULL, "Winsock startup failed", "Error", MB_OK | MB_ICONERROR);
        exit(1);
    }

    sConnect = socket(AF_INET, SOCK_STREAM, NULL);

    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    addr.sin_port        = htons(1234);
    addr.sin_family      = AF_INET;

    cout << "Connect to Masterserver? [ENTER]" <<endl;
    getchar();
    RetVal = connect(sConnect, (SOCKADDR*)&addr, sizeof(addr));

    if(RetVal != 0)
    {
        MessageBoxA(NULL, "Could not connect to server", "Error", MB_OK | MB_ICONERROR);
        main();
    }
    else
    {
        int ID;
        char* cID = new char[64];
        ZeroMemory(cID, 64);

        recv(sConnect, cID, 64, NULL);
        ID = atoi(cID);

        cout << "Connected" <<endl;
        cout << "You are Client No: " << ID <<endl;

        CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE) ClientThread, NULL, NULL, NULL);

        for(;; Sleep(10))
        {
            char* buffer = new char[256];
            ZeroMemory(buffer, 256);

            getline(cin,buffer);
            //cin >> buffer;
            getchar();

            send(sConnect, buffer, 256, NULL);
        }
    }

    return 0;
}
4

2 に答える 2

3

クライアントが正常に切断すると、recv()は0を返します。クライアントが異常に切断した場合、またはその他のエラーが発生した場合は、recv()が戻り、エラーの原因を調べるためSOCKET_ERRORに使用できます。両方の条件を処理し、戻り値が0未満WSAGetLastError()の場合にサーバーを「クライアントを忘れる」ようにする必要があります(致命的なエラーではない/の特定の場合を除く)。現在、ソケットエラーは、クライアントから実際にデータを受信したかのように扱っています。recv()SOCKET_ERRORWSAEWOULDBLOCK

の戻り値をゼロに対してテストしていますが、それはエラーで返されるものではありません(実際には、-1のエイリアスであり、falseではなくtrueと評価されます)。recv()recv()SOCKET_ERRORif (-1)

于 2013-03-12T00:03:12.627 に答える
2

サーバーがループに陥る理由は、の戻り値をrecv()正しく使用していないためです。

コードには他にも問題があります(CreateThread()初心者にとっては、悪用)。

代わりにこれを試してください:

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

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

using namespace std;

SOCKADDR_IN addr;
int addrlen;

SOCKET sListen;
SOCKET sConnect;
SOCKET Connections[64];

struct Buffer
{
    int ID;
    char Message[256];
};

bool doSend(SOCKET s, void *data, int datalen)
{
    char pdata = (char*) data;

    while (datalen > 0)
    {
        int numSent = send(s, pdata, datalen, NULL);
        if (numSent < 1)
            return false;

        pdata += numSent;
        datalen -= numSent;
    }

    return true;
}

DWORD WINAPI ServerThread(LPVOID lpParam)
{
    int ID = (int) lpParam;
    SOCKET sThisClient = Connections[ConID];

    char cID[64];
    ZeroMemory(cID, sizeof(cID));
    itoa(ID, cID, 10);

    if (doSend(sThisClient, cID, sizeof(cID)))
    {
        Buffer sbuffer;
        sbuffer.ID = ID;
        ZeroMemory(sbuffer.Message, sizeof(sbuffer.Message));

        for (;; Sleep(10))
        {
            int numRecv = recv(sThisClient, sbuffer.Message, sizeof(sbuffer.Message), NULL);
            if (numRecv < 1)
                break;

            for (int a = 0; a < 64; a++)
            {
                SOCKET sOtherClient = Connections[a];
                if ((sOtherClient != INVALID_SOCKET) && (sOtherClient != sClient))
                    doSend(sOtherClient, &sbuffer, sizeof(Buffer));
            }
        }

        ZeroMemory(sbuffer.Message, sizeof(sbuffer.Message));
    }

    closesocket(Connections[ID]);
    Connections[ID] = INVALID_SOCKET;

    return 0;
}

int main()
{
    for (int i = 0; i < 64; ++i)
       Connections[i] = INVALID_SOCKET;

    WSAData wsaData;
    int RetVal = WSAStartup(MAKEWORD(2,1), &wsaData);
    if (RetVal != 0)
    {
        MessageBoxA(NULL, "Winsock startup failed", "Error", MB_OK | MB_ICONERROR);
        exit(1);
    }

    sListen = socket(AF_INET, SOCK_STREAM, NULL);
    if (sListen == INVALID_SOCKET)
    {
        MessageBoxA(NULL, "Socket create failed", "Error", MB_OK | MB_ICONERROR);
        exit(1);
    }

    sConnect = socket(AF_INET, SOCK_STREAM, NULL);
    if (sConnect == INVALID_SOCKET)
    {
        MessageBoxA(NULL, "Socket create failed", "Error", MB_OK | MB_ICONERROR);
        exit(1);
    }

    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    addr.sin_port        = htons(1234);
    addr.sin_family      = AF_INET;

    if (bind(sListen, (SOCKADDR*)&addr, sizeof(addr)) != 0)
    {
        MessageBoxA(NULL, "bind failed", "Error", MB_OK | MB_ICONERROR);
        exit(1);
    }

    if (listen(sListen, 64) != 0)
    {
        MessageBoxA(NULL, "listen failed", "Error", MB_OK | MB_ICONERROR);
        exit(1);
    }

    for(;; Sleep(50))
    {
        addrlen = sizeof(addr);

        sConnect = accept(sListen, (SOCKADDR*)&addr, &addrlen);
        if (sConnect != INVALID_SOCKET)
        {
            int ConID = -1;
            for (int i = 0; i < 64; ++i)
            {
                if (Connections[i] == INVALID_SOCKET);
                {
                    ConID = i;
                    break;
                }
            }

            if (ConID == -1)
            {
                closesocket(sConnect);
                continue;
            }

            Connections[ConID] = sConnect;

            HANDLE hThread = CreateThread(NULL, NULL, &ServerThread, (LPVOID)ConID, NULL, NULL);
            if (!hThread)
            {
                closesocket(sConnect);
                Connections[ConID] = INVALID_SOCKET;
                continue;
            }

            CloseHandle(hThread);
        }
    }

    return 0;
}

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

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

using namespace std;

SOCKADDR_IN addr;

SOCKET sConnect;

struct Buffer
{
    int ID;
    char Message[256];
};

bool doRecv(SOCKET s, void *data, int datalen)
{
    char pdata = (char*) data;

    while (datalen > 0)
    {
        int numRecv = recv(s, pdata, datalen, NULL);
        if (numRecv < 1)
            return false;

        pdata += numRecv;
        datalen -= numRecv;
    }

    return true;
}

DWORD WINAPI ClientThread(LPVOID lpParam)
{
    Buffer sbuffer;

    for(;; Sleep(10))
    {
        if (!doRecv(sConnect, &sbuffer, sizeof(sbuffer)))
            break;

        cout << "<Client " << sbuffer.ID << ":> " << sbuffer.Message << endl;
    }

    return 0;
}

int main()
{
    system("cls");

    WSAData wsaData;
    int RetVal = WSAStartup(MAKEWORD(2,1), &wsaData);
    if (RetVal != 0)
    {
        MessageBoxA(NULL, "Winsock startup failed", "Error", MB_OK | MB_ICONERROR);
        exit(1);
    }

    sConnect = socket(AF_INET, SOCK_STREAM, NULL);
    if (sConnect == INVALID_SOCKET)
    {
        MessageBoxA(NULL, "Socket create failed", "Error", MB_OK | MB_ICONERROR);
        exit(1);
    }

    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    addr.sin_port        = htons(1234);
    addr.sin_family      = AF_INET;

    do
    {
        cout << "Connect to Masterserver? [ENTER]" << endl;
        getchar();

        RetVal = connect(sConnect, (SOCKADDR*)&addr, sizeof(addr));
        if (RetVal == 0)
            break;

        MessageBoxA(NULL, "Could not connect to server", "Error", MB_OK | MB_ICONERROR);
    }
    while (true);

    char cID[64];
    ZeroMemory(cID, 64);

    if (!doRecv(sConnect, cID, 64))
        exit(1);

    int ID = atoi(cID);

    cout << "Connected" << endl;
    cout << "You are Client ID: " << ID << endl;

    if (!CreateThread(NULL, NULL, &ClientThread, NULL, NULL, NULL))
        exit(1);

    for(;; Sleep(10))
    {
        string buffer;
        getline(cin, buffer);

        doSend(sConnect, buffer.c_str(), buffer.length());
    }

    return 0;
}

更新:最近の更新を考えると、クライアントコードにまだ問題があります。上記で提供したコードを試してみましたか?以下は最近のコードの修正ですが、元のコードに関する他の多くの問題に対処する上記のコードを確認することをお勧めします。

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

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

using namespace std;

SOCKADDR_IN addr;

SOCKET sConnect;

struct Buffer
{
    int ID;
    char Message[256];
};

int ClientThread()
{
    Buffer sbuffer;

    char buffer[sizeof(sbuffer)];

    for(;; Sleep(10))
    {
        int numRead = recv(sConnect, &buffer, sizeof(buffer), NULL);
        if (numRead < 1) break;

        memcpy(&sbuffer, buffer, numRead);
        cout << "<Client " << sbuffer.ID << ":> " << sbuffer.Message << endl;
    }

    return 0;
}

int main()
{
    system("cls");

    int RetVal = 0;

    WSAData wsaData;
    WORD DllVersion = MAKEWORD(2,1);
    RetVal = WSAStartup(DllVersion, &wsaData);
    if (RetVal != 0)
    {
        MessageBoxA(NULL, "Winsock startup failed", "Error", MB_OK | MB_ICONERROR);
        exit(1);
    }

    sConnect = socket(AF_INET, SOCK_STREAM, NULL);

    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    addr.sin_port        = htons(1234);
    addr.sin_family      = AF_INET;

    do
    {
        cout << "Connect to Masterserver? [ENTER]" <<endl;
        getchar();

        RetVal = connect(sConnect, (SOCKADDR*)&addr, sizeof(addr));
        if (RetVal == 0) break;

        MessageBoxA(NULL, "Could not connect to server", "Error", MB_OK | MB_ICONERROR);
    }
    while (true);

    char cID[64];
    ZeroMemory(cID, 64);

    recv(sConnect, cID, 64, NULL);
    int ID = atoi(cID);

    cout << "Connected" << endl;
    cout << "You are Client No: " << ID << endl;

    CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE) ClientThread, NULL, NULL, NULL);

    for(;; Sleep(10))
    {
        string buffer;
        getline(cin, buffer);
        if (send(sConnect, buffer.c_str(), buffer.length(), NULL) < 1)
            exit(1);
    }

    return 0;
}
于 2013-03-12T00:30:55.683 に答える