Autor Wątek: Jak zaprojektowac aplikacje serwerowe dla gry online.  (Przeczytany 13794 razy)

Offline blackstar

  • Użytkownik

# Lipiec 31, 2006, 17:19:04
tam jest jakis "smietnik" co czysci za programiste, jakeis dziwne ulatwienia, generalnie w tej kwesti nie pomoge ; )
Garbage collector.
Tak samo dziwne jak funkcja malloc() w porownaniu z mmap() lub (dawno temu) brk()/sbrk().

Cytuj
Pod systemem Windows, wygodniej i szybciej bedzie uzywanie: 1 wątek = 1 gracz, pod systemem Linux jest wiecej mozliwosci i zawsze "dylemat" ( ma to swoje plusy ; )  )
Hmm.....
1. Moze i wygodniej ale dla duzej liczby polaczen raczej napewno nie szybciej. I pamietaj, ze domyslnie stos dla jednego watku w NT ma (bodajze) 256kB. Przy 1000 polaczen tracisz 256MB ramu na same stosy dla watkow!
2. Windows ma wiecej mozliwosci. Pod Linuxem masz do wyboru: gniazda blokujace, select() i poll() (to prawie to samo) i (chyba) sygnaly.
Pod Windowsem masz: gniazda blokujace, select(), WSAAsyncSelect() - wysyla ci komunikat do okienka, kiedy zajdzie jakies zdarzenie na gniazdku, np mozna odebrac dane, WSAEventSelect() - sygnalizuje zdarzenie, gdy cos sie stanie na gniazdku (na takie zdarzenia czeka sie funkcja WaitForSingleObject()/WaitForMultipleObjects(), etc) oraz Overlapped IO.
http://tangentsoft.net/wskfaq/articles/io-strategies.html
Tyle tylko, ze to w Win32. W CLR sprawa wyglada troche inaczej. Nie wiem jak, wiec nie powiem.

Jesli chcesz pisac w C# to poszukaj od Concurrency and Coordination Runtime*. Obecnie jest dostepne chyba tylko w Microsoft Robotics SDK. W kazdym razie warto sie zaznajomic - zupelnie inne podejscie do wspolbieznosci.

Cytuj
Co do tych serwerow, to nie wiem co kolega chcial przekazac, cos na "klastry" ale takowe sie robi pod systemem Linux, tam tez sa odpowiednie "biblioteki systemowe" ktore wspomagaja takie zadania, pod systemem windows ... moze sie da, ale ja bym tego tam nie testowal ; p
Moim zdaniem klaster w sensie Linux z latka OpenMosix to nie jest zbyt dobre rozwiazanie w takiej sytuacji. Raczej cos takiego, jak napisalem w poprzednim poscie. Klaster to raczej nie jest dobre okreslenie; chodzilo mi o kilka wspolpracujacych ze soba serwerow.

*)http://channel9.msdn.com/wiki/default.aspx/Channel9.ConcurrencyRuntime
http://channel9.msdn.com/ShowPost.aspx?PostID=149380
http://channel9.msdn.com/Showpost.aspx?postid=219308

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline skiter

  • Użytkownik

# Lipiec 31, 2006, 21:24:38
Garbage collector.
Tak samo dziwne jak funkcja malloc() w porownaniu z mmap() lub (dawno temu) brk()/sbrk().
Cytuj
No tak ale, jak mnie sie wydaje ze w C# nie jest tak łatwo wyjsc poza zakres pamieci by wywolac, "blue screen'a", w C jak i jego nastepcy C++, trzeba sie bardziej pilnowac, za to pozwala zejsc calkiem nisko na pamieci czego, jak mi sie wydaje C# nie pozwala tak latwo.

Hmm.....
1. Moze i wygodniej ale dla duzej liczby polaczen raczej napewno nie szybciej. I pamietaj, ze domyslnie stos dla jednego watku w NT ma (bodajze) 256kB. Przy 1000 polaczen tracisz 256MB ramu na same stosy dla watkow!
Cytuj
http://condor.depaul.edu/~dmumaugh/readings/handouts/CSC343/threadfuncs.html
Cytuj
#include <windows.h>

HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes,
                     DWORD dwStackSize,
                     LPTHREAD_START_ROUTINE lpStartAddress,
                     LPVOID lpParameter, DWORD dwCreationFlags,
                     LPDWORD lpThreadId );
Wiec tak zle nie jest ; ) ...

Moim zdaniem klaster w sensie Linux z latka OpenMosix to nie jest zbyt dobre rozwiazanie w takiej sytuacji. Raczej cos takiego, jak napisalem w poprzednim poscie. Klaster to raczej nie jest dobre okreslenie; chodzilo mi o kilka wspolpracujacych ze soba serwerow.

*)http://channel9.msdn.com/wiki/default.aspx/Channel9.ConcurrencyRuntime
http://channel9.msdn.com/ShowPost.aspx?PostID=149380
http://channel9.msdn.com/Showpost.aspx?postid=219308
A w takie kwiatk isie nie bawilem ... ale pomysl naprawde ciekawy

Offline zarius

  • Użytkownik

# Lipiec 31, 2006, 23:47:06
Jeszcze raz pisze ze uzywam C# nie C++ wiec _startThread() czy cos innego mi nic nie mowi. Juz wole jak bys mogl uzywac pseudo kodu ;p

Bawilem sie na poczatku w takie proste serwery w C#, z watkami to mniej wiecej wyglada tak:

namespace GameServer
{
    public class ConnectionManager
    {
        private ArrayList alConnections;

        private Thread thdListener;
        private Thread thdConnectionHandler;

        public TextBox tbport;

        public ConnectionManager(TextBox tbPort)
        {
            thdListener = new Thread(new ThreadStart(this.ConnectionListener));
            thdConnectionHandler = new Thread(new ThreadStart(this.ConnectionHandler));

            alConnections = new ArrayList();
            this.tbport = tbPort;
        }

        private void ConnectionListener()
        {
            IPHostEntry IPHost = Dns.GetHostByName(Dns.GetHostName());
            IPEndPoint ep = new IPEndPoint(IPHost.AddressList[0], int.Parse(this.tbport.Text));
           
            Socket socListener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            socListener.Bind(ep);
            socListener.Listen(-1);
           

            Socket temp;

            temp = socListener.Accept();
            if(temp.Connected)
            {
                lock (this)
                {
                    alConnections.Add(temp);
                    temp = null;
                    this.HandleStart();
                }
            }
        }
        private void ConnectionHandler()
        {
            byte[] recvBuffer = new byte[255];
            Socket connection = (Socket)alConnections[0];
            connection.Receive(recvBuffer);

            if (recvBuffer[0] == 255)
            {
                byte[] sendbuffer =  new byte[1];
                sendbuffer[0] = 100;
                connection.Send(sendbuffer);
            }

        }

        public void ListenStart() { this.thdListener.Start(); }
        public void ListenStop() { this.thdListener.Suspend(); }
        public void HandleStart() { this.thdConnectionHandler.Start(); }
        public void HandleStop() { this.thdConnectionHandler.Suspend(); }


    }
}

To nie jest bron boze jakas proba pisania wyzej opisywanego serwera tylko przyklad takiego szkieletu jak robilem to do tej pory i jak mniej wiecej wyglada to w C# :P (dosyc przyjaznie IMO w przeciwienstwie do C++ jak pamietam :/)

Tutaj robilem to na 2 watki (wlasciwie to 3, bo jeszcze byl watek zajmujacy sie oknem windowsowym i jego odrysowywaniem) i pobieralem najswieze polaczenie z listy polaczen aktywnych, ale to chyba nie bylo dobre rozwiazanie. Czyli wedlug pomyslu skitera mial bym te watki dodawac np do listy lub innego dynamicznego kontenera i uruchamiac gdy polaczenie jest nawiazane a usuwac po zakonczeniu ?

Btw: ladne formatowanie postu skiter ;p tak to zrobiles ze mialem problem z rozszyfrowaniem kto kogo gdzie cytuje i gdzie sa twoje wypowiedzi ;p.

