(TCP / IP) При приеме более трех клиентов произошла ошибка, из-за которой поврежден recvBuffer - PullRequest
0 голосов
/ 09 июня 2018

Я начинаю разрабатывать простую многопользовательскую игру по протоколу TCP / IP с использованием Direct-X, когда к серверу принимаются два клиента. Это не имеет значения. Но когда более трех клиентов пытаются связаться, происходит «recvBuffer клиента поврежден»ошибка и закрыта.Я думаю из-за перегрузки в recvBuffer больше, чем размер массива.Как я могу решить эту перегруженную проблему?Вот мой код 1.Сервер 2.Клиент

#include <stdio.h>
#include <WinSock2.h>
#include<string>
#include "Packet.h"

#define MAX_CLIENT 8


///////////////////////////////////////////////////
Packet  recvPacket[MAX_CLIENT];
char    receiveBuffer[MAX_CLIENT][PACKETBUFFERSIZE];
int     receivedPacketSize[MAX_CLIENT] = { 0, 0, 0 };

///////////////////////////////////////////////////


void main()
{
    WSADATA wsaData;
    SOCKET socketListen, socketTemp;
    SOCKET socketClient[MAX_CLIENT];    
    struct sockaddr_in serverAddr;
    int  k;


    //  네트워크를 초기화 한다.
    ::WSAStartup(0x202, &wsaData);

    for (k = 0;k<MAX_CLIENT;k++)    
        socketClient[k] = INVALID_SOCKET;


    socketListen = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (socketListen == INVALID_SOCKET)
    {
        printf("Socket create error !!\n");
        return;
    }

    ::memset(&serverAddr, 0, sizeof(serverAddr));
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = ::htonl(INADDR_ANY); // ::inet_addr( "165.194.115.25" ); //::htonl( INADDR_LOOPBACK   INADDR_ANY );
    serverAddr.sin_port = ::htons(8600);

    if (::bind(socketListen, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
    {
        printf("bind failed!! : %d\n", ::WSAGetLastError());
        return;
    }

    if (::listen(socketListen, SOMAXCONN) == SOCKET_ERROR)
    {
        printf("listen failed!! : %d\n", ::WSAGetLastError());
        return;
    }

    printf("****** Server Start with Maximum %d at %s ***\n", MAX_CLIENT, ::inet_ntoa(serverAddr.sin_addr));


    //////////////////////////////////////////////////////////////////// server loop    
    while (1)
    {
        fd_set fds;
        struct timeval tv = { 0, 100 };     //  0.1 초

        FD_ZERO(&fds);

        FD_SET(socketListen, &fds);
        for (k = 0;k<MAX_CLIENT;k++)
            if (socketClient[k] != INVALID_SOCKET)
                FD_SET(socketClient[k], &fds);


        ::select(MAX_CLIENT + 2, &fds, 0, 0, &tv); //  zero ?

        if (FD_ISSET(socketListen, &fds))
        {
            struct sockaddr_in fromAddr;
            int size = sizeof(fromAddr);


            for (k = 0;k<MAX_CLIENT;k++)
                if (socketClient[k] == INVALID_SOCKET)
                    break;
            if (k == MAX_CLIENT) {
                printf("*** Maximum client: Unable to accept ! %d\n", MAX_CLIENT);

                socketTemp = ::accept(socketListen, (struct sockaddr*)&fromAddr, &size);
                ::shutdown(socketTemp, SD_BOTH);
                ::closesocket(socketTemp);
                socketTemp = INVALID_SOCKET;
            }
            else
            {
                socketClient[k] = ::accept(socketListen, (struct sockaddr*)&fromAddr, &size);
                if (socketClient[k] != SOCKET_ERROR)
                    printf("*** Accepted a client : %d(%d) from %s\n", k, socketClient[k], ::inet_ntoa(fromAddr.sin_addr));
            }
        }

        else
        {
            for (k = 0;k<MAX_CLIENT;k++)
                if (socketClient[k] != INVALID_SOCKET && FD_ISSET(socketClient[k], &fds))
                {
                    //  데이터를 받은 후에는 Echo 통신을 한다.
                    char tempBuffer[127];
                    char recvBuffer[PACKETBUFFERSIZE];
                    int recvBytes;
                    int bufSize;

                    bufSize = PACKETBUFFERSIZE - receivedPacketSize[k];

                    if ((recvBytes = recv(socketClient[k], &(receiveBuffer[k][receivedPacketSize[k]]), bufSize, 0))<1) {

                        //  통신이 끝난 후에는 클라이언트의 접속을 해제한다.
                        printf("*** Closed the client : %d(%d)\n", k, socketClient[k]);

                        ::shutdown(socketClient[k], SD_BOTH);
                        ::closesocket(socketClient[k]);
                        socketClient[k] = INVALID_SOCKET;
                        break;
                    }







                    printf("<<< Receive bytes %d from %d(%d) \n", recvBytes, k, socketClient[k]);
                    receivedPacketSize[k] += recvBytes;

                    while (receivedPacketSize[k] > 0)   // parsing Packet Length
                    {





                        recvPacket[k].readData(recvBuffer, bufSize);
                        recvPacket[k].copyToBuffer(receiveBuffer[k], receivedPacketSize[k]);
                        int packetlength = (int)recvPacket[k].getPacketSize();

                        if (receivedPacketSize[k] >= packetlength)
                        {
                            //  Parsing, main routine 
                            if (recvPacket[k].getPacketBuffer()[4] == 9+'0')
                                recvPacket[k].getPacketBuffer()[4] = k+'0';
                            memset(recvBuffer,0, sizeof(recvBuffer));
                            recvPacket[k].readData(recvBuffer, recvPacket[k].getDataFieldSize());
                            printf("(%d Bytes, ID=%d) %s\n", recvPacket[k].getDataFieldSize(), recvPacket[k].id(), recvBuffer);

                            //::send( socketClient[k], recvBuffer, strlen(recvBuffer)+1, 0 );
                            for (int a = 0;a < MAX_CLIENT;a++) {
                                if (a!=k&&socketClient[a] != INVALID_SOCKET) {

                                    ::send(socketClient[a], recvPacket[k].getPacketBuffer(), recvPacket[k].getPacketSize(), 0);
                                }

                            }
                            receivedPacketSize[k] -= packetlength;
                            if (receivedPacketSize[k] > 0)
                            {

                                ::CopyMemory(recvBuffer, (receiveBuffer[k] + recvPacket[k].getPacketSize()), receivedPacketSize[k]);


                                ::CopyMemory(receiveBuffer[k], recvBuffer, receivedPacketSize[k]);

                                printf("%s", recvBuffer);
                            }
                        }
                        else
                            break;
                    }

                    ///////////////////////////////////////////////////////////////////////////////////////

                } // MAX_CLIENT
        }

    }
    //////////////////////////////////////////////////////////////////// end of server

    ::WSACleanup();
}

#include <stdio.h>
#include <WinSock2.h>
#include <ws2tcpip.h>

#include "wtpipv6.h"
#include "wspiapi.h"

#include "Packet.h"


//**********************************************************************
void usage(char *progname)
{
    fprintf(stderr, "usage: %s [-n name] [-p port] \n", progname);
    fprintf(stderr, "   -n name    Host name to resolve, [127.0.0.1] \n");
    fprintf(stderr, "   -p port    Port number to resolve, [8600] \n");

    ExitProcess(-1);
}


int resolveAddr(int argc, char **argv, char *serverName, char *serverPort)
{
    int count, rc, i;

    serverName[0] = 0;
    serverPort[0] = 0;

    for (i = 1; i < argc;i++)
    {
        if ((argv[i][0] != '-') && (argv[i][0] != '/') && (strlen(argv[i]) < 2))
            usage(argv[0]);

        switch (tolower(argv[i][1]))
        {

        case 'n':       // name to resolve
            if (i + 1 >= argc)
                usage(argv[0]);
            strcpy(serverName, argv[++i]);
            break;

        case 'p':       // port/service to resolve
            if (i + 1 >= argc)
                usage(argv[0]);
            strcpy(serverPort, argv[++i]);
            break;


        default:
            usage(argv[0]);
            break;
        }
    }
    if (serverName[0] == 0)
        strcpy(serverName, "10.210.61.14");
    if (serverPort[0] == 0)
        strcpy(serverPort, "8600");

    printf("** Resolve Address %s:%s \n", serverName, serverPort);


    //******************* remoteHost = gethostbyname(host_name);

    struct hostent *remoteHost;

    remoteHost = gethostbyname(serverName); // ns.cau.ac.kr
    printf(">> gethostbyname Official name: %s", remoteHost->h_name);

    struct in_addr addr;

    addr.s_addr = *(u_long *)remoteHost->h_addr_list[0];
    printf("-(%s) \n", inet_ntoa(addr));


    if (!isalpha(serverName[0]))
    {
        struct in_addr addr = { 0 };
        addr.s_addr = inet_addr(serverName);    // 127.0.0.1
        remoteHost = gethostbyaddr((char *)&addr, 4, AF_INET);
        //printf(">> gethostbyaddr Official name: %s\n", remoteHost->h_name);
    }

    //*****************************************************************************

    // Resolve the name/address - first assume that the name might be a string
    // literal address
    struct addrinfo  hints, *res = NULL, *ptr;


    memset(&hints, 0, sizeof(hints));
    hints.ai_flags = AI_NUMERICHOST;
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    rc = getaddrinfo(serverName, serverPort, &hints, &res);
    if (rc != 0)
    {
        if (rc == WSAHOST_NOT_FOUND)
        {
            hints.ai_flags = AI_CANONNAME;
            printf("** AI_CANONNAME\n");
            rc = getaddrinfo(serverName, serverPort, &hints, &res);
            if (rc != 0)
            {
                fprintf(stderr, "getaddrinfo failed: %d\n", rc);
                return -1;
            }
        }
        else
        {
            fprintf(stderr, "getaddrinfo failed: %d\n", rc);
            return -1;
        }
    }

    // Count how many addresses were returned
    count = 0;
    ptr = res;
    while (ptr)
    {
        count++;
        ptr = ptr->ai_next;
    }
//  printf("** Hostname '%s' resolved to %d addresses\n", serverName, count);


    //Do a reverse lookup on each of the resolved address
    char numerichost[NI_MAXHOST];

    rc = getnameinfo(res->ai_addr, res->ai_addrlen, numerichost, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); // NI_NAMEREQD, NI_NUMERICHOST
    if (rc != 0)
    {
        fprintf(stderr, "getnameinfo failed: %d\n", rc);
        return -1;
    }
    strcpy(serverName, numerichost);

    printf(">> Numeric address resolved: %s:%s\n", serverName, serverPort);

    // Free up the results from getaddrinfo
    freeaddrinfo(res);

    return 1;
}
//************************************************************************

void CommRecv(char *recvData);

DWORD WINAPI NetReceive(LPVOID socketConnect)
{
    char recvBuffer[127];
    int  RecvBytes;

    Packet  recvPacket;
    char    receiveBuffer[PACKETBUFFERSIZE];
    int     receivedPacketSize = 0;


    while (1) {

        ///////////////////////////////////////////////////////
        int bufSize = PACKETBUFFERSIZE - receivedPacketSize;

        if ((RecvBytes = recv((SOCKET)socketConnect, &(receiveBuffer[receivedPacketSize]), bufSize, 0))<1) {
            ::shutdown((SOCKET)socketConnect, SD_BOTH);
            ::closesocket((SOCKET)socketConnect);
            socketConnect = (LPVOID)INVALID_SOCKET;
            break;
        }
        receivedPacketSize += RecvBytes;

        while (receivedPacketSize > 0)  // parsing Packet Length
        {
            recvPacket.copyToBuffer(receiveBuffer, receivedPacketSize);
            int packetlength = (int)recvPacket.getPacketSize();

            if (receivedPacketSize >= packetlength)
            {
                //  Parsing, main routine 
                recvPacket.readData(recvBuffer, recvPacket.getDataFieldSize());
                printf("(%d Bytes, ID=%d) %s\n", recvPacket.getDataFieldSize(), recvPacket.id(), recvBuffer);
                CommRecv(recvBuffer);

                receivedPacketSize -= packetlength;
                if (receivedPacketSize > 0)
                {
                    ::CopyMemory(recvBuffer, (receiveBuffer + recvPacket.getPacketSize()), receivedPacketSize);
                    ::CopyMemory(receiveBuffer, recvBuffer, receivedPacketSize);
                }
            }
            else {// if(recvPacket.id()==0){
                printf("(%d Bytes, ID=%d) %s\n", recvPacket.getDataFieldSize(), recvPacket.id(), receiveBuffer);
                //              receivedPacketSize=0;
                break;
            }
        }
        ///////////////////////////////////////////////////////

    }

    return NULL;
}**



SOCKET socketConnect = INVALID_SOCKET;

void CommInit(int argc, char **argv)
{
    WSADATA wsaData;
    struct sockaddr_in serverAddr;
    HANDLE handleThread;


    ::WSAStartup(0x202, &wsaData);


    socketConnect = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (socketConnect == INVALID_SOCKET)
    {
        printf("Cannot create socket !!\n");
    }


    //******************************* Address and port resolve
    char serverName[120], serverPort[120];

    if (resolveAddr(argc, argv, serverName, serverPort)<1) {
        printf("*** Unable to resolve server name !\n");
        ExitProcess(-1);
    }

    //  접속할 서버의 정보를 설정한다.
    ::memset(&serverAddr, 0, sizeof(serverAddr));
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = ::inet_addr(serverName);
    serverAddr.sin_port = ::htons(atoi(serverPort));

    //********************************************************


    if (socketConnect != INVALID_SOCKET) {
        if (::connect(socketConnect, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
            //          printf( "Cannot connect to server !!\n");
            socketConnect = INVALID_SOCKET;
            ExitProcess(-1);
        }
        else {
            // create thread for receive
            handleThread = CreateThread(NULL, 0, NetReceive, (void *)socketConnect, THREAD_PRIORITY_NORMAL, NULL);
        }
    }

}



void CommSend(char *sending)
{
    char sendData[200];
    Packet sendPacket;
    int sentBytes;

    if (socketConnect == INVALID_SOCKET)
        return;

    sprintf(sendData, "%s", sending);

    sendPacket.clear();
    sendPacket.id(1001);
    sendPacket.writeData(sendData, strlen(sendData) + 1);

    sentBytes = ::send(socketConnect, sendPacket.getPacketBuffer(), sendPacket.getPacketSize(), 0);

    if (sentBytes<0) {
        ::shutdown(socketConnect, SD_BOTH);
        ::closesocket(socketConnect);
        socketConnect = INVALID_SOCKET;
    }
}
extern void _GameProc(char *recvData);

void CommRecv(char *recvData)
{
    _GameProc(recvData);

}

1 Ответ

0 голосов
/ 09 июня 2018

Но когда более трех клиентов пытаются установить связь, ...

#define MAX_CLIENT 8
...
int     receivedPacketSize[MAX_CLIENT] = { 0, 0, 0 };
...
                    if ((recvBytes = recv(socketClient[k], &(receiveBuffer[k][receivedPacketSize[k]]), bufSize, 0))<1) {

Учитывая приведенный выше код, вы инициализируете только первые три значения в receivedPacketSize.Все остальное в этом массиве будет неинициализировано, т.е. может принимать значения, отличные от 0. Затем вы используете это инициализированное значение позже, чтобы получить доступ к позиции внутри receiveBuffer[k].Поскольку receivedPacketSize[k] неинициализирован для k>=3, вы можете получить позицию, которая находится за пределами выделенной памяти для receiveBuffer[k].И тогда это вызовет recvBuffer has corrupted.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...