Połączenia sieciowe w TCP

0

Niedawno implementowałem na własne potrzeby połączenia sieciowe TCP wykorzystując QTcpServer i QTcpSocket.

Nie wchodząc w szczegóły, idea była taka, że program wysyła co jakiś czas pojedynczy komunikat danych (może mieć wielkość od kilku bajtów do 10 megabajtów) do innej instancji poprzez sieć.

Na podstawie www.google.com i własnych prób udało mi się zrealizować odbiór poprzez sygnał readyRead() w obiekcie klasy QTcpSocket, podłączyłem taki slot:

void PicNetwork::TcpMsgRecvEvent()
{
    QByteArray buffer = TcpSocket_->readAll();
    MsgRecvEvent(buffer, TcpSocket_->peerAddress(), TcpSocket_->peerPort());
}

void PicNetwork::MsgRecvEvent(QByteArray &Msg, QHostAddress MsgAddr, quint16 MsgPort)
{
    // Tu jest reakcja związana z odbiorem komunikatu, jako parametry jest komunikat oraz adres z portem drugiej strony komunikacji
}

Problem jest taki, że jak komunikat jest dłuższy niż kilka kilobajtów, to po drugiej stronie przychodzi więcej komunikatów, czyli jest więcej wywołań slotu TcpMsgRecvEvent, w każdym inna część komunikatu. Zdarzało się także, że dwa komunikaty wysyłane jeden za drugim zostały odebrane jako jeden komunikat. Ja poradziłem sobie z tym problemem w ten sposób, że w każdym komunikacie na początku są dopisane 4 bajty, które przenoszą długość całego komunikatu w bajtach, druga strona ma globalny bufor i globalnie zapamiętaną długość, działa według mniej więcej takiego algorytmu:

int DlugoscCalkowita = 0;
QByteArray Bufor;

void SlotOdbierajacyKomunikat(QByteArray Komunikat)
{
    if (DlugoscCalkowita == 0)
    {
        DlugoscCalkowita = LiczbaZPierwszychCzterechBajtow(Komunikat);
    }
    Bufor.append(Komunikat);
    if (Bufor.length() >= DlugoscCalkowita)
    {
        WykonanieAkcjiZwiazanejZKomunikatem(Bufor.mid(0, DlugoscCalkowita));
        Bufor.remove(0, DlugoscCalkowita);
        if (Bufor.length() >= DlugoscCalkowita)
        {
            DlugoscCalkowita = LiczbaZPierwszychCzterechBajtow(Bufor);
        }
        else
        {
            DlugoscCalkowita = 0;
        }
    }
}

Wyżej opisany sposób działa bardzo dobrze. Kiedyś miałem podobny problem w .NET i rozwiązałem go w taki sam sposób. Jak widać, w samym komunikacie mam informacje o rozmiarze, który pozwala wyczuć, kiedy komunikat został odebrany w całości. Innym sposobem jest przyjęcie pewnego ciągu bajtów oznaczającego koniec komunikatu i sprawdzenie, czy ten ciąg wystąpił.

W jaki sposób można wyczuć przyjęcie całego komunikatu, ale bez wprowadzania elementów kontrolnych do samego komunikatu, takimi jak wielkość komunikatu, element oznaczający koniec komunikatu lub przyjęcie stałej wielkości komunikatów?

Chodzi o to, żeby wykorzystać mechanizmy zapewniane przez sieć, zamiast traktować komunikaty przychodzące jako nieskończony strumień uzupełniany porcjami i analizować treść komunikatu w poszukiwaniu początku lub końca bądź poprzez odczytywanie z bufora porcji o wielkości ustalonej na podstawie samej treści komunikatu.

Gdybym nie wprowadził wielkości komunikatu, to w kodzie wyżej napisanym bym nie wiedział, kiedy odebrano część komunikatu (i która część), kiedy cały, a kiedy dwa jednocześnie.

3

Nie ma takiej możliwości, transmisja TCP symuluje strumień. To co teraz robisz (własny protokół on top of TCP) to poprawne rozwiązanie. Polskie nazwy już mniej ;​)

1

Druga opcja to użycie jakiegoś gotowca. On uwolni Cię od implementacji własnego rozwiązania na bazie TCP (bo zrobi to za Ciebie), ale trudnością może być znalezienie i dobranie pasującego do Twoich zastosowań protokołu wyższej warstwy.

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