A Gargabe collector to nie zadne udziwnienie bo czesto sa ludzie ktorzy je pisza we wlasnych enginach w C++, C# po prostu ma wbudowany :) ynteligetny zwalniacz zasobow ;p (ynteligetny bo nie zawsze dziala tak super :?)

Offline skiter

  • Użytkownik

# Sierpień 01, 2006, 09:21:12
No to Quote to jakos smiesznie wyszlo ... bo jakos tak ... ale sie naucze ; p

Wlasciewie to jak kod w C# wygalda wiem, jak sie tam pisze tez, mniej wiecej wiem, i ja pisze nie tyle co w C++ a w C, gdzie jestem "nisko" systemu i jego kodu.

Jak sam powiedziales, nie musisz sie interesowac "tworzeniem" wątkow, jak tez specjalnie ich pilnowaniem, ten kod ktory psizesz sam sie troszczy o takie sprawy jak "start", "stop" wątka ... dobrze zgadlem ?

w/g mnie serwer wyglada tak:
Start serwera -> konfoguracja adresu portu na ktorm ma dzialac i przestawienie go tak by przyjmowal nowe polaczenia.

Jezeli przyjdzie nowe polaczenie, stworz nowy watek "wolny", czyli serwer nie bedzie czekal az sie zakonczy, i nie chce wiedziec czy sie zakonczy!, bo jak sie skonczy to sie skonczy jak nie ... to trzeba zrobic tak! by byla pewnosc ze sie "kiedys" skonczy ... jest pare trików ; ) - mow tutaj o wątku ; )

Kazde kolejne "nowe polaczenie" ma swoj wlasny watek i wlasny "kod" to jak by to tak nazwac.

Zabezpieczeniem, przed "zawieszeniem" sie watka, ja np mam watki po to by miec "obsluge gracza" a raczej jego polaczenia, sila zeczy szukalem jakiegos sposobu by zapobiedz "AFK'owaniu", czyli inymi slowy ze koles sie polaczy z serwerem nic nie bedzie robic i tak sobie bedzie wisial pare godzin az mu sie znudzi ; ).

System jest prosty, jezeli cos robisz ... liczniek nie leci, dopiero jak staniesz i nic "od klienta" nie ma to zaczyna liczyc ... 15 min, bez reagowania ? chym koles po stronie "klienta" chyba poszedl sobie na kawe ... no to wyslemy mu wiadomosc ... "Hey don't afk in game", lub podobna 4 takie wylatuej z gry co za tym idzie jego watek sie wlasnie konczy.

PS: Pisze MMO ; )

fd_set rfds;
struct      timeval tv;

tv.tv_sec   = 15*60;
tv.tv_usec  = 0;

int Retery  = 0;

while( Error != SOCKET_ERROR )
  {
     FD_ZERO( &rfds );
     FD_SET( PLAYER_SOCKET, &rfds );

     int Select_Error = select( PLAYER_SOCKET+1, &rfds, NULL, NULL, &tv );

     if( Select_Error == 1 )
        {
            memset( RecvBuff, '\0', sizeof(RecvBuff) );
            Error = recv( PLAYER_SOCKET, RecvBuff, sizeof( RecvBuff ), 0 );
            Retery = 0;
            tv.tv_sec = 15*60;
            }
         else if( Select_Error == -1 )
          {
               #ifdef WIN32
                  closesocket( PLAYER_SOCKET );
               #endif
               #ifdef linux
                  close( PLAYER_SOCKET );
               #endif
               break;
            }
         else
         {
               Retery++;
               if( Retery == 5 )
                  {
                     #ifdef WIN32
                        closesocket( NewPlayer->GetSocket() );
                     #endif
                     #ifdef linux
                        close( NewPlayer->GetSocket() );
                     #endif
                     break;
                  }
               tv.tv_sec = 1*60;
            }
      }
Niby pisze w C++, uzywam wątkow, a zeby bylo wesolo zabezpieczenia robie na select(), czyli namieszanie z pokreceniem, polane to teqilą i mozna popijac sokiem z pomaranczy ; ), ale dziala ; p

