Многопоточный чат: сервер

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by sergg, 22 Jul 2011.

  1. sergg

    sergg New Member

    Joined:
    10 May 2010
    Messages:
    11
    Likes Received:
    0
    Reputations:
    0
    Всем привет. Мне необходимо написать многопоточный чат. При написании, у меня возникла проблема. Я пишу по примеру из "Самоучитель игры на WinSock2". В книге приведен пример эхо-сервера, т.е. написать на его примере сервер для чата - не должно составить особой проблемы.
    Сейчас я нахожусь на стадии написания сервера. Возникла проблема с многопоточностью. В книге все просто: клиент коннектится к серверу а далее вызывается функция CreateThread с одним из параметров SexToClient, где SexToClient - функция, обслуживающая клиента. Вот код данной функции:
    Code:
    DWORD WINAPI SexToClient(LPVOID client_socket)
    {
    SOCKET my_sock;
    my_sock=((SOCKET *) client_socket)[0];
    char buff[20*1024];
    #define sHELLO "Hello, Sailor\r\n"
    // отправляем клиенту приветствие
    send(my_sock,sHELLO,sizeof(sHELLO),0);
    // цикл эхо-сервера: прием строки от клиента и возвращение ее клиенту
    while( (int bytes_recv=recv(my_sock,&buff[0],sizeof(buff),0)) &&
    bytes_recv !=SOCKET_ERROR)
    send(my_sock,&buff[0],bytes_recv,0);
    // если мы здесь, то произошел выход из цикла по причине
    // возращения функцией recv ошибки – соединение с клиентом разорвано
    nclients++; // уменьшаем счетчик активных клиентов
    printf("disconnect\n"); PRINTNUSERS
    // закрываем сокет
    closesocket(my_sock);
    return 0;
    }
    Как отправлять сообщение пользователя не ему самому, а всем пользователям сразу?
    Я так понимаю, что надо как-то создать массив типа SOCKET, в котором будут хранится все сокеты, по которым подключены клиенты, и в цикле отправлять это сообщение всем, но как это сделать в потоке? Или может есть еще какие-нибудь варианты?
    P.S. Один поток обслуживает одного клиента. Максимальное число клиентов - 64.

    Подскажите, пожалуйста, решение этой проблемы.
    Спасибо.
     
  2. R0nin

    R0nin Member

    Joined:
    11 Jul 2010
    Messages:
    261
    Likes Received:
    24
    Reputations:
    8
    Обычная структура многопоточного сервера:

    1. Главная функция в цикле вызывает accept()
    2. Принимает клиента, сохраняет сокет
    3. Создает поток/процесс для клиента и передает ему сокет (его сокет)

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

    Создай примерно такую структуру :

    структура СТР{
    клиентский_сокет;
    указатель_на_сокеты_других_клиентов;
    указатель_на_количество_подключенных_клиентов;
    };

    Насчет "указатель_на_количество_подключенных_клиентов" можно сказать еще :
    1. что это указатель, и количество клиентов может меняться, значит тут придеться использовать semaphor-ы или mutex-ы.
    2. или можешь сделать так, чтобы каждый поток работал с 64 сокетами, но тут придеться проверять каждый сокет на "работо-
    спобность"

    Вот например для твоей проблемы примерно такой псевдокод :
    Code:
    struct STR {
         csocket cs;
         csocket *p_list;
    };
    
    
    func  main() {
       while( event()) {
            ListOfSocket[i] = AcceptClient();
            if (OK) {
               STR * st  = new STR;
               st->p_list = ListOfSocket;
               st->cs = ListOfSocket[i];
               create_thread( client_thread_func(), st );
            }
       }
    }
    
    
    func client_thread_func( STR *st ) {
        while( (buffer = getFromClient(st->cs))) {
               for (i = 0; i < 64; i++) {
                    if (st->p_list[i] == OK) {
                        SendToClient( buffer, st->p_list[i] );
                    }
               }
         }
    }
     
  3. sergg

    sergg New Member

    Joined:
    10 May 2010
    Messages:
    11
    Likes Received:
    0
    Reputations:
    0
    Сейчас у меня так и есть:

    Code:
    	while((clientSocket=accept(servSocket, (sockaddr *) &clientAddr, &clientAddrSize))) {
    		usersCount++;
    
    		HOSTENT *host;
    		host=gethostbyaddr((char *) &clientAddr.sin_addr.s_addr,4,AF_INET); // пытаемся получить имя хоста
    
    		// выводим данные о клиенте
    
    		cout<<host->h_name<<" ("<<inet_ntoa(clientAddr.sin_addr)<<" ) has connected")<<endl;
    		PrintUsers();
    
    		DWORD threadID;
    		CreateThread(NULL,NULL,ServeClient,&clientSocket,NULL,&threadID);
    	}
    
    Далее для решения проблемы мне необходимо создать статический массив типа SOCKET (например он называется usersList) и проинициализировать все элементы значением INVALID_SOCKET. Далее, где у меня идет часть кода, с функцией accept сделать так:

    Code:
    while((clientSocket=accept(servSocket, (sockaddr *) &clientAddr, &clientAddrSize))) {
                                usersList[usersCount]=clientSocket;
    		usersCount++;
    А вот, что дальше делать с потоками я не очень понял. Не мог бы более подробней пояснить?
     
  4. R0nin

    R0nin Member

    Joined:
    11 Jul 2010
    Messages:
    261
    Likes Received:
    24
    Reputations:
    8
    Ты наверное не понял что я написал:
    я ж говорил, что ты должен создать структуру и передать указатель на структура в новый созданный поток.
    Еще раз хорошенько прочти, то что я написал, там есть все что тебе нужно.

    p.s.
    Когда передалаешь код должен выглядеть примерно так :

    Code:
    CreateThread(
       NULL, 
       NULL,
       ServeClient, 
       [B]УКАЗАТЕЛЬ_НА_СТРУКТУРУ[/B]  ,
       NULL,
       &threadID);
     
    1 person likes this.