2015-07-12 13 views
7

Mam działający kod serwera i klienta. Serwer i klient mogą poprawnie się łączyć i rozmawiać ze sobą. Ale kiedy otworzę kolejny terminal klienta, klient mówi: Awaiting confirmation from the server i nic więcej. Chociaż serwer i klient nr 1 nadal mogą czatować.Gniazda C++ - Serwer nie akceptuje wielu klientów (Linux)

Szukałem wielowątkowości, ale przykłady lub fragmenty kodu, które pokazują, są zaawansowane. Może trochę wyjaśnień lub przykład pomoże dużo!

Poniższy kod działa. Mam działający serwer, ale akceptuje tylko jedno połączenie. Jak sprawić, aby serwer zezwalał na wiele połączeń? Żeby program wyglądał jak czat grupowy.

client.cpp (gdy klient nr 2 łączy, kod zamarza w wierszu 40)

#include <iostream> 
#include <string.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <arpa/inet.h> 
#include <netdb.h> 
#include <stdlib.h> 
#include <unistd.h> 

using namespace std; 

int main() 
{ 
    char a; 
    int client; 
    int portNum = 1500; 
    int bufsize = 1024; 
    char* buffer = new char[bufsize]; 
    bool isExit = false; 
    char* ip = "127.0.0.1"; 

    struct sockaddr_in direc; 

    if ((client = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 
     cout << "\nError creating socket..." << endl; 
     exit(0); 
    } 

    cout << "\nSocket created successfully..." << endl; 
    direc.sin_family = AF_INET; 
    direc.sin_port = htons(portNum); 
    inet_pton(AF_INET, ip, &direc.sin_addr); 

    if (connect(client,(struct sockaddr *)&direc, sizeof(direc)) == 0) 
     cout << "Connection to the server " << inet_ntoa(direc.sin_addr) << endl; 

    cout << "Awaiting confirmation from the server..." << endl; //line 40 
    recv(client, buffer, bufsize, 0); 

    cout << "\n=> Enter # to terminate the connection\n" << endl; 

    do { 
     cout << "Client: "; 
     do { 
      cin >> buffer; 
      send(client, buffer, bufsize, 0); 
      if (*buffer == '#') { 
       send(client, buffer, bufsize, 0); 
       *buffer = '*'; 
       isExit = true; 
      } 
     } while (*buffer != 42); 

     cout << "Server: "; 
     do { 
      recv(client, buffer, bufsize, 0); 
      cout << buffer << " "; 
      if (*buffer == '#') { 
       *buffer = '*'; 
       isExit = true; 
      } 

     } while (*buffer != 42); 
     cout << endl; 

    } while (!isExit); 
    cout << "=> Connection terminated.\nGoodbye"; 

    close(client); 
    return 0; 
} 

server.cpp

#include <iostream> 
#include <string.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <arpa/inet.h> 
#include <stdlib.h> 
#include <unistd.h> 

using namespace std; 

int main() 
{ 
    int client, server; 
    int bufsize = 1024; 
    int portNum = 1500; 
    bool isExit = false; 
    char* buffer = new char[bufsize]; 

    struct sockaddr_in direc; 
    socklen_t tamano; 
    pid_t pid; 

    if ((client = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 
     cout << "\nError establishing socket..." << endl; 
     exit(1); 
    } 

    cout << "\nSocket server has been created..." << endl; 

    direc.sin_family = AF_INET; 
    direc.sin_addr.s_addr = htons(INADDR_ANY); 
    direc.sin_port = htons(portNum); 

    if ((bind(client, (struct sockaddr*)&direc,sizeof(direc))) < 0) { 
     cout << "\nError binding connection..." << endl; 
     return -1; 
    } 

    tamano = sizeof(direc); 
    cout << "Looking for clients..." << endl; 
    listen(client, 1); 

    while ((server = accept(client,(struct sockaddr *)&direc,&tamano)) > 0) { 
     strcpy(buffer, "Server connected...\n"); 
     send(server, buffer, bufsize, 0); 
     cout << "Connected with the client, you are good to go..." << endl; 
     cout << "Enter # to end the connection\n" << endl; 

     cout << "Client: "; 
     do { 
      recv(server, buffer, bufsize, 0); 
      cout << buffer << " "; 
      if (*buffer == '#') { 
       *buffer = '*'; 
       isExit = true; 
      } 
     } while (*buffer != '*'); 

     do { 
      cout << "\nServer: "; 
      do { 
       cin >> buffer; 
       send(server, buffer, bufsize, 0); 
       if (*buffer == '#') { 
        send(server, buffer, bufsize, 0); 
        *buffer = '*'; 
        isExit = true; 
       } 
      } while (*buffer != '*'); 

      cout << "Client: "; 
      do { 
       recv(server, buffer, bufsize, 0); 
       cout << buffer << " "; 
       if (*buffer == '#') { 
        *buffer == '*'; 
        isExit = true; 
       } 
      } while (*buffer != '*'); 
     } while (!isExit); 

     cout << "\n=> Connection terminated... " << inet_ntoa(direc.sin_addr); 
     close(server); 
     cout << "\nGoodbye..." << endl; 
     isExit = false; 
    } 

    close(client); 
    return 0; 
} 

Jak zrobić serwer akceptuje wielokrotne połączenia?

Dzięki!

+0

Serwer przyjmuje nowych klientów, wykonując 'accept()'. Więc zaakceptuje drugiego klienta, gdy tylko go nazwiesz (zakładając, że drugi klient czeka). Problem polega na tym, że kod serwera czeka na zakończenie pierwszego połączenia przed ponownym wywołaniem. Musisz spojrzeć na 'select()' lub 'pselect()' lub 'epoll()'. Umożliwi to akceptację i rozmowę z wieloma gniazdami za pomocą pojedynczego wątku. Alternatywnie możesz rozwinąć wątek dla każdego połączenia po selekcji (ale to tylko skaluje się do tej pory, ponieważ wątki są drogie). –

Odpowiedz

3

Aby poprawnie obsługiwać wiele połączeń, należy uruchomić nowy wątek dla każdego połączenia przychodzącego. Każde nowe połączenie jest identyfikowane przez własny unikalny deskryptor gniazda zwracany przez accept(). Prosty przykład:

while ((accepted = accept(client,(struct sockaddr *)&direc,&tamano)) > 0) { 
    /*Create the thread and pass the socket descriptor*/ 
    if(pthread_create(new_thread, &thread_attributes, &handle_tcp_connection, (void *)accepted) != 0){ 
     perror("create thread"); 
     exit(EXIT_FAILURE); 
    } 
} 
+2

Kilka problemów: 1) C++ ma własny model wątków (prawdopodobnie powinieneś go użyć). 2) Wątki nie są dobrze skalowane (może to działać na połączenia 10 (może 100, jeśli masz szczęście), a następnie nie jest to dobry model do naśladowania.Zachęcamy do użycia 'select()' lub jednego z jego następców. –

+1

Pozwól mi nie zgadzam się z rozwiązaniem 'select()' .Jeśli skalowalność jest problemem, najlepszym podejściem jest pula wątków wykorzystująca wątki i 'select()' .Ale dla punktu wyjścia myślę, że wątki są najłatwiejszym rozwiązaniem, ponieważ 'wybierz() 'sprawia, że ​​obsługa jest trochę skomplikowana – Manos

+1

@LokiAstari Możesz uzyskać znacznie wyższy poziom współbieżności z serwera z wątkami C++ niż tylko sto. – PSkocik

3

Trzeba będzie użyć select lub poll i maszyna stan wzór robić to, co chcesz zrobić. Oznacza to, że będziesz musiał przetwarzać dane w zależności od tego, który klient je wysyła. Spójrz na here, aby uzyskać przykład działania.

Powiązane problemy