Offline Reg

  • Administrator
    • Adam Sawicki - Home Page

# Sierpień 05, 2006, 06:06:21
Cytuj
Pod systemem Windows, wygodniej i szybciej bedzie uzywanie: 1 wątek = 1 gracz, pod systemem Linux jest wiecej mozliwosci i zawsze "dylemat" ( ma to swoje plusy ; )  ).
Akurat AFAIK pod Windowsem wątki są bardziej kosztowne niż pod Linuksem.

Dodam, że z moich wielomiesięcznych obserwacji wynika, że serwery WoW działają właśnie tak jak piszecie. Gracz najpierw loguje się do serwera logowania, ten uwierzytelnia go i przekierowuje do wybranego (przez gracza) realma, a ten już działa na zewnątrz jako jeden, ale wewnątrz ma podzielone zadania między niezależne wątki/procesy/maszyny, bo podczas wieczornego, dużego obciążenia zdarza się, że aukcje, poczta, a nawet zbieranie surowców czy walka strasznie laguje albo wręcz zacznie szwankować czy zupełnie wysiądzie, a reszta (np. chodzenie) działa.


Offline zarius

  • Użytkownik

# Sierpień 05, 2006, 13:00:48
Cytuj
Dodam, że z moich wielomiesięcznych obserwacji wynika, że serwery WoW działają właśnie tak jak piszecie. Gracz najpierw loguje się do serwera logowania, ten uwierzytelnia go i przekierowuje do wybranego (przez gracza) realma, a ten już działa na zewnątrz jako jeden, ale wewnątrz ma podzielone zadania między niezależne wątki/procesy/maszyny, bo podczas wieczornego, dużego obciążenia zdarza się, że aukcje, poczta, a nawet zbieranie surowców czy walka strasznie laguje albo wręcz zacznie szwankować czy zupełnie wysiądzie, a reszta (np. chodzenie) działa.

Wow (nie gra ;p), dzieki. To mi pomoglo :) Przynajmniej wiem ze moje pomysly nie sa kompletnie do bani no i ze nie jestem az taki glupi x)

To moze zapytam jeszcze o jedna rzecz, bo widze ze sie troche orientujesz:

Na wow'ie jest ok 6mln graczy, realmow jak dobrze mi ktos powiedzial jest 7 (lub 9 nie pamietam dobrze teraz) wiec jakim cudem oni obsluguja wsyzstkich, ze ludzie sa zadowoleni (przeciez oni placa !) ile ludzi moze pomiescic jeden realm ?

Pozdrawiam

Offline Krzysiek K.

  • Redaktor
    • DevKK.net

# Sierpień 05, 2006, 14:53:10
Dodam, że z moich wielomiesięcznych obserwacji wynika, że serwery WoW działają właśnie tak jak piszecie. Gracz najpierw loguje się do serwera logowania, ten uwierzytelnia go i przekierowuje do wybranego (przez gracza) realma, a ten już działa na zewnątrz jako jeden, ale wewnątrz ma podzielone zadania między niezależne wątki/procesy/maszyny, bo podczas wieczornego, dużego obciążenia zdarza się, że aukcje, poczta, a nawet zbieranie surowców czy walka strasznie laguje albo wręcz zacznie szwankować czy zupełnie wysiądzie, a reszta (np. chodzenie) działa.
Pewnie mają to na kilku wątkach, ale jestem pewien, że nie jest tak, że każdy gracz ma własny wątek, bo przełączenie wątku to zawsze dodatkowy narzut. Po tym, co powiedziałeś, wnioskowałbym raczej, że problem jest z komunikacją pomiędzy maszynami wchodzącymi w skład jednego realmu, bo przecież każdy realm jest obsługiwany na pewno przez więcej niż jedną maszynę. :)

Offline zarius

  • Użytkownik

# Sierpień 05, 2006, 16:54:19
@Krzysiek: czyli tak jak ktos pisal, najlepiej jest to robic w np 1 watku i przeskakiwac po wszystkich aktywnych gniazdach i zbierac dane na gniezdzie ?

