Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

Uruchomienie funkcji z wątku na rzecz innego obiektu ( wielowątkowy serwer, SFML 2,1 )

Ostatnio zmodyfikowano 2014-09-04 18:50
Autor Wiadomość
colorgreen19
Temat założony przez niniejszego użytkownika
Uruchomienie funkcji z wątku na rzecz innego obiektu ( wielowątkowy serwer, SFML 2,1 )
» 2014-09-01 22:23:35
Zacząłem pisać wielowątkowy serwer i clientem, coś ala prosty komunikator. Może najpierw wstęp jak to wszystko działa, może komuś się przyda, a przy okazji poprosze o komentarz czy podane rozwiązania są dobre, wydajne itp. (kto nie chce czytać niech pominie akapit)

Serwer "oparty" jest na protokole TCP, logujący sie klient wysyła komunikat czy się loguje czy rejestruje (nie będe wdawał się w szczegóły rozwiązania tego). Przy logowaniu serwer przesyła całe dane użytkownika do klienta, który trzyma to w pamięci podręcznej. Dane konwersacji czatowych  i dane użytkowników są trzymane przez serwer, i na żądanie, w przypadku np. konwersacji bedą przesywałane fragmenty do klienta. Kiedy klient sią zaloguje serwer odpala wątek na rzecz jego, który jest odpowiedzialny za odbiór danych od klienta. Odbiera dane w postaci struktury zawierajcej adresata, wiadomość i date. Serwer zapisuje wtedy wiadomość do pliku i przysyła ją odpowiedniemu klientowi (pomijam opis sprawdzania czy klient jest online, w kontaktach itp).

Klient po zalogowaniu "działa" w głównej pętli "sfmla" i odpala wątek odbierajćy wiadomości. i tu jest problem. dana wiadomość, jeśli ma być wyświetlona to musze do wątku przekazać ten wektor gdzie się dodaje i wyświetla z niego. jesli serwer prześle jakiś "rozkaz/operacje", np przesyła dane nowego klienta czy powiadomienie musi to być "przetworzone" gdzie indziej, czyli inne argumenty powinienem do wątku wsadzić, bo inną operacje klient muśi wykonać. i tu pytanie jak to obejść. Jak moge zwrócic coś z wątku (konkretniej tą wiadomość by ją 1 raz obsłużyć w pętli głównej), poprosze o jakiś pomysł czy idee.
P-116540
DejaVu
» 2014-09-02 01:10:59
Sensowny serwer TCP nie powinien mieć wielu wątków do wielu klientów. Jeden wątek wystarczy do nasłuchiwania wszystkich odpowiedzi. Drugi wątek wystarczy do wysyłana wszystkich informacji. Trzeci wątek to wątek główny aplikacji. Komunikację między wątkami generalnie opatruje się w sekcje krytyczne po to, aby jeden kod nie był wykonywany przez wiele wątków jednocześnie. Poczytaj o nich. Jeżeli używasz C++11, to możesz użyć std::mutex.

Przykład:
C/C++
#include <mutex>

struct Klasa
{
   
    void metoda()
    {
        std::lock_guard < std::mutex > lock( m_cs );
        //kod tu umieszczony będzie bezpieczny wielowątkowo
    }
private:
    mutable std::mutex m_cs;
   
};

Jeżeli chcesz efektywnie odblokowywać jakiś wątek w chwili 'pojawienia' się danych w jakimś kontenerze to powinieneś użyć:
C/C++
#include <condition_variable>


std::condition_variable m_event;

w połączeniu z:
C/C++
m_event.notify_one();
oraz:
C/C++
std::unique_lock < std::mutex > lock( m_cs );
i:
C/C++
m_event.wait(...);

