клиентский чат сервера c ++ - PullRequest
1 голос
/ 16 мая 2011

Я делаю сервер, клиентское приложение на основе c ++ консоли.

Что я сделал до сих пор:

  • Я могу подключиться к серверу.
  • Я могу отправлять сообщения на сервер.
  • Сервер может отправлять сообщения обратно.

Но что я не могу понять, как я могу позволить серверу выступать также как клиент для отправки сообщений клиенту, пока он обрабатывает полученные сообщения от клиента?

Люди могут использовать это как пример: D

Хорошо, я также опубликую некоторые части кода:

Сервер:

  #include "stdafx.h"
  using namespace std;
 //our main function
 void main()
     {
int numClients;
long antwoord;
char chatname[100];
char bericht[250]; //messages
char sbericht[250]; //smessages
     //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("192.168.1.103");

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


//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;
for(;;)
{

        //now we let the socket listen for incoming connections
            //SOMAXCOMM heeft het nut dat het dan voordurend luisterd naar inkomende verbindingen zonder limiet
        listen(sListen, SOMAXCONN);
        while(numClients < SOMAXCONN)
        {
            //if a connection is found: show the message!
            if(sConnect = accept(sListen, (SOCKADDR*)&addr, &addrlen))
            {
                cout << "A Connection was found!" <<endl;

                antwoord = send(sConnect, "Welcome to our chat:", 21,NULL);

                if(antwoord > 1)
                {

                    antwoord = recv(sConnect, sbericht, sizeof(sbericht), NULL);
                    antwoord = recv(sConnect, chatname, sizeof(chatname), NULL);

                        while(antwoord = recv(sConnect, sbericht, sizeof(sbericht), NULL) && (antwoord = recv(sConnect, sbericht, sizeof(sbericht), NULL)) )
                        {
                            antwoord = send(sConnect, sbericht, sizeof(sbericht), NULL);
                            antwoord = send(sConnect, chatname, sizeof(chatname), NULL);    
                        }

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


}
}

Клиент:

    // ChatServer.cpp : Defines the entry point for the console application.
    //
    //include of the stdafx.h file where importent files are being included

    #include "stdafx.h"

    using namespace std;

    void smessage()
    {

    }
   //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("192.168.1.103");

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, 250);
                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;

            }
        }
    }
}
    }

Ответы [ 3 ]

0 голосов
/ 16 мая 2011

Прежде всего: размещение 20-мегапиксельного zip-файла в Интернете для примерно 4 интересных исходных файлов не является хорошим вариантом. Ваши объектные файлы и результаты отладки нам не интересны, так как мы хотим помочь с вашим исходным кодом. В следующий раз попробуйте загрузить ZIP-файл, содержащий только исходные файлы.

Во-вторых: если другие хотят понять ваш исходный код и не знакомы с вашим родным языком, они должны догадаться. Попробуйте использовать английский в качестве языка исходного кода по этой и ряду других причин.

Теперь, чтобы ответить на ваш вопрос:

Ответ уже есть в вашем коде. В настоящее время сервер зацикливается до тех пор, пока максимальное количество подключений не получит входные данные и не отправит ответ. Так что на самом деле вы уже реализовали это. Я думаю, что если вы хотите отправлять инициированные сообщения обоими способами, вам нужно немного изменить архитектуру программного обеспечения.

0 голосов
/ 16 мая 2011

В вашем коде есть несколько фундаментальных проблем:

  • Сервер может обрабатывать только одного клиента за раз.Если ваш сервер когда-либо будет иметь более одного пользователя (как это всегда будет с сервером чата), вам необходимо иметь возможность прослушивать более одного соединения одновременно.select, или WSAEventSelect и WaitForMultipleObjects, здесь бы очень помогли.

  • Вы предполагаете, что за один раз появится сообщение всего фиксированного размера.TCP не может гарантировать этого (поскольку концепция «потока» рассматривает данные как потенциально бесконечную последовательность отдельных байтов), а наполовину отправленное сообщение может заморозить ваш сервер, пока оно ожидает остальных.Ничего страшного, если это все в вашей локальной сети, но если вы предоставляете эту услугу в Интернете, вы запрашиваете случайные блокировки.Чтобы предотвратить это, получите данные и поместите их в буфер по мере поступления, обрабатывая его только тогда, когда у вас есть целое сообщение.

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

Пример многопоточного кода (очень грубо, поскольку у меня нет под рукой компилятора C ++):

const int MessageLength = 250;
const int NameLength = 250;

char myname[NameLength];

bool sendFully(SOCKET s, char* buffer, size_t buffer_len, int flags)
{
    char *end = buffer + buffer_len;
    while (buffer != buffer_len)
    {
        int sent = send(s, buffer, end - buffer, flags);
        if (sent == 0) return false;
        buffer += sent;
    }
    return true;
}

DWORD WINAPI watchConsoleInput(void*)
{
    char input[MessageLength];
    while (true)
    {
        std::cin.getline(input, MessageLength);
        if (!sendFully(sConnect, input, sizeof(input), 0))
            break;
        if (!sendFully(sConnect, myname, sizeof(myname), 0))
            break;
    }
    return 0;
}

int main()
{
    char chatname[NameLength];
    char sbericht[MessageLength];

    ... get our name in myname ...

    ...  do the connect stuff  ...

    HANDLE watcher = CreateThread(NULL, 0, watchConsoleInput, NULL, 0, NULL);

    while (true)
    {
        // Added MSG_WAITALL to work around the whole-message-at-a-time thing
        if (recv(sConnect, sbericht, sizeof(sbericht), MSG_WAITALL) != sizeof(sbericht))
            break;
        if (recv(sConnect, chatname, sizeof(chatname), MSG_WAITALL) != sizeof(sbericht))
            break;
    }

    // Don't care about errors; we're just being polite
    shutdown(sConnect, SD_BOTH);

    closesocket(sConnect);
    cout << "Connection lost\n";

    // ExitProcess rather than just 'return', so we know the watcher thread dies
    ExitProcess(0);
}
0 голосов
/ 16 мая 2011

Возможно, вам придется открыть другой сокет.Клиент должен был бы также выступать в роли сервера.

...