Odbiór dużej ilości socketów

0

Cześć,
stworzyłem aplikacje klient-serwer. Serwer wysyła do klienta dane w postaci: "pole|pole|pole|pole". Pojawił mi się problem w momencie kiedy wysyłam duże ilości danych.

Klient odbierając pakiet, odpala nowy wątek, który obrabia sobie te dane.

Problemem jest to, że kiedy wysyłam w pętli dużo takich pakietów to klient nie wyrabia z obsługiwaniem tego. Części nie wyłapuje, część pakietów dochodzi połączona i wtedy klient świruje.

Rozwiązałem to dając przy każdym przejściu pętli po stronie serwera sleepa, żeby klient miał czas odpalić wątek. Czy jest to jednak eleganckie rozwiązanie? Czy jednak powinienem spróbować dodać jakiś znak końca pakietu i czekać po stronie klienta aż dojdzie całość ewentualnie wtedy to sobie obrabiać?

W tym przypadku sleep zdaje egzamin ale kiedy kilka watkow z serwera wysle do klienta pakiety to moga pojawic sie problemy.

Czy jestem przewrazliwiony czy jednak powinienem to inaczej rozwiazac? :) Moge prosic o porade?

pozdrawiam!

1

Ale lecisz na TCP czy UDP?

W przypadku TCP masz mechanizmy kontroli, które powinny zapobiegać gubieniu się pakietów.
W UDP musisz sam zadbać o taki mechanizm.

Ja jednak mam podejrzenie (jeśli jeszcze tego nie sprawdziłeś), że problem może być z buforem, który trzyma te pakiety w kolejce do obrobienia.

0

Lece na TCP. Większy problem moim zdaniem teraz to łączenie się tych pakietów. Pewnie powinienem dać jakiś znak końca pakietu żebym przy obrabianiu wiedział gdzie się kończy a gdzie zaczyna następny.

W aplikacji klienta w funkcji receive jedyne co mam to odpalenie wątku i wrzucenie odebranych informacji do niego.

Nie wiem jak to rozwiązac inaczej w sytuacji kiedy pakiet jest zlepkiem dwóch gdzie drugi jest niepełny. Wrzucic to gdzieś do bufora, zakończyć na tym wątek i czekac aż przyjdzie kolejna część pakietu i wtedy w następnym wątku sprawdzić czy w buforze coś nie czeka?

//edit
hmm a moze w tych watkach wrzucac wszystko do jednego bufora i zostawic obsluge tego jednemu wydzielonemu watkowi?

0

Nigdy nie masz pewności, że dostaniesz ramkę w całości, to chyba normalne, że musisz zbierać dane do bufora i je analizować....

0

Domyślam się, dlatego pytam jak to się rozwiązauje 'standardowo' żebym nie wymyślał jakiś własnych dziwacznych rozwiązań ;)

0

Nie czaję odbierając pakiety masz pętlę w której po odebraniu części danych (obieg pętli) odpalasz wątek który ma obrobić te dane i się zakończyć i po tym dopiero byś chciał odebrać następną część danych?

1

wątek obsługujący połączenie (1) (TYLKO odbiera dane i zapisuje do bufora)
|
/
bufor FIFO
|
/
wątek obrabiający dane z bufora (2)

  1. działa tylko na czas połączenia klienta i a tylko odebrać i zapisać do bufora dane
  2. działa dopóki nie skończą się dane w buforze i (1) zasygnalizuje, że skończył odbierać
1

Tak jak już zostało to powiedziane pakiety mogą się sklejać lub przychodzić pocięte (tzn. część w 1 pakiecie kolejna w następnym).

Ja realizuje to w ten sposób - jest 1 wątek, który zajmuje się odbieraniem i składaniem pakietów - tutaj w zależności czy jest to połączenie synchroniczne czy asynchroniczne będziesz miał albo nieskończoną pętlę czekającą na dane z bufora albo zdarzenie, które wywoła twój event, sprowadza się to do tego samego...
Czyli do dzielenia sklejonych pakietów lub czekania na nadejście kolejnego chunk'a. Jeśli już złożę jeden pakiet w całość wg. protokołu - u ciebie będą pewnie rozdzielone | (pipeline) to robie Event od faktycznego "odbieracza", który już ma pewność że pakiet przyszedł jeden cały (czyli zawartość pomiędzy ||) i można zająć się obróbką faktyczną pakietu.

