serwer wieloklientowy UDP

0

Wie ktoś jak to napisać?
Mam kod:

IPEndPoint remote = new IPEndPoint(IPAddress.Any, 18900);
   while (true)
                {
                    Byte[] data = server.Receive(ref remote);
                    SetText(remote.Address.ToString()+"  "+remote.Port.ToString());
                }

Dlaczego w powyższym kodzie remote.Port.ToString() zawsze wyświetla inny port 4100,4101,4102... Za każdym włączeniem programu jest coraz wyższy port. Dlaczego tak jest? Dlaczego nie jest to port 18900 tak jak deklarowany na początku?

0

Pomoże ktoś? Niestety w internecie nie ma przykładów jak to zrobić :(

0

No ale z czym konkretnie masz problem? Pokaż mi w Internecie chociaż jeden serwer UDP przeznaczony dla pojedynczego użytkownika, czyżby w takim razie wszystkie były "wieloklientowe"?

0

Bo próbuję zrozumieć jak to ma działać. Mam jeden serwer i mogę mieć np 300 klientów. Mam zdefiniowany protokół. Wymyśliłem następujące rzeczy: UDP jest bezpołączeniowy więc nie można trzymać w osobnych wątkach połączeń do konkretnych klientów, ale we własnym protokole mam znacznik, więc jak co pewien czas przyjdzie ramka z tym znacznikiem to będę uznawał, że klient jest nadal połączony (czy to jedyna metoda sprawdzania połączenia klienta?...). No i właśnie teraz się gubię bo jak mam klasę:

UdpClient server = new UdpClient();

to ma metodę server.Receive,server.Send, server.BeginReceive,server.BeginSend
ale też server.Clients ma takie same metody. Dodatkowo można zdefiniować socketa żeby odbierał ramki UDP :

Socket udpsocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); 

Wobec tego powstaje pytanie czy to są 3 sposoby uzyskania tego samego? Na dodatek server.* nie ma metod Listen, Accept itd, a server.Clients i udpsocket mają.
Nie ogarniam tego wszystkiego. Ja po prostu chciałem żeby wielu klientów mogło do mnie wysyłać pakiety na raz abym je autoryzował a tylko z jednym na raz bym się komunikował w pełni.

Edit1:
Dlaczego server.Client i udpsocket mają nasłuchiwanie (Listen, Accept) skoro UDP jest bezpołączeniowy?

Przepraszam, że namieszane ;/

0
maszynaz napisał(a):

Dlaczego server.Client i udpsocket mają nasłuchiwanie (Listen, Accept) skoro UDP jest bezpołączeniowy?

Klasa Socket to klasa implementująca Berkley sockets, obsługuje wszystkie typy socketów jednocześnie i udostępnia wspólny interface. Pojedyncza klasa implementuje różne typy gniazd.

Listen causes a connection-oriented Socket to listen for incoming connection attempts.

0

Dobra, ponieważ UDP jest bezpołączeniowy to jakiekolwiek utrzymywanie połączenia nie ma sensu. Zrobiłem tak, że w jednym wątku odbieram pakiety:

 while (true)
{                                         
        Byte[] data = server.Receive(ref remote);   
}

Czy odebrane ramki od klientów warto dodawać do kolejki MessageQueue i potem w kolejnym wątku je dekodować? Czyli problem producent-konsument. A może żeby nie zgubić żadnego pakietu trzeba używać jakoś metody BeginReceive i Callbacki? Czy ktoś mi objaśni jak to się powinno robić? Bo na raz 300 klientów może próbować się komunikować z serwerem i wobec tego odbieranie pakietów i ich obsługa muszą być wydajne tak żeby pakiety nie ginęły.

Znalazłem taki przykład z obsługą kolejki:

Queuing packets for background processing - High packet rate capture (Example QueuingPacketsForBackgroundProcessing in the source package)

Packet rates can exceed the rate at which they can be processed by the callback routines. This can occur if the packet processing code is time intensive, such as writing to disk, or if the packet processing code is performing advanced logic. Sometimes it is possible to reduce packet rates by applying bnf filters but in other cases these filters can be complex.

If packet rates only exceed the rate of processing for short periods of time it is possible to simply defer packet processing until periods of less activity. One technique for this is to queue the packets and process them in a background thread as shown in this example.

       

        /// <summary>
        /// Checks for queued packets. If any exist it locks the QueueLock, saves a
        /// reference of the current queue for itself, puts a new queue back into
        /// place into PacketQueue and unlocks QueueLock. This is a minimal amount of
        /// work done while the queue is locked.
        ///
        /// The background thread can then process queue that it saved without holding
        /// the queue lock.
        /// </summary>
        private static void BackgroundThread()
        {
            while(!BackgroundThreadStop)
            {
                bool shouldSleep = true;

                lock(QueueLock)
                {
                    if(PacketQueue.Count != 0)
                    {
                        shouldSleep = false;
                    }
                }

                if(shouldSleep)
                {
                    System.Threading.Thread.Sleep(250);
                }
                else // should process the queue
                {
                    List<RawCapture> ourQueue;
                    lock(QueueLock)
                    {
                        // swap queues, giving the capture callback a new one
                        ourQueue = PacketQueue;
                        PacketQueue = new List<RawCapture>();
                    }

                    Console.WriteLine("BackgroundThread: ourQueue.Count is {0}", ourQueue.Count);

                    foreach(var packet in ourQueue)
                    {
                        var time = packet.Timeval.Date;
                        var len = packet.Data.Length;
                        Console.WriteLine("BackgroundThread: {0}:{1}:{2},{3} Len={4}",
                            time.Hour, time.Minute, time.Second, time.Millisecond, len);
                    }

                    // Here is where we can process our packets freely without
                    // holding off packet capture.
                    //
                    // NOTE: If the incoming packet rate is greater than
                    //       the packet processing rate these queues will grow
                    //       to enormous sizes. Packets should be dropped in these
                    //       cases
                    
                }
            } 
     }

