QTcpSocket podział wiadomości

0

Hey. Mam pytanie co do przesyłanych danych za pomocą QTcpSocket'a. Mianowicie czy jest jakaś możliwość w QT podziału strumienia danych zależnie od typu wiadomości? Np mam klasę Wiadomość, klasę jeszcze jakąś inną (serializuję i przesyłam), komunikaty PingPong, i chciałbym móc jakoś rozróżnić, który rodzaj danych przesyłam, żeby np funkcja odpowiedzialna za Pingowanie nie otrzymała danych przeznaczonych dla klasy Wiadomość. A może trzeba zbudować jakąś uniwersalną klasę?

0

A skąd Qt ma wiedzieć jaki to jest typ wiadomości? Z socketa czyta bufor danych i to Ty musisz go parsować.

0

No właśnie, czyli format wiadomości zawsze musi być ten sam (jakas jedna klasa)? Mógłbyś rozwinąć myśl, jak to najlepiej zrobić?

1

Musisz z przeczytanego bufora umieć wyciągnąć typ przekazanej wiadomości oraz jej wielkość (a także sprawdzić czy cała wiadomość jest już w buforze).
Z pamięci mogę przytoczyć trzy rozwiązania, zapewne można to rozwiązać także w inny sposób:

  • każda wiadomość zaczyna się i kończy w szczególny sposób (np. tag w xml, {})
  • każda wiadomość ma stałą wielkość
  • każda wiadomość zaczyna się nagłówkiem informującym o jej wielkości

po wczytaniu gdzieś wewnątrz wiadomości musisz zawrzeć jej typ (id w nagłówku/tag w xml) i na jego podstawie odpalić odpowiednią funkcję obsługującą.

0

Chyba wybiorę metodę 3.
1 to chyba to samo co 3 tylko bardziej przekombinowane
2 odpada, bo za bardzo może mi obciążyć łącze jezeli caly czas idą ping-pongi między klientami a serwerem (małe porcje danych) + inne wiadomości (większe)

po wczytaniu gdzieś wewnątrz wiadomości musisz zawrzeć jej typ (id w nagłówku/tag w xml) i na jego podstawie odpalić odpowiednią funkcję obsługującą.

Mam wiele klas nasłuchujących (PingPong, Message, Console) więc w każdej chyba będę musiał załączyć jakieś funkcje weryfikujące czy te dane były przeznaczone dla tej klasy?

0

Nie, najlepiej miej osobną klasę/funkcję routującą wiadomości do klas, które dostaną to co chciały i to obsłużą - inaczej będziesz powtarzał funkcjonalność routera w każdej z nich.

0

Hmm to byłoby trudne. Mam np. dla każdego usera osobny obiekt który w innym wątku go cały czas pinguje. Musiałbym przelecieć po całej tablicy klientów i wysyłać odpowiednim komunikat. A co jeżeli np. te operacje routingu wiadomości będą trwały tyle że zakłamią mi ping (ilość milisekund) bo np. transport po internetach będzie trwał 10ms a kolejne 10ms routing i wszystkie poboczne operacje = 20ms. Ta klasa routująca musiałaby być bardzo sprytna i mieć dostęp do wszystkich klas które tych danych porzebują

1

O ile nie robisz czegoś bardzo źle (albo na kalkulatorze z zeszłego tysiąclecia), to wszystko powinno wykonać się w ułamku milisekundy. Ponadto, jak Ty to sobie inaczej wyobrażasz? Wiele klas współdzielących ten sam socket i wyjmujących wszystko jak popadnie?

0

Nie neguję tej koncepcji, ba, lepszej chyba nie ma, zastanawia mnie tylko jak najefektywniej to zrobić (żeby nie powstał chaos w kodzie). Pewnie zrobię jakąś klasę nadrzędną dla wiadomości i klasy pochodne dla jej typów + klasa routująca, sprwadzająca na podstawie wielkości wiadomości, która to jest klasa pochodna. Tylko muszę się zastanowić jak dobrze przesłać już zdeserializowaną wiadomość do odpowiednich klas (Ping, Wiadomość itd.). Sygnał się w tym sprawdzi?

0

