Audio-chat przez UDP - czy są potrzebne dwa porty?

0

Hej! Walczę z protokołem UDP w Qt i wysyłaniem dźwięku za pomocą socketów. W jedną stronę już działa. Chciałbym, aby między klientami pośredniczył serwer. Czy potrzebuję dwóch osobnych portów - jednego dla każdego klienta? Czy może wystarczy jeden port? Tylko wtedy jak rozpoznać, z którego adresu została wysłana wiadomość?

0

https://pl.wikipedia.org/wiki/User_Datagram_Protocol
Zobacz co w pierwszych 64 bajtach.

0
_13th_Dragon napisał(a):

https://pl.wikipedia.org/wiki/User_Datagram_Protocol
Zobacz co w pierwszych 64 bajtach.

void server::startServer()
{
    socketServer = new QUdpSocket();
    socketClient = new QUdpSocket();
    socketServer->bind(1234);
    connect(socketServer, &QUdpSocket::readyRead, this, &server::socketReadyRead);
}

void server::socketReadyRead()
{
    QByteArray buffer;
    while(socketServer->hasPendingDatagrams())
    {
        QHostAddress sender;
        quint16 senderPort;
        buffer.resize(socketServer->pendingDatagramSize());
        socketServer->readDatagram(buffer.data(), buffer.size(), &sender, &senderPort);
        //socketClient->writeDatagram(buffer.data(), buffer.size(), sender, senderPort);
    }
}

Ok, mogę funkcją readDatagram() odczytać adres i port nadawcy. Tylko teraz pytanie jak to wysłać do drugiego klienta, bo ostatni datagram to ten pierwszy klient...
Jak skomunikować ze sobą pierwszego i drugiego klienta? Pewnie za pomocą gąszczy if'ów można by do tego dojść, ale za każdym razem trzeba by sprawdzać czy kolejny datagram różni się adresem od poprzedniego. Do tego jeszcze jakieś pomocniczne zmienne i flagi. Chyba, żeby sprawdzić połączenie gniazdem Tcp i potem na podstawie tego wysyłać datagramy na konkretny port klienta, ale jakoś tego nie widzę. Jest jakiś prosty sposób, żeby to ogarnąć? Proste rozwiązania często są najlepsze ;)

1

Zacznij od poczytania czym się różni UDP od TCP. Różnica pomiędzy nimi i będzie odpowiedzią na twoje pytanie.

0
_13th_Dragon napisał(a):

Zacznij od poczytania czym się różni UDP od TCP. Różnica pomiędzy nimi i będzie odpowiedzią na twoje pytanie.

UDP jest bezpołączeniowy. Nie ma gwarancji, że wszystkie pakiety dojdą. Różnice znakomicie pokazała mi poprzednia wersja mojego małego projektu, kiedy wysyłając dźwięk z mikrofonu przez TCP miałem mega opóźnienia, ale wszystko docierało do celu. Przy UDP tego problemu nie było. Tylko wcześniej wysyłałem dźwięk w jedną stronę. Teraz chcę, żeby pośredniczył w tym serwer, aby wystarczył tylko adres serwera sztywno wpisany w aplikacje.

0

Narysuje obrazek jak by ta architekta poleczenia miała działać , bo napisałeś "adres i port nadawcy" oraz skomunikowanie dwóch klientów to czy chcesz ich połączyć ze sobą ? A co jak obaj za NAT ?

Dlaczego nie tak ?
KLIENT1 ==[wysyla dane do serwer] ==> SERWER ===[odebrane dane wysyła do drugiego klienta] ==>KLIENT2

0
Adamek Adam napisał(a):

Narysuje obrazek jak by ta architekta poleczenia miała działać , bo napisałeś "adres i port nadawcy" oraz skomunikowanie dwóch klientów to czy chcesz ich połączyć ze sobą ? A co jak obaj za NAT ?

Dlaczego nie tak ?
KLIENT1 ==[wysyla dane do serwer] ==> SERWER ===[odebrane dane wysyła do drugiego klienta] ==>KLIENT2

Chcę to zrobić dokładnie tak jak na załączonym obrazku ;)
Muszę sprawdzić każdy datagram od KLIENT1 pod kątem adresu nadawcy przed dostarczeniem do KLIENT2?

1

Jeśli KLIENT1 wysyła informacje do serwera to musisz w jakiś sposób zawrzeć adresata w wiadomości. No bo jak inaczej: co jak KLIENT1 chce uderzyć do KLIENT3 a nie do KLIENT2?

Czy może wystarczy jeden port?

Tak

0
slsy napisał(a):

Jeśli KLIENT1 wysyła informacje do serwera to musisz w jakiś sposób zawrzeć adresata w wiadomości. No bo jak inaczej: co jak KLIENT1 chce uderzyć do KLIENT3 a nie do KLIENT2?

Czy może wystarczy jeden port?

Tak