/edit:
A co do przekazywania 'operacji' jaką ma wykonać dany wątek to wystarczy, że zarejestrujesz sobie w kontenerze odebrane sf::Packet-y, a wątek właściwy, odpowiedzialny za przetwarzanie danych zinterpretuje dane z pakietu i wykona jakąś operację, zależną od zawartości sf::Packet-a.
P-116550
colorgreen19
Temat założony przez niniejszego użytkownika
» 2014-09-02 08:34:57
ale to mowisz ze wiele watkow jest malo wydajne? moge skorzystac z SocketSelektora, jak sfml proponuje, ale nie wiem czy przy wielu klientach (wielu ich nie bedzie,ale...) zsumowany czas oczekiwania na kazdym socketcie nie bedzie zbyt dlugi ( dajmy na to 15 klientow, nasluch u kazdego 0.1 sec i sie robi juz 1,5 z tego co w przypadku chatu jest dlugo...
P-116551
1aam2am1
» 2014-09-02 08:49:21
Gdy już zapełniłeś selektor gniazdami, które chcesz obserwować musisz wezwać metodę wait dopóki czegoś nie odbierzesz (lub dostaniesz błąd). Możesz także ustawić opcjonalny czas oczekiwania, a więc jeżeli zostanie zwrócony błąd jeżeli nie zostaną odebranie żadne dane w określonym czasie. Dzięki temu będziesz mógł uniknąć czekania.
Jeżeli wait zwraca true to oznacza, że przynajmniej w jednym gnieździe odebrano jakieś dane oraz możesz bez obawy uruchomić receive na tym sockecie, który przestanie być blokowany. Jeżeli tym socketem jest sf::TcpListener to oznacza, że socket jest gotowy do połączenia i możesz użyć metody accept.
P-116552
DejaVu
» 2014-09-02 09:49:46
Wątek nie jest złotym środkiem na 'szybkie' działanie aplikacji. Wątek ma umożliwiać równoległe przetwarzanie (a przynajmniej takie ma sprawiać wrażenie). Tymi wątkami musi 'coś' zarządzać i tym czymś jest system operacyjny. Wyobraź sobie, że będziesz miał w pewnym momencie 100 wątków, a rdzeni procesora tylko 4. Każdy wątek będzie chciał coś 'robić' i te 4 rdzenie będą musiały intensywnie obsługiwać 100 wątków jednej aplikacji, udostępniając bardzo małe wycinki czasu pracy na każdy wątek. Każdy wątek używa określone zasoby komputera, więc... zamiast mieć 1 wątek zajmujący się intensywnym odbieraniem pakietów Ty masz 100 pomimo, że masz 1 kartę sieciową, ze 4 rdzenie CPU, ograniczoną ilość pamięci RAM itd. Synchronizowanie dostępu do 100 wątków będzie również bardzo niewydajne, bo ostatecznie chcesz mieć jeden punkt przetwarzania danych i podejmowania decyzji 'co z nimi zrobić'.

Najpierw napisz po prostu aplikację sieciową, a potem zdobywaj nowe umiejętności dot. jej optymalności. Programowanie sieciowe to nie tylko wymiana danych między komputerami i dlatego jest takie trudne.
P-116553
colorgreen19
Temat założony przez niniejszego użytkownika
» 2014-09-03 20:36:14
Myśle ze przerobieniem serwera zajme się właśnie później, ale chyba tez to nie bedzie tródne bo sfml ma "przestępne" te socket selectory itp.

Co do oparacji na pakietach, pakiet do kontenera wiadomości jest dodawany z kimś później. Narazie musze stwierdzić że go tam dodam. Wymyśliłem ze do wątku przekarze wskaźnik, pod który wątek podstawi wiadomość, zaś w pętli głównej jest on sprawdzany czy != NULL, jeśli tak, to go obsługuje, po czym znowu ustawia na NULL.
Kod:
funkcja z wątku, Client.cpp
C/C++
void Client::RecevingDataThread( MessagesCointainer::Message * p_message )
{
    Packet packet;
    while( socket->receive( packet ) == Socket::Done )
    {
        MessagesCointainer::Message * message = new MessagesCointainer::Message;
        int a;
        packet >> message->user >> message->message >> a;
        message->date = time( NULL );
       
        p_message = message; //ten wskaźnik
       
        packet.clear();
    }
}
i kod w głównej pętli:
C/C++
if( p_message != NULL )
{
    if( p_message->user == "serwer_addfriend" ) //komendy serwerow czy cos, pseudo kod
    {
        delete p_message;
    }
    else //OBSLUGA WIADOMOSCI
    {
        for( int i = 0; i < Core::client.Messages.size(); i++ )
        {
            if( Core::client.Messages[ i ]->GetUser() == p_message->user )
            {
                Core::client.Messages[ i ]->GetMessages().push_back( p_message );
                break;
            }
        }
        m_chatwindow->AddMessage( guimessage );
    }
    p_message = NULL;
}
Sprawdzając coutem, nawet nie przechodzi warunku != NULL (sam wiadomośći w RecevingDataThread() przychodzą). Pewnie banalny błąd ale... prosze o pomoc. I poza tym jeśli to też nie jest jakieś super rozwiązanie, tzn macie lepsze to zapodajcie pomysły prosze ;)
P-116594
Monika90
» 2014-09-03 21:44:44
C/C++
p_message = message; //ten wskaźnik
To modyfikuje loklany obiekt, to raz.
Dwa, to gdyby nie był lokalny to zachowanie byłoby niezdefiniowane, bo nie używasz żadnych mechanizmów synchronizacji między wątkami.
P-116600
colorgreen19
Temat założony przez niniejszego użytkownika
» 2014-09-03 21:50:27
hmmm... wydawało mi sie ze jak przesyłam cos przez wskaźnik to przesyłam adres tego gdzie to sie znajduje czyli defacto oryginał...  czyż nie tak uczą w kursach?
P-116601
« 1 » 2 3
  Strona 1 z 3 Następna strona