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

现在,一切正常,除非您连接两个客户端(运行应用程序两次)并关闭其中一个客户端,关闭应用程序的客户端向聊天中发送无限的消息,这些消息永远不会停止!有什么帮助解决这个问题吗?

如果可能的话,我想请人帮我评论源代码!

更新代码:

#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,然后您可以使用WSAGetLastError()它找出错误的原因。当返回 <= 0 时,您需要处理这两种情况并让服务器“忘记客户端” recv()(除了SOCKET_ERROR/的特定情况WSAEWOULDBLOCK,这不是致命错误)。目前,您正在处理套接字错误,就好像您确实从客户端接收到数据一样。

正在测试recv()针对零的返回值,但这不是recv()错误返回的值(SOCKET_ERROR实际上是 -1 的别名,并且if (-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 回答