Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

 
Reply to this topicStart new topic
> Komunikacja z protokolem UDP, Jak stworzyc pakiet ?
azerty
post 2.03.2011, 16:12:40
Post #1





Grupa: Zarejestrowani
Postów: 6
Pomógł: 0
Dołączył: 2.03.2011

Ostrzeżenie: (0%)
-----


Czesc.

Mam dokumentacje protokolu opartego o UDP:
http://xbtt.sourceforge.net/udp_tracker_protocol.html

... i nie bardzo wiem jak to ugryzc. Chodzi mi o to, jak mam podac dane. Nie moge uzmyslowic sobie co to jest offset - zakladam, ze jest to n-ty bajt w ciagu ?

  1. $transId = 1234567;
  2. $binString = pack('I@8I@12I@16', 0x41727101980, 0, $transId);
  3.  
  4. $fp = stream_socket_client($uri, $errNo, $errStr, $timeout);
  5. stream_set_timeout($fp, $timeout);
  6.  
  7. if (!$fp) {
  8. echo "ERROR: $errNo - $errStr<br />\n";
  9. }else{
  10. fwrite($fp, $binString);
  11. $r = fgets($fp, 1024);
  12. var_dump($r);
  13. echo 'ok';
  14. fclose($fp);
  15. }


Gdyby ktos byl tak mily, i wytlumaczyl mi krok 1 - "obtain connection id" oraz jak odebrane dane przekonwertowac z postaci binarnej do np. tablicy.
Go to the top of the page
+Quote Post
Crozin
post 2.03.2011, 16:37:10
Post #2





Grupa: Zarejestrowani
Postów: 6 476
Pomógł: 1306
Dołączył: 6.08.2006
Skąd: Kraków

Ostrzeżenie: (0%)
-----


Cytat
Chodzi mi o to, jak mam podac dane.
Struktura pakietu jest dosyć jasno wytłumaczona. Jako treść wiadomości musisz podać zmienną zawierającą w sobie zlepek różnych danych (do zlepienia danych musisz wykorzystać pack).
Pamiętaj, że w PHP liczby całkowite (int) mogą być 32 lub 64 bitowe, tak więc powinieneś to mieć na uwadze. W przypadku, gdy masz 64-bitowy PHP, a protokół wymaga podania danych 32-bitowych musisz pozbyć się początkowych 32 bitów. W przypadku gdy masz 32-bitowy PHP, a protokół wymaga podania 64-biotwej liczby musisz spakować dwie zmienne w jedną - ponownie: pack.

Cytat
wytlumaczyl mi krok 1 - "obtain connection id"
Jest to losowa 32-biotwa liczba, która pozwala Ci identyfikować kolejne pakiety, w skrócie robisz coś takiego:

  1. $id1 = mt_rand(0, 0xFFFFFFFF);
  2.  
  3. // wysyłasz pakiet (zawiera on pow. id)
  4.  
  5. // ....
  6.  
  7. $packet = // odbierasz pakiet
  8. if ($packet->getId() == $id1) {
  9. // odebrany pakiet jest odpowiedzią na ten wysłany tuż wcześniej
  10. }
  11.  
  12.  
  13. // Ponownie generujesz identyfikator
  14. $id2 = mt_rand(0, 0xFFFFFFFF);
  15.  
  16. // wysyłasz pakiet (zawiera on pow. id)
  17.  
  18. // ....
  19.  
  20. $packet = // odbierasz pakiet
  21. if ($packet->getId() == $id2) {
  22. // odebrany pakiet jest odpowiedzią na ten wysłany tuż wcześniej, a nie ten z samego początku
  23. }
Pamiętaj, że UDP to protokół który nie gwarantuje praktycznie niczego - dotyczy to również kolejności w jakiej dochodzą pakiety (pakiet wysłany później może dojść wcześniej).
Cytat
oraz jak odebrane dane przekonwertowac z postaci binarnej do np. tablicy.
Działanie odwrotne od pakowania danych do binarnego stringu - czyli unpack

EDIT:
Cytat
Nie moge uzmyslowic sobie co to jest offset - zakladam, ze jest to n-ty bajt w ciagu ?
Offset jest tam podany w bajtach (czyli offset = 8 oznacza "od 64 bitu"). Tutaj masz przykład struktury pakietu wysyłanego w protokole TCP: http://en.wikipedia.org/wiki/Transmission_...gment_structure - myślę, że to dobrze obrazuje o co chodzi.