0

Zrobiłem taką procedurę. Działa poprawnie, rozdziela komunikaty od serwera nawet jeżeli przylecą sklejone w jednym sockecie. Jeżeli jakiś został rozdzielony to również go skleja. Na pewno można to zapisać o wiele prościej ale już dzisiaj późno i stać mnie było tylko na takie coś ;)

Kiedy przychodzą pakiety to wrzucam je do "Bufor", który jest typu TStringList. Zabezpieczam to wszystko TCriticalSection.
Dodalem wiecej komentarzy zeby latwo bylo sie zorientowac o co chodzi.

procedure TObslugaBufora.Execute;
var
  stop,P,K:integer;
  socket, komunikat, urwany:string;
  koniec:boolean;
begin

stop:=0;              //na potrzeby nieskonczonej petli, ktora sprawdza bufor
urwany:='';          //tu bedzie trzymany fragment urwanego komunikatu

  repeat               //zaczynamy nieskonczona petle

    socket:='';       //zerujemy przed pobranie z bufora

      CSBufor.Enter;

      if Bufor.Count>0 then
      begin
      socket:=Bufor[0];     //pobieramy z bufora i wyrzucamy z niego pobrany socket
      Bufor.Delete(0);
      end;

      CSBufor.Leave;

      if socket<>'' then      //jezeli cos jest w buforze...
      begin

        repeat                    //obrabiamy socket az wydzielimy wszystkie komunikaty

          koniec:=false;       //jezeli zakonczymy prace nad socketem

           //kazdy komunikat zaczyna sie znakiem <
           //a konczy > np. <test1|test2|test3|test4>

          if urwany<>'' then  //poprzedni socket konczyl sie urwanym komunikatem
          begin

            K:=AnsiPos('>', socket);

            komunikat:=Copy(socket, 1, K);
            urwany:=urwany+komunikat;

            Delete(socket, 1, K);

            Przekaz(urwany);     //docinamy brakujacy fragment i przekazujemy do wlasciwej obrobki

            urwany:='';

          end;

          P:=AnsiPos('<', socket);    
          K:=AnsiPos('>', socket);

            if (P<>0)and(K<>0) then //jezeli znaleziono kolejny komunikat
            begin

              komunikat:=Copy(socket, P, K);
              Delete(socket, P, K);

              Przekaz(komunikat);       //docinamy i przekazujemy do obrobki

            end         //znalezlismy juz wszystkie pary w sockecie
            else
            begin
              koniec:=true;
            end;

            if (P<>0) and (K=0) then  //jezeli ostatnia para jest urwana
            begin
              urwany:=socket; //reszta, ktora zostala z socketa, zostanie doklejona przy nastepnym sockecie
              koniec:=true;
            end;

        until koniec;    //koniec obrobki socketu

      end;

  Sleep(50);          //chwile spimy
  until stop=1;       //nieskonczona petla


end;

A dziala to tak:

Dane wejsciowe:

Socket1:
<test0|test2|test3|test4|test5|test6><test1|test2|test3|test4|test5|test6><test2|test2|test3|test4|test5|test6><test3|test2|test3|test4|test5|test6><test4|test2|test3|test4|test5|test6><test5|test2|test3|test4|test5|test6><test6|test2|test3|test4|test5|test6><test7|test2|test

Socket2:
3|test4|test5|test6><test8|test2|test3|test4|test5|test6><test9|test2|test3|test4|test5|test6><test10|test2|test3|test4|test5|test6><test11|test2|test3|test4|test5|test6><test1

Socket3
2|test2|test3|test4|test5|test6>

Dane wyjsciowe:

<test0|test2|test3|test4|test5|test6>
<test1|test2|test3|test4|test5|test6>
<test2|test2|test3|test4|test5|test6>
<test3|test2|test3|test4|test5|test6>
<test4|test2|test3|test4|test5|test6>
<test5|test2|test3|test4|test5|test6>
<test6|test2|test3|test4|test5|test6>
<test7|test2|test3|test4|test5|test6>
<test8|test2|test3|test4|test5|test6>
<test9|test2|test3|test4|test5|test6>
<test10|test2|test3|test4|test5|test6>
<test11|test2|test3|test4|test5|test6>
<test12|test2|test3|test4|test5|test6>

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.