聊天程序在c++中返回无限消息

Chat program in C++ returning infinite messages

本文关键字:返回 无限 消息 c++ 程序 聊天      更新时间:2023-10-16

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

聊天系统的工作原理是有一个主服务器,所有客户端都连接到主服务器。下面是主服务器的代码。

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

当客户端正常断开连接时,recv()将返回0。当客户端异常断开连接,或发生任何其他错误时,recv()将返回SOCKET_ERROR,然后您可以使用WSAGetLastError()来找出错误的原因。您需要处理这两种情况,并使服务器在recv()返回<= 0时"忘记客户端"(SOCKET_ERROR/WSAEWOULDBLOCK的特定情况除外,这不是致命错误)。当前你正在处理套接字错误,就好像你真的从客户端收到了数据。

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

服务器陷入循环的原因是因为您没有正确使用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;
}

Update:给定你最近的更新,你的客户端代码仍然有问题。你试过我上面给你的密码吗?下面是对你最近的代码的修复,尽管我仍然建议你检查上面的代码,它解决了你的原始代码中的许多其他问题:

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