Ten post edytował Crozin 2.03.2011, 16:45:03
Go to the top of the page
+Quote Post
azerty
post 2.03.2011, 21:22:00
Post #3





Grupa: Zarejestrowani
Postów: 6
Pomógł: 0
Dołączył: 2.03.2011

Ostrzeżenie: (0%)
-----


Dzieki, pomoglo.
Pogoooglowalem i ... :

32 bitowa liczba posiada 4 oktety (4*8 = 32).
64 bitowa liczba ma owych oktetów 8 (8*8 = 64).

Offset w pakiecie to numer oktetu po ktorym doklejamy kolejne dane.

I tak:

Kod
0    64-bit integer    connection_id    0x41727101980
8    32-bit integer    action    0
12    32-bit integer    transaction_id
16


(Liczymy od zera, tak jak w tablicach)
Od 0 do 7 - osiem oktetow, liczba 64 bitowa
8 - 11 cztery oktety liczby 32 bitowej
12 - 15 kolejne cztery oktety.

Dla pozostalych, ktorzy tez poszerzaja wiedze na ten temat - oktet to np: 1001 1001 1001 1001 1001 1001 1001 1001, razem 32 bity.
Kazda cyfra w HEX (szesnastkowa, 0 - F) reprezentuje 4 bity.
I tak: 1001 = 0x9 a ten dlugi ciag wczesniej: 0x99999999.

Mam 32 bitowa wersje PHP wiec 64-bitowa liczba 0x41727101980 (8 znakow - za duzo na 32 bit - max to 8) nie bedzie dzialac, rozdzielam ja wiec na dwie 32-bitowe:
0x417, 0x27101980 (po prostu rozdzielam, nie robie zadnych dzialan matematycznych).

Nastepnie pakujemy dane. W specyfikacji pisze, ze liczby sa w formacie big endian (najwazniejszy bajt pierwszy (bajt = 8 bitow (oktet))).
Nalezy wziac to pod uwage przy funkcji PACK:

  1. $transId = mt_rand(0, 0xFFFFFF);
  2. $binString = pack('N4', 0x41727, 0x101980, 0, $transId);


Wyjasnienie "N3" - N oznacza, ze pierwszy parametr powinien zostac zakodowany jako:
unsigned long (always 32 bit, big endian byte order)
"4" oznacza, ze nalezy zastosowac to do 4 kolejnych argumentow zaczynajac od aktualnego.
2 pierwsze argumenty to nasz podzielony, 64 bitowy integer, ktorego moje php nie ogarnia :-)

Ok i pozniej lecimy:

  1. $fp = stream_socket_client($uri, $errNo, $errStr, $timeout);
  2. stream_set_timeout($fp, $timeout);
  3.  
  4. if (!$fp) {
  5. echo "ERROR: $errNo - $errStr<br />\n";
  6. }else{
  7. fwrite($fp, $binString);
  8. $r = fread($fp, 255); // oczekiwane 16
  9. var_dump($r);
  10. fclose($fp);
  11. }


... i kupa. fwrite zwraca 16, czyli jest GIT. $fp jest OK - polaczenia nawiazane, var dump z fread natomiast zwraca string o zerowej dlugosci. Nie dostaje zadnej odpowiedzi.

Bede dalej probowal, natomiast tych bardziej doswiadczonych prosil bym o ewentualne sugestie. Moze jest cos o czym nie wiem. Wg. dokumentacji powinienem teraz dostac odpowiedz o dlugosci min. 16 bajtow.
Go to the top of the page
+Quote Post
Zyx
post 3.03.2011, 16:03:06
Post #4





Grupa: Zarejestrowani
Postów: 952
Pomógł: 154
Dołączył: 20.01.2007
Skąd: /dev/oracle

Ostrzeżenie: (0%)
-----


Nie stworzysz pakietu UDP, bo w UDP nie ma pakietów. Nie nawiążesz połączenia UDP, bo w UDP nie ma połączeń.

  1. $binString = pack('N4', 0x41727, 0x101980, 0, $transId);