Jeśli wszystko jest w tym samym wątku to po prostu metoda wirtualna wystarczy (ewentualnie mapa wiadomość-handler, gdzie handler to pewnie std::function<bool(wiadomość)>), jeśli wątki różne to sygnał&slot lub bezpośrednio QMetaObject::invokeMethod.

0

Czytam o invokeMethod ale to jest chyba to samo co

obiekt->akcja(); 
// == ?
QMetaObject::invokeMethod(obiekt, "akcja");
1

Jeśli obiekt żyje w innym wątku - nie. Wtedy wewnętrzny mechanizm Qt zagwarantuje bezpieczną komunikację międzywątkową i wywołanie slotu w jego wątku. (to jest używane przez sygnały i sloty wewnętrznie) Możesz oczywiście przesyłać argumenty do slotów, tylko muszą one być znane mechanizmowi Qt (czyli nie obejdzie się bez qRegisterMetaType<> na początku progamu) oraz opakowane w makro Q_ARG

edit: znowu jeśli obiekt żyje w tym samym wątku, to invokeMethod jest kilka razy wolniejsze (na współczesnych komputerach to są nanosekundy, może kilka mikrosekund, ale zawsze) oraz pozbawia Cię statycznej analizy kompilatora w kontekście użycia tego slotu

0

Tzn, na pewno będzie trzeba użyć invoke bo obiekty Pingujące są w osobnych wątkach. Nie wiem jeszcze czy opłaca się tworzyć osobną klasę routującą czy załatwić to w metodzie w klasie Servera:

//sygnal wyslany od connect(server, SIGNAL(readReady()), server, SLOT(deliver())))

void Server::deliver() {
    //tutaj sprawdzenie typu wiadomości
  
    // cos w stylu:
     if(do pingow)
          for(ping : pingi) 
               ping.obierz(wiadomosc) //ping odpowie
      else if(do usera) //np jakas wiadomosc tekstowa
          //pobranie adresata i dostarczyciela i wyslanie do konkretnego klienta
          server.wyslij(client, wiadomosc)
    
}

I czy do wysyłania danych też trzeba jeden strumień czy to jest pakowane w jakąś kolejkę i mogę z różnych miejsc wysyłać dane?

1

W dokumentacji nie ma nigdzie informacji, że send jest thread-safe, więc załóż, że nie jest.

0

Mam jeszcze jeden mały problem z którym się męczę. Chcę przesłać wiadomość Ping

class Ping
{
   private:
         QString message;
   public:
         Ping(QString msg);
}

Fajnie sobie obiekt zserializowałem (szkoda że tu nie ma mechanizmu jak w Javie, że nie trzeba każdego pola osobno przepisywać do streamu) i jest problem, bo niby wysyłam 4 bajty (czyli tyle ile zajmuje ten obiekt) a odbiera mi 12. I tu już jest zonk, bo ja sprawdzam co wysłałem za pomocą porównania ilości bajtów z rozmiarem klasy (tyle że nie wiem czy to najlepszy pomysł, bo ciekawe co będzie jeżeli akurat w buforze znajdą się BUFOR -> [Ping(4), CosInnego(24)] = 28bajtów (przykład) i wtedy nie idzie sprawdzić co zostało przesłane )

   //odbieranie
    QDataStream in(sender);
    in.setVersion(QDataStream::Qt_5_0);
    qDebug() << sender->bytesAvailable(); //12
    qDebug() << sizeof(PingData);                //4

    if(sender->bytesAvailable() == sizeof(PingData)) {
        PingData pingMsg("nvm"); 
        in >> pingMsg;
        Message::console(pingMsg.getMessage());
    }
//Wysyłanie
    QByteArray bytes;
    QDataStream out(&bytes, QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_5_0);

    PingData pingMsg("Ping");
    out << pingMsg;
    qDebug() << sizeof(bytes); //4
    socket->write(bytes);
1

człowieku sizeof zwraca ci stałą wielkość, a zawartość QString jest gdzis inndzije na stercie i ma zmienny rozmiar.
Poczytaj ten wątek: http://stackoverflow.com/a/19682690/1387438

1 użytkowników online, w tym zalogowanych: 0, gości: 1