Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: socket_write i sockety
Forum PHP.pl > Forum > PHP
DzikiLis
Cześć,

Piszę w PHP prosty serwer działający po socketach. Schemat działania jest prosty:
1) klient wysyła ciąg znaków z komendą,
2) serwer odbiera ciąg,
3) serwer "myśli" przez kilkanaście-kilkadziesiąt sekund,
4) serwer wysyła odpowiedź,
5) klient odbiera odpowiedź i zamyka połączenie.

Sęk w tym, że mielenie w punkcie trzecim może trwać dowolnie długo i klient może w międzyczasie zamknąć połączenie, a odpowiedź zostanie wysłana na zamknięty port. Zgodnie z manualem funkcja socket_write() powinna zwrócić false w przypadku, gdy nie doszło do zapisania. Niestety, nigdy nie zwraca false, nawet jeśli klient w międzyczasie przerwie połączenie.

Wymyśliłem obejście z funkcją socket_shutdown, ale to wymusza schemat: jedno połączenie, jedno zapytanie, jedna odpowiedź. Może ktoś ma pomysł co z tym fantem zrobić?

  1. #!/usr/bin/php -q
  2. <?php
  3. $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
  4. socket_set_option($sock, SOL_SOCKET, SO_REUSEADDR, 1);
  5. socket_bind($sock, '12.34.56.789', 10000);
  6. socket_listen($sock);
  7. $clients = array($sock);
  8. while (true) {
  9. $read = $clients;
  10. if (socket_select($read, $write = null, $except = null, 0) < 1) continue;
  11. if (in_array($sock, $read))
  12. {
  13. $clients[] = $newsock = socket_accept($sock);
  14. socket_getpeername($newsock, $ip);
  15. echo "New client connected: {$ip}r\n";
  16. $key = array_search($sock, $read);
  17. unset($read[$key]);
  18. }
  19. foreach ($read as $j => $read_sock)
  20. {
  21. $data = @socket_read($read_sock, 1024, PHP_NORMAL_READ);
  22. if ($data === false) {
  23. $key = array_search($read_sock, $clients);
  24. unset($clients[$key]);
  25. echo "Client disconnected.r\n";
  26. continue;
  27. }
  28. $data = trim($data);
  29. if (!empty($data))
  30. {
  31. if ($data == 'mielenie') sleep(30);
  32. // i tutaj pojawia się problem, bo socket_write nie zwraca błędu nawet jeśli połączenie zostanie przerwane
  33. if (@socket_write($read_sock, $data."\n") !== false)
  34. {
  35. echo "Sent {$data}r\n";
  36. // działa natomiast ręczne zamknięcie socketa, ale też nie zawsze (NAT).
  37. if (@socket_shutdown($read_sock) !== false)
  38. {
  39. echo "Shutdown ok {$data}r\n";
  40. }
  41. else 
  42. {
  43. echo "Shutdown error {$data}r\n";
  44. }
  45. }
  46. else 
  47. {
  48. echo "Error {$data}r\n";
  49. }
  50. }
  51. }
  52. }
  53. socket_close($sock);
  54. ?>
marcio
a nie mozesz go uruchomic w tle??np w iframe na jakiejs stronie i ktos bedzie mial wlaczona przegladarke to skrypt bedzie dalej chodzil?
DzikiLis
Cytat(marcio @ 4.11.2007, 13:52:44 ) *
a nie mozesz go uruchomic w tle??np w iframe na jakiejs stronie i ktos bedzie mial wlaczona przegladarke to skrypt bedzie dalej chodzil?


Niestety nie. Nie mam wpływu na sposób działania klienta i czas, po którym następuje zerwanie połączenia.
marcio
nie nie zle mnie zrozumiales mowilem w takim sensie ze program gdy zaczyna swoja prace uruchamia sie w iframe na stronie przez ciebie zrobiona zrob taka opcje i wtedy bedzie dalej skanowal i potem wyswietli wyniki
DzikiLis
Cytat(marcio @ 4.11.2007, 16:39:07 ) *
nie nie zle mnie zrozumiales mowilem w takim sensie ze program gdy zaczyna swoja prace uruchamia sie w iframe na stronie przez ciebie zrobiona zrob taka opcje i wtedy bedzie dalej skanowal i potem wyswietli wyniki


Klient i serwer nie mają nic wspólnego ze stronami internetowymi. Skrypty uruchamiane są z poziomu powłoki, serwer nasłuchuje na innym porcie (10000), a skrypt jest wykonywany w nieskończonej pętli. Po co mam uruchomić go z poziomu przeglądarki i wsadzić do iframe? I w jaki sposób rozwiąże to problem błędnego działania funkcji socket_write?
marcio
ja tylko mowilem w sprawie polaczenia z klientem
rolnix
Kod
Returns the number of bytes successfully written to the socket or FALSE one error.