Ogolnie nie wiem jak mozna inaczej robic niz tak jak napisalem i polaczenie = nowy watek... Sa jeszcze funkcje asynchroniczne (dobrze mowie ?;p) ale to chyba nie o to chodzi ;p

Jest w internecie jakis dokument, lub wzor na taki model aplikacji sieciowej ? Chodzi tylko o model nie jakas wyszukana aplikacje, taki ktory by w miare sprawnie dzialal nawet przy duzych obciazeniach serwera. Wiadomo, ze i tak glowna role pelnia lacza, ale mi chodzi teraz raczej o wydajne dzialanie na maszynie, zeby ilosc polaczen nie przygniotla maszyny (a nie lacza ;p) bo tak jak ktos zauwazyl watki to duzy narzut i trzeba by sporo pamieci zeby to bylo wydajnie, a napewno sa juz jakies sprawdzone sposoby.

Pozdrawiam i dzieki wszystkim za odpowiedzi.

PS: Lamerskie pytanie, da sie jakos obliczyc narzut jednego klienta na serwer i w ten sposob np policzyc orientacyjnie ile dany komp jest w stanie utrzymac klientow bez maksymalnego obciazania, czy raczej trzeba po prostu testowac ?

Offline Ktoso the Ryba

  • Użytkownik

# Sierpień 05, 2006, 17:20:42
1 watek = 1 klient odradzam, nie dosc ze system ogranicza liczbe watkow (tak mi sie wydaje conajmniej) to skakanie miedzy masą wątków po prostu trwa za długo. Piszę w javie więc nie wiem jakby to wygladalo w C, ale u siebie uzywam blokującego Selector.select() oczekiwania na przyjscie zdarzen na kanalach.

Polecam podejscie z listą zdarzeń do której wątek odbierający zdarzenia dodaje, oraz kilka wątków liczących i wysyłających zdarzenia-odpowiedzi pobiera (są zapętlone i jeżeli lista ma więcej niż 0 elementów pobiera i opracowywuje go).

Co do PS: wąskim gardłem pewnie będzie nie tyle komp co połączenie zatem trzeba sprawdzić ile ono udźwignie: szacujesz ile zajmuje średni pakiet wysylany do/z serwera, mnozysz to tyle razy co ile taki pakiet jest wysylany i mnozysz przez ilosc klientow. Najwieksza liczba uzytkownikow przy ktorej wyliczona liczba nie przekracza szybkosci transferu twojego polaczenia powinna teoretycznie odzwierciedlac to ile klientow mozesz obsluzyc...

Mam nadzieje, ze cos pomoglem

Offline Reg

  • Administrator
    • Adam Sawicki - Home Page

# Sierpień 07, 2006, 00:40:04
Tak jak napisałem, nie jestem w stanie powiedzieć czy to są osobne wątki, procesy czy osobne maszyny. Tak samo nie wiem czy jedną rzecz robi pojedyncza maszyna, czy cały klaster. Jedno jest pewne - jak jedna z tych rzeczy zamuli, inne działają. To nie jest problem z komunikacją między nimi, a raczej z nadmiarem pracy jaką ma któraś z nich - nie wyrabia albo procesor, albo przepustowość przenaczonego na to łącza.

Realmów w WoW jest nie kilka tylko dziesiątki, raczej ponad 100. http://www.wow-europe.com/en/serverstatus/ - tu jest lista tylko tych europjskich, są jeszcze amerykańskie i azjatyckie AFAIK. Każdy działa niezależnie i nie ma między nimi połączenia. Jeśli do tego dodamy, że  te 6 mln graczy to liczba zarejestrowanych, a niewiele kto gra 24h/dobę, to mamy jednocześnie połączoną taką liczbę graczy, że liczba graczy na jeden serwer wydaje się być całkiem rozsądna.

Offline really

  • Użytkownik

# Sierpień 07, 2006, 00:47:21
W WoWa nigdy nie grałem, ale słyszałem, że na początku było obsługiwało go 40 komputerów, ale gra stała się szybko popularna, że musieli dołożyć kolejne 40. Tylko że to było od razu po premierze gry, teraz nie wiem ile mają maszyn.

