c ++ мульти клиент / серверный чат - PullRequest
0 голосов
/ 18 декабря 2011

Я нахожусь на пределе, думая о решении проблемы с моим сервером чата и клиентом.

Что предполагается сделать, клиент запрашивает имя пользователя, а затем запрашивает соединение у пользователя с ответом [Да / Нет].

При нажатии «да» клиент должен подключиться к серверу, когда ему это нужно, он должен идти в отдельном потоке (для обработки нескольких клиентов (но моя проблема в том, что когда присоединяется более одного пользователя) (имя пользователя текущего зарегистрированного пользователя) изменился на последнего, кто присоединился к чату. Пока это происходит (сервер показывает имя пользователя, в то время как на экране клиента оно исчезает, и не появляются никакие или все странные признаки).

Мне также нужна помощь в рассылке сообщений другим подключенным клиентам (кроме самого пользователя)

Сервер кодов:

#include "stdafx.h"


long antwoord;
char chatname[100];
char bericht[498];
char sbericht[498];


using namespace std;

DWORD WINAPI SocketHandler(void*);

//our main function
void main()
{
    //here we set the Winsock-DLL to start

    WSAData wsaData;
    WORD DLLVERSION;
    DLLVERSION = MAKEWORD(2,1);

    //here the Winsock-DLL will be started with WSAStartup
    //version of the DLL
    antwoord = WSAStartup(DLLVERSION, &wsaData);

    if(antwoord != 0)
    {
        WSACleanup();
        exit(1);
    }
    else
    {
        cout << "WSA started successfully" <<endl;
        cout << "The status: \n" << wsaData.szSystemStatus <<endl;
    }
    //the DLL is started

    //structure of our socket is being created
    SOCKADDR_IN addr; 

    //addr is our struct

    int addrlen = sizeof(addr);

    //socket sListen - will listen to incoming connections
    SOCKET sListen;
    //socket sConnect - will be operating if a connection is found.
    SOCKET sConnect;

    //setup of our sockets
    //opgezocht op internet - AF_INET bekend dat het lid is van de internet familie
    //Sock_STREAM  betekenend dat onze socket een verbinding georiënteerde socket is.
    sConnect = socket(AF_INET,SOCK_STREAM,NULL);

    //now we have setup our struct

    //inet_addr is our IP adres of our socket(it will be the localhost ip
    //that will be 127.0.0.1

    addr.sin_addr.s_addr = inet_addr("127.0.0.1");

    //retype of the family
    addr.sin_family = AF_INET;

    //now the server has the ip(127.0.0.1) 
    //and the port number (4444)
    addr.sin_port = htons(4444);

    //here we will define the setup for the sListen-socket
    sListen = socket(AF_INET,SOCK_STREAM,NULL);

    if (sConnect == INVALID_SOCKET)
    {
        cout << "Error at socket(): \n" << WSAGetLastError() <<endl;
        WSACleanup();
    }
    else
    {
        cout << "Connect socket() is OK!" <<endl;
    }

    if(sListen == INVALID_SOCKET)
    {
        cout << "Error at socket(): \n" << WSAGetLastError() <<endl;
        WSACleanup();
    }
    else
    {
        cout << "Listen socket() is OK!" <<endl;
    }

    //here the sListen-socket will be bind
    //we say that the socket has the IP adress of (127.0.0.1) and is on port (4444)
    //we let the socket become the struct "addr"
    if(bind(sListen, (SOCKADDR*)&addr, sizeof(addr)) == SOCKET_ERROR)
    {
        cout << "bind() failed: \n" << WSAGetLastError() <<endl;
        WSACleanup();
        exit(1);
    }
    else{
        cout << "bind() is OK!" <<endl;
    }

    if(listen( sListen, 10) == -1 ){
        cout << "Error listening %d\n" << WSAGetLastError() <<endl;

    }

    //here we will tell what the server must do when a connection is found
    //therefor we will create an endless loop
    cout << "Waiting for a incoming connection..." <<endl;


    //now we let the socket listen for incoming connections
    //SOMAXCOMM heeft het nut dat het dan voordurend luisterd naar inkomende verbindingen zonder limiet
    int* csock;

    while(true)
    {
        csock = (int*)malloc(sizeof(int));
        //if a connection is found: show the message!
        if((*csock = accept(sListen, (SOCKADDR*)&addr, &addrlen))!= INVALID_SOCKET)
        {
            cout << "A Connection was found with :" << inet_ntoa(addr.sin_addr) <<endl;

            antwoord = send(*csock, "Welcome to our chat:", 21,NULL);
            CreateThread(0,0,&SocketHandler, (void*)csock , 0,0);
            cout << *csock <<endl;

        }
    }

}
//sbericht is the message
DWORD WINAPI SocketHandler(void* lp)
{
    int *csock = (int*)lp;

    for(;;)
    {
        antwoord = recv(*csock, sbericht, sizeof(sbericht), NULL);
        antwoord = recv(*csock, chatname, sizeof(chatname), NULL);

        while(antwoord = recv(*csock, sbericht, sizeof(sbericht), NULL) && (antwoord = recv(*csock, sbericht, sizeof(sbericht), NULL)) )
        {
            printf("%s\: \"%s\"\n", chatname,  sbericht);
            antwoord = send(*csock, sbericht, sizeof(sbericht), NULL);
            antwoord = send(*csock, chatname, sizeof(chatname), NULL);

        }
        return 0;


    }
}

Код клиента:

#include "stdafx.h"

using namespace std;

//our main function
int main()
{
    //here we set the Winsock-DLL to start
    string bevestiging; 

    char chatname[100]; 

    char bericht[250];
    char sbericht[250];

    string strbericht;

    string strsbericht;

    long antwoord;
    //here the Winsock-DLL will be started with WSAStartup
                    //version of the DLL
    WSAData wsaData;
    WORD DLLVERSION;
    DLLVERSION = MAKEWORD(2,1);
    antwoord = WSAStartup(DLLVERSION, &wsaData);
    if(antwoord != 0)
    {
        exit(1);
    }
    else
    {
        cout << "WSA started successfully" <<endl;
        cout << "The status: \n" << wsaData.szSystemStatus <<endl;
    }

    SOCKADDR_IN addr;

    int addrlen = sizeof(addr);

    SOCKET sConnect;

    sConnect = socket(AF_INET, SOCK_STREAM, NULL);

    if (sConnect == INVALID_SOCKET)
    {
        cout << "Error at socket(): \n" << WSAGetLastError() <<endl;
    }
    else
    {
        cout << "socket() is OK!\n" <<endl;
    }


    addr.sin_addr.s_addr = inet_addr("127.0.0.1");

    addr.sin_family = AF_INET;

    addr.sin_port = htons(4444);

    cout << "What is your chat name?" <<endl;

    cin.getline(chatname, 100);


    cout << "Do you want to connect to the server? [Y/N]" <<endl;

    cin >> bevestiging;


    if (bevestiging == "N")
    {
        exit(1);
    }
    else
    {
        if(bevestiging == "Y")
        {

            connect(sConnect, (SOCKADDR*)&addr, sizeof(addr));

            antwoord = recv(sConnect, bericht, sizeof(bericht), NULL);

            strbericht = bericht;

            cout << strbericht << chatname <<endl;

            while(true)
            {
                if(antwoord > 1)
                {

                    cin.clear();
                    cin.sync();
                    cout << chatname << " :" <<endl;
                    cin.getline(sbericht, sizeof(sbericht));
                    antwoord = send(sConnect, sbericht, sizeof(sbericht), NULL);
                    antwoord = send(sConnect, chatname, sizeof(chatname), NULL);

                    while(antwoord = send(sConnect, sbericht, sizeof(sbericht), NULL) && (antwoord = send(sConnect, sbericht, sizeof(sbericht), NULL)))
                    {
                        antwoord = recv(sConnect, sbericht, sizeof(sbericht), NULL);
                        antwoord = recv(sConnect, chatname, sizeof(chatname), NULL);
                        cout << chatname << ":" <<endl;
                        cout << sbericht <<endl;
                        cin.getline(sbericht, 250);

                    }

                }

                else
                {
                cout << "The connection to the server has been lost... \n" << "please exit the client." <<endl;

                }
            }

Извините, если я не написал это хорошо (я только учусь программировать сокеты), но я не могу понять это. Так что не стесняйся со мной, мне все еще нужно учиться, но я не могу найти то, что мне нужно. Поэтому я думаю, что если кто-нибудь покажет мне, как это сделать, я смогу увидеть, как это делается и почему.

Всегда чему-то учиться (в настоящее время я также занят обучающим руководством по сетевому программированию Beejee).

Ответы [ 2 ]

0 голосов
/ 18 декабря 2011

Есть несколько проблем с этим кодом, но сокеты - это непростая вещь, когда вы начинаете.В коде вашего сервера кажется, что многопоточность - ваш настоящий зверь.Обратите внимание, что потоки и сокеты - это очень разные понятия, но они часто используются вместе.Большая проблема (как сказал Эндрю) в том, что у вас есть условия гонки.Вам нужно использовать взаимные исключения, чтобы гарантировать взаимное исключение, если вы пишете в глобальные переменные в нескольких потоках.Например, в SocketHandler вам нужно защитить свою переменную antwoord.Кроме того, используйте new и delete в C ++, а не malloc() и free() (они используются в C).Также обратите внимание, что для каждого нового соединения вы перезаписываете значение вашей переменной csock.Вам нужны отдельные переменные для хранения открытого клиентского сокета.

Серверные сокеты в основном работают следующим образом: socket(), bind(), listen(), accept().Теперь, чтобы сохранить порт, к которому вы bind() 'подключены и listen()' открыты для дополнительных соединений, accept() перенаправляет ваш клиент и помещает их в другой дескриптор файла сокета (который является возвращаемым значениемaccept()) чтобы вы могли продолжить общение с этим клиентом.Поэтому для каждого клиента вам необходимо уникально хранить значение accept().

Однако я скептически отношусь к тому, что в коде вашего клиента вы получаете то, что ожидаете.Гарантирует ли ваш протокол, что он отправит только имя пользователя, а затем чат?Вам может просто понадобиться один recv() звонок, чтобы получить все ваши данные.Кроме того, какова актуальность следующей строки?

while(antwoord = send(sConnect, sbericht, sizeof(sbericht), NULL) && (antwoord = send(sConnect, sbericht, sizeof(sbericht), NULL)))

Вы просто выполняете одну и ту же работу дважды - достаточно одного раза (и, возможно, в этот момент программа может заблокировать и перезаписать данные без вашего ведома).Для простого текстового протокола очень вероятно, что вы можете создать переменную ~ 512 байт и получить всю информацию от сервера соответствующим образом за один вызов recv().

Я пытался выделить большие проблемыв вашем коде, и эти проблемы, более или менее, часто встречаются и в других областях вашего кода.Если вы не знакомы с многопоточностью, решите эту проблему позже.Научитесь делать однопоточные сокеты, а затем изучите многопоточность.Захватить их обоих сразу укусит тебя.Удачи!

0 голосов
/ 18 декабря 2011

sbericht и chatname - глобальные переменные Ваши 2 потока работают с этими глобальными буферами одновременно Таким образом, один поток перезаписывает данные другого потока

...