C ++中的聊天程序返回无限消息

时间:2013-03-11 23:58:37

标签: c++ windows sockets chat

下面是一个聊天应用程序代码,我遇到了麻烦。

聊天系统的工作原理是拥有一台主服务器,所有客户端都连接到该服务器。所以,这是主服务器的代码。

#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;
}

现在,一切正常,除非您连接两个客户端(运行应用程序两次)并关闭其中一个客户端关闭应用程序垃圾邮件聊天时无限消息永远不会停止!有任何帮助解决这个问题吗?

我想问一个人是否有可能帮我评论源代码!

更新的代码:

#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;
}

2 个答案:

答案 0 :(得分:3)

当客户端正常断开连接时,recv()将返回0.当客户端异常断开连接或发生任何其他错误时,recv()将返回SOCKET_ERROR,然后您可以使用WSAGetLastError()找出错误的原因。当recv()返回&lt; = 0时,您需要处理这两个条件并使服务器“忘记客户端”(SOCKET_ERROR / WSAEWOULDBLOCK的特定情况除外,这不是致命错误)。目前,您正在处理套接字错误,就好像您确实从客户端接收了数据一样。

正在测试recv()的返回值为零,但这不是recv()返回错误的结果(SOCKET_ERROR实际上是别名-1,if (-1)评估为真,而不是假。)

答案 1 :(得分: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;
}
相关问题