Offline skiter

  • Użytkownik

# Sierpień 07, 2006, 01:58:57
Komputery nie jako musza miec jakies polaczenie miedzy soba, po np serwer numer 1, sprawdza moj login itd ... i przekieruje mnie na serwer numer 2, to niby jak serwer numer 2 moze wiedziec ze ja jestem zalogowany "poprawnie" ; p, wiec ... polaczenie jest tylko nie wielkie napewno ; p

Offline blackstar

  • Użytkownik

# Sierpień 09, 2006, 00:28:27
Komputery nie jako musza miec jakies polaczenie miedzy soba, po np serwer numer 1, sprawdza moj login itd ... i przekieruje mnie na serwer numer 2, to niby jak serwer numer 2 moze wiedziec ze ja jestem zalogowany "poprawnie" ; p, wiec ... polaczenie jest tylko nie wielkie napewno ; p
Teoretycznie serwer 1 moze dac klientowi certyfikat wazny x czasu podpisany swoim kluczem prywatnym. Serwer 2 wtedy weryfikuje autentycznosc certyfikatu znajac klucz publiczny serwera 1. Przydaloby sie, zeby w certyfikacie byl zapisany adres ip i port, z ktorego laczy sie klient, i pewnie cos jeszcze.

Ale zamiast tak kombinowac lepiej, zeby serwer 1 dal klientowi ciastko (cookie), ktore jednoczesnie (serwer 1) przesyla serwerowi 2. Klient laczy sie z serwerem 2, ktory okresla stan zalogowania na podstawie ciastka, ktore ma klient.

Jesli chodzi o polaczenie miedzy serwerami to i tak oba musza byc podpiete do internetu, wiec automatycznie sa ze soba polaczone.

Jesli stawiasz kilka serwerow to jest bardzo prawdopodobne, ze beda one fizycznie blisko siebie. Wtedy spokojnie mozna polaczyc je gigabitowym ethernetem i problem przepustowosci jest rozwiazany.

dummy

  • Gość
# Sierpień 14, 2006, 19:22:55
apache ma chyba coś takiego jak worker threads. Każdy taki wątek obsługuje określoną maksymalną liczbę klientów. Można jeszcze dążyć do tego, żeby równoważyć obciążenie pomiędzy wątkami.

Mnie się to widzi tak:
1. Na początku odpalasz z góry określoną liczbę workerów
2. Tworzysz 1 wątek, który przyjmuje połączenia, autoryzację i przydziela zadanie do określonego wątku (np. żeby starać się równoważyć obciążenie)
3. Może być odpalony kolejny wątek, który co jakiś czas równoważy obciążenie workerów, żeby nie było tak, że jeden obsługuje 100 a drugi 5 klientów, bo się nagle 95 na drugim rozłączyło a nikt się na razie nie podłącza.
4. Jeśli liczba połączeń jest mała, można co jakiś czas redukować liczbę workerów, żeby niepotrzebnie nie zajmowały zasobów serwera. Przykładowe minimalne ograniczenie liczby: liczba workerów nie może być mniejsza niż liczba procesorów w serwerze.

Spróbuję zakodzić dzisiaj jakiś przykładowy kod.
« Ostatnia zmiana: Sierpień 14, 2006, 19:36:16 wysłana przez agent_J »

Offline skiter

  • Użytkownik

# Sierpień 15, 2006, 06:09:26
Apache uzywa "pre-fork'a", czyli jak napisales ma pare watkow i na kazdym z nich po "kilka" ludzi podpietych na poll/select, co daje mu przewage taka ze jest przenoszony miedzy systemami, ale za to "wolny" i problem z c10k nadal pozostaje, a przepisanie caleg kodu zajelo by wiecznosc ; p, i generalnie musial by byc napisany od 0.

A kod jaki chcesz napisac bedzie w C#, bo autor ... w takowym pisze o ile mnie pamiec nie myli ? ; p