Pierwszy pomysł po chwili zastanowienia jaki mi przyszedł na myśl to dopisać adres powiedzmy w ostatnich bajtach w QByteArray buffer po stronie klienta i wczytywać te dane po stronie serwera. O czymś takim mówisz?

0
// QUdpSocket *udpSocketOut;
// QAudioInput *input;

void MainWindow::on_sendButton_clicked()
{
    socket = new QUdpSocket();
    socket->connectToHost("192.168.0.111", 1234);
    input = new QAudioInput(setFormat(), this);
    input->start(socket);
}

QAudioFormat MainWindow::setFormat()
{
    QAudioFormat format;
    format.setSampleRate(44100);
    format.setChannelCount(2);
    format.setSampleSize(32);
    format.setCodec("audio/pcm");
    format.setByteOrder(QAudioFormat::LittleEndian);
    format.setSampleType(QAudioFormat::UnSignedInt);

    return format;
}

To kod po stronie klienta, który w najprostszy sposób wysyłał to co wpadało do mikrofonu.
Z tego co się orientuje to dzięki funkcji connectToHost() mogę mikrofon przekierować bezpośrednio do socket'u.
Jak zrobić to trochę na niższym poziomie, żebym mógł manipulować wysyłanym datagramem?

1

To jest zbyt naiwna implementacja

  1. nie masz kontroli na podziały na datagramy
  2. możesz utracić datagram
  3. kolejność odbieranych datagramów nie musi być zachowana

Efekt taki, że będzie działać zależnie od fazy księżyca.

UDP jest dość skomplikowane w obsłudze większych danych, dlatego większość protokołów wykorzystuje TCP.
Przykładowo opracowanie protokołu HTTP3 (które przeszło na UDP) zajęło 10 lat (niedawno był release). Można zobaczyć w działaniu na YouTube.

Szczerze, to do zabawy UPD jest Ok jak chcesz zrobić chat, gdzie dane są proste, a obsługa brakujących lub złej kolejności datagramów jest prosta. Dla wymiany audio jest to już spore wyzwanie.

0

Zdaję sobie sprawę, że nie wymyśle sposobu, który zrewolucjonizuje informatykę czy chociaż dorówna dobrym aplikacjom, ale mimo wszystko nie chcę składać broni. Proszę o poradę jak w tym frameworku uzyskać możliwość przesyłu danych niekoniecznie za pomocą connectToHost() i jak strumieniować dane dzięki czemu będę miał możliwość modyfikacji każdego datagramu, który będzie zawierał dodatkową informację dotyczącą docelowego odbiorcy datagramu.

0
void MainWindow::on_sendButton_clicked()
{
    socket = new QUdpSocket();
    input = new QAudioInput(setFormat(), this);
   
    QByteArray buffer;
    QIODevice *io = input->start();
    buffer = io->read(sizeOfDatagram);
    socket->writeDatagram(buffer.data(), buffer.size(), IPaddress, port);
}

Coś takiego ma prawo zadziałać? Wtedy mógłbym zmodyfikować buffer, żeby zawierał dodatkowe dane, które chcę przesłać.

1

a propos HTTP3 to może zamiast się męczyć z czystym UDP od razu przejdź na protokół QUIC https://github.com/microsoft/msquic które jest wrapperem na UDP i łączy chyba wszystkie pozytywne cechy TCP i UDP plus od razu załatwia szyfrowanie i dodaje parę nowoczesnych feature'ów

Ja UDP używałem jedynie do wzajemnego wykrywania się klientów w sieci przez broadcast, do wszystkiego innego był zbyt toporny

0
obscurity napisał(a):

a propos HTTP3 to może zamiast się męczyć z czystym UDP od razu przejdź na protokół QUIC https://github.com/microsoft/msquic które jest wrapperem na UDP i łączy chyba wszystkie pozytywne cechy TCP i UDP plus od razu załatwia szyfrowanie i dodaje parę nowoczesnych feature'ów

Ja UDP używałem jedynie do wzajemnego wykrywania się klientów w sieci przez broadcast, do wszystkiego innego był zbyt toporny

Dzięki za propozycje. Może przyjdzie i na to czas. Póki co bawię się moim projektem w celach edukacyjnych i dla fun'u ;) Chciałbym mieć solidne podstawy.

    QByteArray buffer;
    QIODevice *io = input->start();
    while(true)
    {
        buffer = io->read(input->bufferSize());
        socket->writeDatagram(buffer.data(), buffer.size(), QHostAddress("192.168.0.111"), 1234);
    }

To zadziałało. Tylko teraz muszę ustalić:

  1. warunek w pętli while
  2. wielkość przesyłanych danych
    a) tu rodzi się pytanie, jaka jest optymalna wielkość datagramu
    b) czy odbierany datagram przez serwer (readDatagram()) zawiera nagłówek IP (spodziewam się, że następuje dekapsulacja)? Chciałbym jakoś obliczyć miejsce, gdzie miałbym wstawić dodatkowe dane...

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