Zainstaluj sobie jakiś dobry kalkulator, ew. weź kartkę papieru w dłoń, zamień sobie 0x41727101980 na zapis binarny, podziel to sobie na dwie 32-bitowe połówki, każdą z nich z powrotem zamień na heksy. Gwarantuję, że nie wyjdą Ci liczby 0x41727 i 0x101980.


--------------------
Specjalista ds. głupich i beznadziejnych, Zyx
Nowości wydawnicze: Open Power Collector 3.0.1.0 | Open Power Autoloader 3.0.3.0
Go to the top of the page
+Quote Post
Crozin
post 3.03.2011, 16:12:23
Post #5





Grupa: Zarejestrowani
Postów: 6 476
Pomógł: 1306
Dołączył: 6.08.2006
Skąd: Kraków

Ostrzeżenie: (0%)
-----


Cytat
Nie stworzysz pakietu UDP, bo w UDP nie ma pakietów. Nie nawiążesz połączenia UDP, bo w UDP nie ma połączeń.
Połączeń nie ma, bo jest to protokół bezstanowy, ale jeśli nie pakiety to co? Datagramy? - nie mam pojęcia czy istnieje jakieś tłumaczenie. Na dobrą sprawę w kontekście UDP pakiet UDP i datagram UDP można traktować jako synonimy.
Cytat
Zainstaluj sobie jakiś dobry kalkulator [...]
Nawet Windowsowy kalkulator takie coś posiada. wink.gif

Ten post edytował Crozin 3.03.2011, 16:13:00
Go to the top of the page
+Quote Post
erix
post 3.03.2011, 23:55:04
Post #6





Grupa: Moderatorzy
Postów: 15 467
Pomógł: 1451
Dołączył: 25.04.2005
Skąd: Szczebrzeszyn/Rzeszów




Cytat
Połączeń nie ma, bo jest to protokół bezstanowy, ale jeśli nie pakiety to co? Datagramy? - nie mam pojęcia czy istnieje jakieś tłumaczenie. Na dobrą sprawę w kontekście UDP pakiet UDP i datagram UDP można traktować jako synonimy.


Oj chyba nie do końca (coś mi świszczy, że to zależy od warstwy OSI, którą mamy na myśli; pakiety były chyba w warstwie transportowej, a datagramy nieco niżej), ale w tej chwili wyleciało mi to z głowy i mogę się mylić. wink.gif

Jeśli chodzi o skrypt, problem po prostu w logice komunikacji:

  1. fwrite($fp, $binString);
  2. $r = fread($fp, 255); // oczekiwane 16
  3. var_dump($r);
  4. fclose($fp);

W UDP, to tak, jakbyś rzucał granatem na oślep - nie interesuje Cię, czy usłyszysz, że wybuchnie. Jedyna odpowiedź, to kolejny granat od wroga. wink.gif

Summa summarum, w tej sytuacji IMO lepiej przejść na TCP, a jeśli chcesz koniecznie na UDP, to musisz otworzyć gniazdo nasłuchujące, które odbierze pakiety UDP od drugiej strony komunikacji.


--------------------

ZCE :: Pisząc PW załączaj LINK DO TEMATU i TYLKO w sprawach moderacji :: jakiś błąd - a TREŚĆ BŁĘDU? :: nie ponaglaj z odpowiedzią via PW!
Go to the top of the page
+Quote Post
azerty
post 4.03.2011, 01:13:31
Post #7





Grupa: Zarejestrowani
Postów: 6
Pomógł: 0
Dołączył: 2.03.2011

Ostrzeżenie: (0%)
-----


No tak. Jak Wy zaczniecie gadac, to dyskusja wedruje do nrPostu^n warstwy OSI, a nie o to pytam.
Datagramy, czy pakiety, za wiki:
Cytat
UDP (ang. User Datagram Protocol – protokół pakietów użytkownika)
... Pakiety UDP, zwane też datagramami ...


Uznajmy to za temat zamkniety.

Dalej, to co wspomnial Zyx o kalkulatorze itd. - tak, duzo literowek i bledow jest w tym poscie ale za cholere nie moge go edytowac.
Do tej pory nawet odpowiedzi nie moglem napisac, bo w polu edycji bylo pelno znakow html, a przy dodawaniu postu bledy - a dokladnie ich lista ... pusta.