Tako rzecze manual... a spróbuj var_dumpnąć to, co zwraca socket_write i jakiego typu jest to wartość. Też mnie to właśnie zaciekawiło o0
DzikiLis
Cytat(marcio @ 4.11.2007, 16:58:43 ) *
ja tylko mowilem w sprawie polaczenia z klientem

Hm, to klient łączy się z serwerem. Mógłbyś rozwinąć tą myśl?

Cytat(rolnix @ 4.11.2007, 17:09:03 ) *
Kod
Returns the number of bytes successfully written to the socket or FALSE one error.


Tako rzecze manual... a spróbuj var_dumpnąć to, co zwraca socket_write i jakiego typu jest to wartość. Też mnie to właśnie zaciekawiło o0

W obu przypadkach (klient czeka do końca / klient się rozłącza w trakcie) var_dump zwraca int(8), czyli długość ciągu "mielenie". Testowałem połączenie lokalne (klient uruchamiany na tym samym komputerze co serwer), wywołanie z innego serwera z netu i z mojego komputera, który jest za NATem. We wszystkich trzech przypadkach jest to samo.
rolnix
Może trochę nie na miejscu... ale po cholerę serwer w PHP? To jest język skryptowy i prawie nijak się do tego nie nadaje. Odsyłam do C++ smile.gif
marcio
lub ewentualnie delphi/c nie wiem jak to wytlumaczyc ale jak ja zrobilem skaner portow w php to chcialem tak zrobic zeby nikt nie musial czekac na wyniki tylko program by dzialal w tle(iframe) i jak skonczy skanowac to pokazuje wyniki
DzikiLis
Cytat(rolnix @ 4.11.2007, 18:10:49 ) *
Może trochę nie na miejscu... ale po cholerę serwer w PHP? To jest język skryptowy i prawie nijak się do tego nie nadaje. Odsyłam do C++ smile.gif

Jasne, C++ z pewnością jest lepszy do tego typu zastosowań, ale w przypadku tego projektu spora część logiki biznesowej (to "mielenie") jest napisana w PHP. Serwer na socketach jest tylko małym dodatkiem do aplikacji internetowej, pozwalającym na dostarczanie danych do innego systemu. Przepisywanie części logiki w C++ tylko po to by chodziło szybciej i nie było językiem skryptowym niepotrzebnie narazi klienta na dodatkowe koszty. Na razie szukam rozwiązania "wystarczająco dobrego", a jeśli serwer na socketach odniesie sukces i zacznie generować przychody, zaproponuję klientowi przejście na C++.
rolnix
Uruchomienie programu napisanego w c++ to dodatkowe koszta? Człowieku, przecież i tak chcesz ten skrypt uruchamiać z shella. Co jest szybsze - interpretowanie i działanie skryptu czy działanie bezpośrednio kodu binarnego?

Może i przepisanie tego do C++ zajmie więcej czasu... ale napisałeś "po to by chodziło szybciej". Radziłbym bardziej zrobić tak, żeby w ogóle chodziło smile.gif
DzikiLis
Cytat(rolnix @ 4.11.2007, 18:57:02 ) *
Uruchomienie programu napisanego w c++ to dodatkowe koszta? Człowieku, przecież i tak chcesz ten skrypt uruchamiać z shella. Co jest szybsze - interpretowanie i działanie skryptu czy działanie bezpośrednio kodu binarnego?

Szybkość działania skryptu nie ma tutaj większego znaczenia. Liczba połączeń jest szacowana na 100 dziennie, potem sie może rozkręcić, jeśli klienci końcowi będą korzystać z oferowanej usługi przez ten kanał. Ale ok: szybsze w działaniu - C++, tańsze dla klienta - PHP. Przeliczyć na głos? Dopisanie serwera po socketach w PHP zajmie ~20 godzin, napisanie serwera w C++ i logiki mielącej (działającej tak samo ja ta z PHP) zajmie dobremu programiście C++ min. 100 godzin (cała logika PHP zajęła > 1200h). A, klient chce ruszać za dwa tygodnie, a nie mamy w zespole dobrego programisty C++. Którą opcję wybrałbyś na miejscu klienta?
rolnix
Wytłumaczyć mu zaistniałą sytuację. Bo - jak widzisz - w PHP to nie pójdzie...
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.