logo elektroda
logo elektroda
X
logo elektroda
REKLAMA
REKLAMA
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

[C++] Robienie wątków, przerabianie gry, czy dobrze myślę?

Sagaceil 21 Sie 2007 11:43 1602 8
REKLAMA
  • #1 4199314
    Sagaceil
    Poziom 11  
    Posty: 29
    Pomógł: 2
    Ocena: 1
    Jak już pisałem miesiąc lub dwa na tym forum, zacząłem przerabiać moją grę RPG na MMORGP. Klient działa w 100% (handel, chat, widać innych ludzi na planszy i co gadają, jak wyglądają, ich avatary itp). Ale jakoś nigdy nie bawiłem się w procesy i omijałem je szerokim łukiem ( teraz tego mocno żałuję). I sprawa jest następująca. Chciałbym zrobić kilkuprocesowy program-serwer. I mam następującą koncepcję:

    void obsluga_klienta( int ConnID ){

    * oczekuj na klienta
    * jeśli listen zadziałało to uruchom proces oblsuga_klienta( pierwszy wolny ID jaki jest ) jeśli jest to klient numer MAX_CONN -1 jeśli nie, to powiedz mu, że serwer pełny i wyłącz proces.

    pętla ->

    * tutaj w pętli czytaj o co prosi serwer i odpowiadaj ( wysyłanie odpowiednich danych itp)

    * w razie zerwania polaczenie to zamknij proces i zwolnij ID

    <-

    }


    void proces_serwera(){

    pętla ->

    * jeśli nikogo nie ma (wszystkie ID są wolne) to
    -> to uruchom proces oblsuga_klienta(0)

    * i wszystkie inne funkcje związane z np ruchem potworków i znikaniem itemów z planszy

    <-

    }


    main(){

    * inicjalizacja serwera, jego secketów itd.

    * uruchom proces serwera

    }



    I czy idę chociażby w dobrym kierunku ?
    Chodzi mi głownie jak ustawić funkcję jako proces i jak ten proces z niego samego zamknąć. Ewentualnie jakie poprawki musiałbym wprowadzić ?
    Z góry dziękuję.

    Dodano po 1 [minuty]:

    I raczej nie chodzi mi o robienie procesu okna, bo takowy jest tylko jeden chyba
  • REKLAMA
  • #2 4200302
    krzychoocpp
    VIP Zasłużony dla elektroda
    Posty: 1866
    Pomógł: 387
    Ocena: 58
    Witam, C++0x ma przynieść na tym polu dużo ciekawych zmian. Na razie, żeby wystartować wątek, musisz użyć odpowiedniej funkcji(beginthread, CreateThread...).

    Pozdrawiam, Krzysztof.
  • REKLAMA
  • #3 4201616
    Sagaceil
    Poziom 11  
    Posty: 29
    Pomógł: 2
    Ocena: 1
    No trochę sobie w miedzy czasie poczytałem. Lepiej poczekam do tych zmian :p
  • Pomocny post
    #4 4201700
    krzychoocpp
    VIP Zasłużony dla elektroda
    Posty: 1866
    Pomógł: 387
    Ocena: 58
    To nie jest najlepszy pomysł - na razie jest tylko eksperymentalna wersja GCC która obsługuje część tego co ma się stać standardem w 2009 roku. Więc jeśli kolega chce wydać grę w najbliższym czasie, proponuję poczytać jednak o wątkach. Samo wystartowanie ich to nie problem, problemem może być synchronizacja, deadlocki, itp. - ale dość dużo już napisano o tym, myślę że rozwiązywać problemy z wątkami będzie łatwiej, kiedy kolega wątki zaimplementuje.

    Pozdrawiam, Krzysztof.
  • REKLAMA
  • #5 4202025
    Sagaceil
    Poziom 11  
    Posty: 29
    Pomógł: 2
    Ocena: 1
    Nom, jakoś powoli idę do przodu. I aktualnie bawię się synchronizacją. Ale wszytko wskazuje na udany dzień z wątkami. Thx
  • #7 4205807
    Sagaceil
    Poziom 11  
    Posty: 29
    Pomógł: 2
    Ocena: 1
    dzięki wielkie

    Dodano po 7 [minuty]:

    Zanim jeszcze zacznę robić synchronizację oraz te wszystkie zabezpieczenie dotyczące jednoczesnego otwierania/zapisywania danych, zrobiłem taki serwerek:

    
    
    #include <string>
    #include <fstream>
    #include <sstream>
    #include <iostream>
    #include <windows.h>
    #include <winsock2.h>
    
    using namespace std;
    fstream plik;
    string str;
    
    #define BUFOR 256
    #define NO_HOST 23023
    #define MAX_CONN 100
    
    WSADATA wsaData;
    SOCKET soc_serwer; //socket serwera oraz klienta
    WORD wersja = MAKEWORD( 2,0  );
    sockaddr_in _sin;
    
    /******************************************************************************/
    int do_int(string wartosc){
    istringstream strum(wartosc);
    int pom = 0;
    strum >> pom;
    return pom;
    }
    
    /******************************************************************************/
    string do_str(int wartosc){
    ostringstream strum ;
    strum << wartosc;
    return strum.str();
    }
    /******************************************************************************/
    
    bool FileExists( const char* FileName )
    {
        FILE* fp = NULL;
        fp = fopen( FileName, "rb" );
        if( fp != NULL )
        {
            fclose( fp );
            return true;
        }
        return false;
    }
    /******************************************************************************/
    
    void zeruj(char *c, char l = 0){
    for(int i = 0; i < BUFOR; i++)
     c[i] = l;
    }
    /******************************************************************************/
    
    DWORD WINAPI obsluga_klienta(){
    SOCKET soc_klient;
    HANDLE hThread;
    DWORD dwThreadId;
    int blad;
    char bufor_[BUFOR], _bufor[BUFOR];
    fstream plik;
    string str;
    
     if(listen(soc_serwer,MAX_CONN)==SOCKET_ERROR) {
            return 2;
            }
     int rozmiar = sizeof(_sin);
     soc_klient = accept(soc_serwer,(struct sockaddr *)&_sin, &rozmiar);
    
    
    hThread = CreateThread( 
                NULL,
                0,
                (LPTHREAD_START_ROUTINE) obsluga_klienta,
                NULL,
                0,
                &dwThreadId);
    
    
    do{
        zeruj(_bufor);
        blad = recv(soc_klient,_bufor,BUFOR,0);
        if(blad == SOCKET_ERROR || _bufor[0] == 0){
            closesocket(soc_klient);
            return 0;
            }
        
        switch(_bufor[0]){
        case 1 : plik.open(_bufor+1,ios::out);
                 plik << " ";
                 plik.close();
                 plik.clear();
                 break;
        case 6 : DeleteFile(_bufor+1);
                 break;
        case 2 : zeruj(bufor_);
                 bufor_[0] = FileExists((char*)_bufor+1);
                 send(soc_klient,bufor_,BUFOR,0);
                 break;
        case 3 : {
                 plik.open(_bufor+1, ios::in);
    
                 do{
                 zeruj(bufor_);
                 for(int i=0;plik.good() && (i<BUFOR);i++)
                  plik.get(bufor_[i]);   
                 send(soc_klient,bufor_,BUFOR,0);
                 }while(bufor_[0]);
    
                 plik.close();
                 plik.clear();
                 break;}
        case 4 : {
                 recv(soc_klient,bufor_,BUFOR,0);
                 if(bufor_[0] == 0){
                  break;
                  }
                 
                 plik.open(_bufor+1, ios::out);
                 while(bufor_[0]){
                 for(int i = 0; i < BUFOR && bufor_[i]; i++)
                  plik.put(bufor_[i]);
                 recv(soc_klient,bufor_,BUFOR,0);
                 }
                 
                 plik.close();
                 plik.clear();
                 break;}
        }      
    
    }while(true);
    
    
    }
    
    /******************************************************************************/
    int main(){
         HANDLE hThread;
         DWORD dwThreadId;
    
         cout << "Uruchamianie modulu WinSock   ";
         int blad = WSAStartup(wersja, &wsaData );
         if( blad != 0 ) {
            cout << "[  BLAD  ]\n";
            system("pause");
            return 0;
            }
         cout << "[   OK   ]\n";
         cout << "Badanie wersji WinSock        ";
         if( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 0 ){
            WSACleanup();
            cout << "[  BLAD  ]\n";
            system("pause");
            return 2;
            }
         cout << "[   OK   ]\n";
         cout << "Tworzenie hosta serwera       ";      
          soc_serwer = socket( AF_INET, SOCK_STREAM, 0 );
          memset( &_sin, 0, sizeof(_sin));
          _sin.sin_family = AF_INET;
          _sin.sin_addr.s_addr = INADDR_ANY;
          _sin.sin_port = htons(NO_HOST);
    
         cout << "[   OK   ]\n";
    
         cout << "Konfigaracja adresu serwera   ";      
          if(bind(soc_serwer,(struct sockaddr *)&_sin, sizeof(_sin))==SOCKET_ERROR) {
            WSACleanup();
            cout << "[  BLAD  ]\n";
            system("pause");
            return 2;
            }
         cout << "[   OK   ]\n";
    
    
    hThread = CreateThread( // -> w tym miejscu następuje crash programu :P
                NULL,
                0,
                (LPTHREAD_START_ROUTINE) obsluga_klienta,
                NULL,
                0,
                &dwThreadId);
    
    closesocket(soc_serwer);
    WSACleanup();
    }
    
    
    


    Kompilować się kompiluje, jednak gdzy przyjdzie mu do uruchomienia pierwszego wątku to się wyłącza. Czym może to być spowodowane. bo akurat tutaj to w wątku zrobiłem zmienne lokalne, przeznaczone tylko dla jednego wątka.
  • REKLAMA
  • #8 4206052
    krzychoocpp
    VIP Zasłużony dla elektroda
    Posty: 1866
    Pomógł: 387
    Ocena: 58
    Pod Linuksem/Wine nie crashuje i działa jeśli poprawię tak jak sugeruję poniżej. Ale wydaje mi się że nie crashuje nawet pod oryginalnym Windowsem - po prostu się kończy. Program kończy się razem z funkcją main, więc zatrzymaj ją po utworzeniu wątku - masz trochę opcji: Pętla wiadomości, WaitForSingleObject, a dla testów to nawet: ;)

    Pozdrawiam, Krzysztof.
  • #9 4206857
    Sagaceil
    Poziom 11  
    Posty: 29
    Pomógł: 2
    Ocena: 1
    Wiem, wczoraj przy kąpieli zdałem sobie sprawę, że nie ma głównej pętli w programie :P

Podsumowanie tematu

✨ Dyskusja dotyczy implementacji wielowątkowości w serwerze gry RPG przerabianej na MMORPG w C++. Autor planuje stworzyć serwer wieloprocesowy obsługujący wielu klientów, z funkcją zarządzania połączeniami i synchronizacją danych. Wskazano, że obecne standardy C++0x (później C++11) wprowadzą ulepszenia w obsłudze wątków, jednak na ten moment należy korzystać z funkcji systemowych takich jak beginthread czy CreateThread. Podkreślono, że uruchomienie wątków jest proste, ale kluczowe są kwestie synchronizacji i unikania deadlocków. Zaproponowano użycie monitorów (System.Threading.Monitor) do synchronizacji. Autor udostępnił fragment kodu serwera opartego na Windows API i Winsock2, implementującego podstawową obsługę połączeń i synchronizację. Zwrócono uwagę na konieczność utrzymania głównej pętli programu, aby wątki nie kończyły działania wraz z funkcją main. Dyskusja obejmuje praktyczne aspekty programowania wielowątkowego i sieciowego w środowisku Windows dla aplikacji sieciowych w C++.
Podsumowanie AI na podstawie dyskusji. Może zawierać błędy.
REKLAMA