Aktualnie mam taki kod:
  1. // 0 - 0xFFFFFFF // 28 bitow, zeby php nie zwariowalo dajac floaty
  2. $packetId = $this->getRandomPacketId(0);
  3. $packet = pack('N4', 0x417, 0x27101980, 0, $packetId);
  4.  
  5. $handle = $this->getHandle();
  6. $this->write($packet);
  7. $response = $this->read(16);
  8.  
  9. $data = unpack('Naction/Npacket/H*cid', $response);
  10.  
  11. if(!$this->hasPacket($data['packet'], $data['action']))
  12. throw new Scrape_Exception
  13. ('Host returned invalid packet ID: '.$data['packet'].':'.$data['action']);
  14.  
  15. // we have obtained our own connection id - save it for further use
  16. $this->connectionId = $data['cid'];


Problem w tym, ze w odpowiedzi connectionId pobieram jako hex-string a nie jako int unsigned 64 bit. Mozna to zrobic inaczej, lepiej ?
Kolejny problem - jezeli read() podaje bez dlugosci, badz z wielkoscia wieksza niz ma otrzymywany pakiet, dostaje error - timeout po 30 sekundach.
Czy rozwiazanie tu bedzie uzycie non-blocking stream / bez buforowania ?
I jeszcze raz - czy istnieje jakis ogolny sposob na pakowanie do danych binarnych liczb 64 bitowych ?
Ja zrobilem to zamieniajac 1 liczbe: 0x41727101980 na 2: 0x417, 0x27101980 i po zakodowaniu parametrem N dostaje dokladnie
taki ciag binarny jaki powinienem (sprawdzone bin2hex).

Polaczenie inicjalizuje:
  1. stream_socket_client($uri, $errNo, $errStr, $this->timeout)

(zaraz ktos znowu przypomni, ze UDP to bezpolaczeniowy protokol ..... )

Odczyt robie tak (i tu mam problem z dlugoscia i timeoutem grr !):

  1. if(is_null($handle))
  2. $handle = $this->handle;
  3.  
  4. $result = stream_get_contents($handle, $length);
  5. if($this->isTimedOut()){
  6. throw new Scrape_Exception
  7. ('Connection timed out while reading stream.');
  8. }
  9. return $result;


isTimedOut() pobiera dane ze stream_get_metadata - tablica z polem timed_out.

Nie rozumiem natomiast o co Erix'owi chodzilo w tym otwieraniu portow UDP ? Huh ?

Ten post edytował azerty 4.03.2011, 01:18:28
Go to the top of the page
+Quote Post
Crozin
post 4.03.2011, 03:02:54
Post #8





Grupa: Zarejestrowani
Postów: 6 476
Pomógł: 1306
Dołączył: 6.08.2006
Skąd: Kraków

Ostrzeżenie: (0%)
-----


Dochodzi 3:00 więc wybacz za formę i ewentualne błędy. wink.gif

Cytat
Problem w tym, ze w odpowiedzi connectionId pobieram jako hex-string a nie jako int unsigned 64 bit. Mozna to zrobic inaczej, lepiej ?
Upewnij się, że tekst (w formie 16-owej) ma dokładnie 16 znaków. Jeżeli ma mniej dopełnij go zerami, czyli "d50abc0bcaedf" -> "000d50abc0bcaedf". Podziel go na dwa teksty równej długości - teraz każdy reprezentuje liczbę 32-biotwą. Wykorzystując hexdec możesz zamienić to na rzeczywistą liczbę.
PHP nie gwarantuje obsługi liczb 64-bitowych, więc bezpieczniej jest trzymać je jako dwie niezależne 32-bitowe zmienne.

Cytat
Kolejny problem - jezeli read() podaje bez dlugosci, badz z wielkoscia wieksza niz ma otrzymywany pakiet, dostaje error - timeout po 30 sekundach.
Mówisz "chcę otrzymać od Ciebie 50 jabłek", więc oczywistym jest, że nie przyjdę do Ciebie mając ich tylko 40, co nie? smile.gif Przede wszystkim nie powinieneś żądać więcej niż się spodziewasz - przecież masz w tej specyfikacji podane ile konkretnie bajtów zostanie przesłane.


Na Twoim miejscu na początku poszukałbym / napisałbym chociażby najbardziej prymitywnej biblioteki do tworzenia "paczek" o konkretnej (binarnej) zawartości, udostępniającej jakieś sensowne API do odczytu / zapisu danych bo Cie szlag trafi z tym packiem i unpackiem. Nie mówiąc już, że podatność na błędy takiego kodu jest ogromna.
Go to the top of the page
+Quote Post
azerty
post 5.03.2011, 02:15:43
Post #9