i w wątku:

while(true)
{
// lock QueueLock to prevent multiple threads accessing PacketQueue at
            // the same time
            lock(QueueLock)
            {
                PacketQueue.Add(e.Packet);
            }
}

No i jest pytanie co zrobić jak liczba pakietów w kolejce zacznie szybko przewyższać liczbę pakietów przetwarzanych w jednostce czasu?

2
maszynaz napisał(a):

No i jest pytanie co zrobić jak liczba pakietów w kolejce zacznie szybko przewyższać liczbę pakietów przetwarzanych w jednostce czasu?

Siąść i płakać. Możesz jedynie ustawić stały rozmiar kolejki, która będzie odrzucać najstarsze pakiety albo na nowe odpowiadać czymś w rodzaju 'mam zapierdol, spróbuj później'.

0

Miałem w robocie bardzo podobny temat. Początkowo odbierałem dane i wpisywałem do kolejki. Niestety na słabym sprzęcie pojawił się problem z możliwością przetworzenia max 1k ramek na sekundę. Rozwiązałem to dość drastycznie. Każda ramka UDP analizowana jest w oddzielnym wątku i po problemie.
Jeśli otrzymywane pakiety będziesz traktował jako stare tak jak polecił @PS to niestety nie wróżę świetlanej przyszłości. Serwer musi być na tyle sprawny aby być w stanie obsłużyć tyle danych ile do niego dociera! Jeżeli serwer zaczyna Ci się zatykać przy 300 klientach (zakładam 300 do 500 pakietów na sekundę) to albo masz słaby sprzęt albo algorytm.

U mnie największym problemem było wpisywanie od 1k do 2k ramek/s do bazy. Niestety operacje dyskowe są czasochłonne.

1

Przecież taka jest natura UDP, że wcale nie musi dojść satysfakcjonująca odpowiedź na wszystkie pakiety? Wszystko zależy od zastosowania oraz możliwości sprzętowych, a to że maszynaz zakłada sobie miliard klientów (mam wątpliwości co do jego jakichkolwiek szacunków) to inna sprawa.

0
lfrenzy napisał(a):

Jeśli otrzymywane pakiety będziesz traktował jako stare tak jak polecił @PS to niestety nie wróżę świetlanej przyszłości.

Można powiadamiać klienta, że jego ramka została odrzucona albo się przedawniła, ale to nie ma sensu, UDP gubi pakiety z definicji, klient i tak nigdy nie wie, czy dane od niego dotarły do serwera. To jest JAKIEŚ rozwiązanie, olać coś, co się już przedawniło i liczyć, że klient za chwilę spróbuje znowu i tym razem będzie można to obsłużyć.

Tak naprawdę to gdybanie, autor wątku to wieczny newbie, który ostatnio próbuje pisać m. in. sniffer, nie raczył napisać, co tym razem płodzi.

0

Ignorowanie starych danych jest to "JAKIEŚ" rozwiązanie chociaż w tym momencie trzeba zastanowić się nad 1 rzeczą. Skoro system zaczyna ignorować pakiety ponieważ nie jest w stanie w czas ich przetworzyć to taki system z mojego punktu widzenia jest bezużyteczny. Gubienie ramek UDP to już zupełnie inny problem, który powinien być przemyślany w momencie wybierania rodzaju komunikacji.

0

To zależy. Ignorowanie starych danych może być pożądane. Np. po UDP przychodzą dane z jakiegoś czujnika, który je bardzo często wysyła. Systemowi jest potrzebny tylko aktualny stan czujnika. Jeżeli pakiet czeka długo na przetworzenie to już pewnie jest nieaktualny i można go olać.

W ogóle jeżeli dane nie mogą być gubione, to wybór UDP do tego celu jest bez sensu.

0

Zgadza się z Twoimi słowami @byku_guzio. Czasem ignorowanie danych jest koniecznym rozwiązaniem. Niestety nie daje mi spokoju fakt, że system zatyka się i z powodu czasu przetwarzania danych usuwamy dane czekające na analizę.

Swoją drogą zastanawiam się teraz nad jedną rzeczą. Jaki typ połączenia będzie szybszy. w transmisji danych. UDP czy TCP?

0
lfrenzy napisał(a):

Swoją drogą zastanawiam się teraz nad jedną rzeczą. Jaki typ połączenia będzie szybszy. w transmisji danych. UDP czy TCP?

Szybszy będzie raczej UDP, bo ma mniejszy narzut. A jeśli TCP jest za wolny, ale potrzebujesz pewności dostarczenia, to zaimplementuj własny protokół.

0

Zrób licznik pakietów. Każdy następny pakiet musi być inkrementowany, jeśli z jakiegoś powodu pakiet UDP nie dotrze do odbiorcy, to transmisję należy przerwać i wysłać taska do klienta z prośbą o rozpoczęcie nadawania od pakietu nr...
Takie zabezpieczenie stosuje się też również w przypadku transmisji TCP gdy klient znajduje się w ruchu i korzysta z transmisji pakietowej GPRS.

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