Grupa: Zarejestrowani
Postów: 6
Pomógł: 0
Dołączył: 2.03.2011

Ostrzeżenie: (0%)
-----


Ok. Prawie dziala.

Odpowiedz jaka dostaje:

Cytat
ff
00000002
00590572
00000000
00000000
00000000
00000000
00000000
00000000


Nie wiem czym jest 0xFF ktore dostalem.
W specyfikacji protokolu go nie ma. Druga linia 0x2 to akcja i taka powinna byc.
Trzeci parametr to nr pakietu - tez sie zgadza.

Istnieje mozliwosc, ze to zle dzialajaca funkcja w php / albo srodowisko windows ?

Update:
Problem wystepuje tylko w wypadku uzycia funkcji fgets. fread daje dobry rezultat - wtf ...

Ten post edytował azerty 5.03.2011, 02:55:08
Go to the top of the page
+Quote Post
kiler129
post 5.03.2011, 02:56:11
Post #10





Grupa: Zarejestrowani
Postów: 566
Pomógł: 35
Dołączył: 21.06.2006

Ostrzeżenie: (0%)
-----


Cytat(azerty @ 5.03.2011, 02:15:43 ) *
Update:
Problem wystepuje tylko w wypadku uzycia funkcji fgets. fread daje dobry rezultat - wtf ...

Tip: Sprawdź znak końca linii (/r | /r/n | /n)


--------------------
flexiCMS v2 [|||||||+--] 75% done
Go to the top of the page
+Quote Post
Crozin
post 5.03.2011, 02:57:18
Post #11





Grupa: Zarejestrowani
Postów: 6 476
Pomógł: 1306
Dołączył: 6.08.2006
Skąd: Kraków

Ostrzeżenie: (0%)
-----


Cytat
Istnieje mozliwosc, ze to zle dzialajaca funkcja w php / albo srodowisko windows ?
Zawsze istnieje taka możliwość, ale raczej wątpię by to mogło być tutaj przyczyną.

Dobrze by było jakbyś udostępnił na Twój dokładny kod, bo ciężko powiedzieć skąd Ci się coś wzięło nie mając dostępu do kodu. Jeżeli jest on długi uprość go maksymalnie.
Go to the top of the page
+Quote Post
azerty
post 5.03.2011, 04:01:46
Post #12





Grupa: Zarejestrowani
Postów: 6
Pomógł: 0
Dołączył: 2.03.2011

Ostrzeżenie: (0%)
-----


Mi sie wydaje, ze jednak cos jest nie tak z moim php.
fread do tej pory zawieszal sie jak probowalem odczytac wiecej znakow niz przeslal serwer; prawie tak, jakby nie wykrywal konca strumienia - pobieralo mi wtedy pusty plik html bez informacji o bledzie (timeout) i caly serwer siadal.
Teraz nagle zaczal dzialac i sie juz nie zawiesza. Hmm.

Narazie jest ok, takze dziekuje za czas i cierpliwosc.
Co do fgets, na bugtracku php ktorys z deweloperow pisal, aby nie uzywac go do odczytu z UDP. Nie dociekam, nie chce sie denerwowac biggrin.gif

Ten post edytował azerty 5.03.2011, 05:37:09
Go to the top of the page
+Quote Post
Fifi209
post 5.03.2011, 10:37:47
Post #13





Grupa: Zarejestrowani
Postów: 4 655
Pomógł: 556
Dołączył: 17.03.2009
Skąd: Katowice

Ostrzeżenie: (0%)
-----


Cytat(kiler129 @ 5.03.2011, 02:56:11 ) *
Tip: Sprawdź znak końca linii (/r | /r/n | /n)

Raczej:
\r
\r\n
\n




--------------------
Zainteresowania: C#, PHP, JS, SQL, AJAX, XML, C dla AVR
Chętnie pomogę, lecz zanim napiszesz: Wujek Google , Manual PHP
Go to the top of the page
+Quote Post

Reply to this topicStart new topic
1 Użytkowników czyta ten temat (1 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Wersja Lo-Fi Aktualny czas: 21.06.2025 - 01:56