Utičnice u PHP-u. Korišćenje WebSocketa sa phpws. Asinhroni web ili šta su websocketi Kada izbjegavati korištenje websocketa

Ili početak rada sa WebSocket PHP-om bez phpDaemona

Zdravo! Oprostite na ovako dugačkom naslovu, ali nadam se da će ovaj članak biti lakši za pronalaženje početnika poput mene, jer nisam mogao pronaći ništa slično. Prije nekoliko sedmica odlučio sam da preradim klijent i server svoje igre Growing Crystals sa AJAX-om, na WebSocket-u, ali sve se pokazalo ne samo teško, već i vrlo teško. Zato sam odlučio da napišem članak koji bi pomogao najnovim WebSocket + PHP programerima da uštede nekoliko dana vremena objašnjavajući što je moguće detaljnije svaki korak u postavljanju i pokretanju prve WebSocket skripte u PHP-u.

Neću dati kod za socket server u PHP-u, jer... nalazi se u arhivi i sa komentarima. Vještački sam ograničio vrijeme rada prihvatanja konekcija na 100 sekundi kako skripta ne bi ostala u memoriji, zatvorila sve veze i ne bi morala stalno restartovati Denver kada se u nju unose izmjene. Štoviše, nakon 100 sekundi, skripta neće prestati raditi, već će čekati vezu, nakon čega će njen rad biti završen i sve utičnice će biti sigurno zatvorene. Također, za testiranje koristim localhost i port 889.

Socket_bind($socket, "127.0.0.1", 889);//povežite ga na navedeni IP i port

Datoteke iz arhive mogu se smjestiti u root localhost folder Denvera. Algoritam testiranja:

  • Pokrećemo http://localhost/socket.html, on će se automatski povezati na ws echo server ws://echo.websocket.org, možete poslati nekoliko poruka koje server mora poslati nazad, jer je ovo echo server. Ako radi, super, sve je u redu sa klijentom, ako nije, provjerite da li ste sve fajlove smjestili u jedan direktorij, da li imate pokrenut Denver i imate li internet vezu.
  • Otvorite JavaScript konzolu u istoj kartici na kojoj imate otvoren ws klijent (u GoogleChromeu 34.8.1847.137 m iu FireFoxu to se radi skoro isti meni->alati->Java Script konzola ili Ctrl+Shift+J, ali lično ja koristiti za ovu FireBug konzolu). Pošaljite još nekoliko poruka. Vidjet ćete koje poruke dolaze sa servera. Možete kliknuti na prekid veze i zatim poslati nekoliko poruka, pobrinut ćete se da poruke ne nestanu, jer. veza sa serverom ws://echo.websocket.org je prekinuta.
  • Pokrećemo naš socket server http://localhost/simpleworking.php u novoj kartici pretraživača. Poželjno je da odmah vidite i karticu klijenta sa konzolom i karticu servera. GO() ... socket_create ...OK socket_bind ...OK Slušanje utičnice... OK

    Što znači da je server spreman za dolazne veze.

  • Na kartici klijenta, u polju za adresu servera, unesite ws://127.0.0.1:889 i kliknite na ponovno povezivanje, vidimo da se ništa ne dešava na klijentu, ali poruke kao što je Klijent "ID resursa #3" je povezao Pošalji klijentu " Zdravo" pojavi se na serveru, klijent!"... OK Čekam... OK

    Što nam govori da je tehnički veza sa socketom na serveru uspostavljena, ali se na klijentu očekuje veza preko web socket protokola, a nije uspostavljena, jer Pretraživač nije primio odgovarajuća zaglavlja ws protokola. Kada pokušate da pošaljete poruku od klijenta, na konzoli bi se trebala pojaviti greška koja navodi da veza sa ws nije uspostavljena. Nažalost, skripta u GoolgeChromeu će se zaustaviti i za nove pokušaje povezivanja morat ćete ponovo učitati stranicu sa web klijentom. U FireFox-u skripta nastavlja da radi. Obavezno se ponovo povežite 100 sekundi nakon pokretanja serverske skripte kako biste joj omogućili da se uspješno završi.

    Klijent "ID resursa #4" se povezao Pošalji klijentu "Zdravo, klijente!"... OK time = 309.8900001049return go() završeno Zatvaranje veze... OK

  • Da biste konačno bili sigurni da server odgovara i da njegove poruke nisu blokirane od strane firewall-a, pokrenite skriptu servera http://localhost/simpleworking.php, pokrenite telnet i pokušajte se povezati na 127.0.0.1:889, ali ovo mora biti urađeno najkasnije 100 sekundi, od momenta pokretanja servera do zatvaranja konekcija i završetka skripte.
  • Odgovor putem telneta bi trebao biti “Zdravo, klijente!”, što znači da sve radi normalno i da je veza sa serverom dvosmjerna.

    Kada testirate na lokalnom računaru koristeći Denver, uvijek provjerite je li PHP skripta završena, u suprotnom ponovo pokrenite Denver kako biste izbjegli kolizije i zauzete portove.

    Websocket protokol

    Sada ostaje samo da naučimo naš socket server da komunicira kao web socket server, da ispravno radi u pozadini (daemon), i, što je najvažnije, na jeftinom hostingu. Malo teorije: omogućavanje serveru da prikaže poruku koju primamo od klijenta kada pokušavamo da se povežemo, dodajući $msg = "Zdravo, klijente!" kod

    Echo "Klijent \"".$accept."\" kaže:
    ";
    echo socket_read($accept,512);
    echo "";

    Možete vidjeti da pokušaj uspostavljanja veze korištenjem WebSocket protokola uvijek prati slanje zaglavlja klijenta u obaveznom skladu s formatom WebSocket protokola. Predoznaka za prikaz zaglavlja nije slučajno izabrana, jer U naslovima, prijelomi redova igraju veliku ulogu. Ovo su zaglavlja koje dobijam iz pretraživača kada se pokušam povezati na naš ws://127.0.0.1:889.

    GET / HTTP/1.1 Host: localhost:889 Korisnički agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0 Prihvati: text/html,application/xhtml+xml,application/xml q=0,9,*/*;q=0,8 Prihvati-jezik: ru-RU,ru;q=0,8,en-US;q=0,5,en;q=0,3 Prihvati-kodiranje: gzip, deflate Sec-WebSocket-verzija : 13 Porijeklo: http://localhost Sec-WebSocket-Key: ndHtpnSPk2H0qOeP6ry46A== Kolačić: vc=3; __gads=ID=9eabc58c385071c7:T=1400699204:S=ALNI_Ma_g9PZBXpi_MLKDBsao3LQiGx-EA Veza: održavanje, Pragma za nadogradnju:

    GET / HTTP/1.1 Nadogradnja: websocket Veza: Nadogradnja Host: localhost:889 Porijeklo: http://localhost Pragma: bez keša Kontrola keša: bez keša Sec-WebSocket-Key: zNMTfmc+C9UK6Ztmv4cE5g=S Seccke Verzija: 13 Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits, x-webkit-deflate-frame Korisnički agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, poput Gecko) Chrome/35.0.1916.114 Safari/537.3

    Čak i pre nego što sam počeo da učim web sokete, mislio sam da će rad sa web utičnicama biti sličan radu sa AJAX-om, gde smo slali zahteve serveru koristeći JavaScript XMLHttpRequest(), ali se u stvarnosti zadatak pokazao mnogo komplikovanijim, i prije svega zato što je WebSocket protokol na istom nivou kao i HTTP protokol (ali sa nekim nijansama) u OSI mrežnom modelu, što znači da na strani servera moramo implementirati funkcionalnost sličnu HTTP obradi (što je Apache uvijek radio ). Nastavljajući poređenja sa AJAX-om, u AJAX-u ne moramo pružati HTTP protokol jer Sve ovo radi Apache pretraživač i web server. Jednostavno šaljemo i primamo poruke. Ali, iako nam korištenje ws nameće prilično veliku količinu posla povezanog s implementacijom servera u PHP-u, ono također pruža prednosti: smanjenje količine prenesenih podataka i povećanje performansi serverskog softvera. Za uspješnu komunikaciju sa klijentima preko WebSocket protokola, server mora biti striktno usklađen sa zahtjevima standarda RFC6455, koji detaljno opisuje formate zaglavlja odgovora. Sve poruke koje se šalju putem WebSocket protokola mogu se podijeliti u nekoliko tipova: handsnake (rukovanje prilikom uspostavljanja veze), ping-pong (provjera veze) i prijenos podataka (prijenos podataka). Postoji i kraći opis protokola u opštem smislu na ruskom jeziku. Da bi se osigurala potpuna, ispravna komunikacija između servera i klijenta putem web socket protokola, potrebno je implementirati funkcije u PHP-u za primanje i raščlanjivanje zaglavlja od klijenta, kompajliranje zaglavlja od strane servera, kompajliranje ključa i broja drugih. Proučavajući materijale predstavljene na Habré-u i drugim resursima, uspjeli smo pronaći implementirane odgovarajuće funkcije, jedina zbunjujuća razlika je u tome što većina autora koristi stream funkcije za rad sa utičnicama, smatraju se kompaktnijim, ali istovremeno i intenzivnijim resursima. zbog upotrebe bafera. Da biste se prebacili na streaming funkcije za rad sa utičnicama, ne morate instalirati nikakve dodatne module osim onih koji su već instalirani, jer niti su ugrađene u PHP 5 po defaultu. Funkcije utičnice za tok uvijek počinju sa stream_socket_*. Kada koristite funkcije toka, preporučljivo je osigurati da phpinfo() podržava potreban transport za tokove.

    Registrirani prijenosi utičnice za tok: tcp, udp.

    Ako takve informacije nema u phpinfo() ili ako se pojave drugi problemi, pogledajte dokumentaciju, ali problem se može riješiti jednostavnim ažuriranjem Denvera na najnoviju verziju.
    Još jedna bitna činjenica je da većina sistema ograničava mogućnost kreiranja utičnice na portu nižem od 1024, tako da će se u svim daljim programima za socket port koristiti 8889.
    Uzimajući izvorne kodove funkcija za kodiranje i dekodiranje zaglavlja WebSocket protokola kao osnovu, uspjeli smo implementirati punopravni ws echo server. Algoritam za njegov rad je jednostavan kao u prethodnom slučaju: kreiramo ws server koristeći funkciju stream_socket_server, pokrećemo beskonačnu petlju u kojoj provjeravamo nove veze i, kada dobijemo novu vezu, stavljamo je u $connects niz, također pokrenite drugu petlju koja prolazi kroz sve veze i zatvara one neuspjele i prima poruke od otvorenih veza. Takođe sam dodao tri funkcije PHP skripti, koju sam dizajnirao kao rukovaoce događajima.

    Funkcija onOpen($connect, $info) (
    echo "otvori OK
    \n";
    }

    funkcija onClose($connect) (
    echo "zatvori OK
    \n";
    }

    funkcija onMessage($connect, $data) (
    $f = dekodiranje($podaci);
    echo "Poruka:";
    echo $f["payload"] . "
    \n";
    fwrite($connect, encode($f["payload"]));
    }

    Koji ne rade ništa osim prikazivanja poruka o događajima na ekranu. A kada primimo poruku od klijenta, mi je dekodiramo i šaljemo njen tekst, nakon što smo je prethodno kodirali, nazad. , ws klijent nije promijenjen. Možete testirati ws echo server tako što ćete preuzeti arhivu i smjestiti je u korijenski folder Denverovog localhost-a. Povežite klijenta na adresu ws://127.0.0.1:8889.
    Razgovaraćemo o suptilnostima vezanim za pokretanje ws servera u pozadini (demon) i njegovo pokretanje na hostingu. Bit će mi drago primiti komentare i povratne informacije.

    Posebno za one koji traže hosting za pokretanje svog projekta na diskusiji o web utičnicama.

    Moji članci o PHP demonima i websocketovima
    • Jednostavan websocket u PHP-u ili websocket sa apsolutnom 0

    U prethodnom članku sam govorio o . Napravili smo server u PHP-u koristeći sokete. I u ovom članku ćemo napisati klijenta u PHP-u koji će poslati zahtjev serveru i dobiti odgovor od njega.

    Evo klijentskog koda u PHP-u:

    Kod je dobro komentiran, tako da mislim da je ovdje sve krajnje jasno. Klijentov algoritam je trivijalan: kreiranje utičnice, povezivanje sa serverom, slanje zahteva, primanje odgovora, zatvaranje veze. Poslali smo vam broj 15. Ako ste pročitali prethodni članak, zapamtite da je zadatak servera da kvadrira ovaj broj i vrati ga. Stoga, ako pokrenete ovog klijenta, vidjet ćete 225 (15*15) sa servera. Zatim izdajemo naredbu za isključivanje, koja zaustavlja server.

    Sada imate minimalan skup znanja o radu sa utičnicama, a općenito je tema vrlo zanimljiva, tako da je možete detaljnije proučiti. Možete kreirati vrlo složene klijent-server aplikacije na koje se uvijek možete povezati i poslati širok spektar zahtjeva koje će server obraditi.

    (Web Sockets) je napredna tehnologija koja vam omogućava da kreirate interaktivnu vezu između klijenta (pretraživača) i servera za razmenu poruka u realnom vremenu. Websockets, za razliku od HTTP-a, omogućavaju dvosmjerni protok podataka, što ovu tehnologiju čini potpuno jedinstvenom. Hajde da shvatimo kako ova tehnologija radi i po čemu se razlikuje od HTTP-a.

    Kako funkcioniše HTTP?

    HTTP šema za razmjenu poruka

    Verovatno znate šta je HTTP (ili HTTPS) jer ovaj protokol vidite svaki dan u svom pretraživaču. Pretraživač stalno pita server da li ima novih poruka za njega i prima ih.

    Možda znate da HTTP dozvoljava različite vrste zahtjeva, kao što su POST, GET ili PUT, od kojih svaki ima svoju svrhu.

    Kako rade websockets?

    Šema za razmjenu poruka kada koristite websockets

    Websockets ne trebaju vaše ponovljene zahtjeve da odgovore. Dovoljno je izvršiti jedan zahtjev i sačekati odgovor. Možete jednostavno slušati server, koji će vam poslati poruke kada bude spreman.

    Websockets se mogu koristiti ako razvijate:

    • aplikacije u realnom vremenu;
    • aplikacije za ćaskanje;
    • IoT aplikacije;
    • igre za više igrača.
    Kada biste trebali izbjegavati korištenje websocketa?

    Skoro nikada. Jedini nedostatak je nekompatibilnost sa nekim pretraživačima, ali 95% pretraživača već podržava web utičnice.

    U nekim slučajevima i dalje vam neće trebati websockets. Ako gradite jednostavan CMS, vjerovatno vam neće trebati funkcionalnost u realnom vremenu. Također ne biste trebali koristiti websockets u REST API-ju jer će HTTP zahtjevi poput GET, POST, DELETE i PUT biti dovoljni.

    Praktični primjeri

    Primeri ispod koriste JavaScript za klijenta i Node.js za server. Primjeri su vrlo jednostavni i malo je vjerojatno da će biti korisni u praksi, ali će vam pomoći da shvatite suštinu.

    Websockets

    Primjer razgovora sa web utičnicom neka ws = new WebSocket("ws://localhost:8080"); // izlaze nove poruke na konzolu ws.onmessage = ((data)) => ( console.log(data); ) // pošalji poruku ws.onopen = () => ws.send("Text");

    Const WebSocket = require("ws"); // kreiramo novi websocket server const wss = new WebSocket.Server(( port: 8080 )); // šaljemo klijentima kada funkcija clientValidator vrati true. ovo je wss. wss.broadcast = function(data, clientValidator = () => true) (this.clients.forEach(client => ( if (clientValidator(client)) ( client.send(data); ) )); ) wss .on ("connection", ws => ( // događaj će se pokrenuti kada klijent pošalje poruku ws.on("message", message => ( // poslati poruku svima osim autoru wss.broadcast(message , klijent => klijent == ws));

    Evo ilustracije kako websocketi rade:

    Demonstracija rada web socketa

    HTTP ekvivalent

    Budući da HTTP mora stalno provjeravati kanal za nove poruke, možete koristiti “prljavu provjeru” - pristup u kojem klijent provjerava server za nove poruke na određenoj frekvenciji (recimo, svakih 200 ms).

    Kako Yandex koristi vaše podatke i mašinsko učenje za personalizaciju usluga -.

    U ovom članku ćemo se osvrnuti na rad sa phpws bibliotekom, koja je potrebna za organizovanje aplikacija ili WEB aplikacija na osnovu soketa i pokretanje nekoliko standardnih primera koji su predstavljeni na stranici GitHub repozitorija ovog projekta.

    Bilješka. Naše utičnice će raditi i na strani servera i na strani klijenta. Na strani servera to će raditi standardni WebSocket, koji se pojavio u HTML5, a posao na strani servera, gdje imamo PHP, će obavljati phpws biblioteka. Postoji mnogo sličnih biblioteka, možda najistaknutija Ratchet, koja mi se učinila glomaznom za moj mali projekat i odlučio sam se na phpws.

    Potreban nam je kompozitor

    Vrlo zgodna stvar koja će olakšati rad sa ovisnostima i bibliotekama uključenim u projekte. Prema univerzalnom standardu kodiranja, ili, jednostavnije, prema ispravnom pisanju koda, sve biblioteke, paketi, zavisnosti ili projekti obično se pohranjuju u repozitorijuma izvornog koda, koji se zatim povezuju s projektom u par naredbi preko paketa. menadžera ili preko menadžera zavisnosti. Svaki jezik ima svog menadžera, ili skoro svaki, pa hajde da se naoružamo ovim alatom i instaliramo ga na sistem komandom u Linuxu

    $ curl -s https://getcomposer.org/installer | php

    Preuzeli smo je, ali kompozitor komande neće biti izvršene putem PATH, pa ćemo preuzetu datoteku premjestiti u /usr/local/bin

    $ mv composer.phar /usr/local/bin/composer

    Izvršavamo naredbu i dobivamo rezultat u obliku instrukcija i naredbi Composer, što ukazuje na uspješnu instalaciju

    $composer

    Za Windows i Mac, uputstva možete pogledati na sajtu.

    Bilješka. Sve zavisnosti koje treba da budu uključene moraju biti navedene u datoteci composer.json u korenu projekta, koja će preuzeti, ažurirati i prikupiti sve zavisnosti u jednu fasciklu dobavljača, iz koje se onda može učitati putem autoloadera klase. Composer ima svoje vlastito spremište paketa i biblioteka pod nazivom Packagist, što vam omogućava da navedete dobavljača/paketa i on će biti instaliran. Da, možete specificirati specifične adrese svn/git spremišta u composer.json, ali to je nezgodno. Mnogo je zgodnije imati neku vrstu centralne tačke gde se nalaze podudaranja paketa sa njihovim adresama spremišta. Ovo je Packagist.

    Potrebna nam je phpws biblioteka

    Da bismo se povezali sa projektom, potrebno je da odemo u koren fascikle projekta ili u poddirektorijum, ako je ovo deo projekta, i tamo instaliramo ovu biblioteku, ali prvo treba da kreiramo composer.json na ovom mestu, koji zatim ćemo izvršiti kroz konzolu koristeći komande composer-a i nakon što sve pročitamo instalirat ćemo ga za nas. Da biste to učinili, kreirajte ovu datoteku sa sljedećim sadržajem

    ( "repozitoriji": [ ( "tip": "vcs", "url": "https://github.com/Devristo/phpws" ) ], "zahtevati": ( "devristo/phpws": "dev-master " ) )

    U ovom slučaju, naznačili smo preuzimanje direktno iz GitHub repozitorija bez posredovanja Packagista.

    Izvršimo ovu datoteku naredbom

    $ composer install

    Nakon toga, u folderu će se pojaviti podfolder dobavljača sa preuzetim bibliotekama i sve što treba da uradimo je da ih povežemo i koristimo.

    Potrebno nam je osnovno razumevanje kako WebSocket radi sa PHP-om

    I tako, na trenutak, hajde da shvatimo šta da radimo sa preuzetim bibliotekama i kako ih koristiti, neću ulaziti u dubinu, pa ako je na prstima, onda su nam potrebne 2 datoteke:

  • client.html je fajl na strani klijenta koji vidi osoba iza pretraživača. Koristi JavaScript za rad sa utičnicom;
  • server.php je zapravo naš socket server, koji obrađuje sve zahtjeve klijenta i na njega odgovara obrađenim odgovorima.
  • Da bismo se povezali, potrebno je da navedemo šemu veze ili komunikacioni protokol, ip - adresu servera. Ako se radi o udaljenom serveru, potrebno je navesti ip adresu hosta ili VPS-a, a ako je lokalna, onda localhost, koja je jednaka adresi 127.0.0.0, a navodimo i port na kojem je server servis će biti pokrenut pod vlastitim PID-om. Svi ovi podaci se navode prilikom kreiranja instance veze.

    Za klijentski dio:

    Var socket = "ws://127.0.0.0:12345/";

    Za serverski dio:

    $server = novi WebSocketServer("tcp://127.0.0.0:12345", $loop, $logger);

    Standardni primjer prikaza trenutnog vremena servera, ažuriranog na sekundu

    Da bi ovaj primjer funkcionirao, potrebno je jednom pokrenuti server.php datoteku kroz konzolu i nakon izvršavanja ove skripte, pokrenuti socket server sa njegovim PID-om

    Šta radi primjer? Primjer pokazuje kako, do djelića sekunde, socket ažurira informacije o vremenu serveru i šalje ih klijentu

    Klijentski dio:

    Tajmeri Server Vrijeme Status: Vrijeme:

    Var socket = new WebSocket("ws://localhost:12345"); socket.onopen = function(msg)(document.getElementById("status").innerHTML = "Online"; ); socket.onclose = function(msg)(document.getElementById("status").innerHTML = "Offline"; ) socket.onmessage = function(msg)(document.getElementById("time").innerHTML = msg.data; ) ;

    Serverski dio:

    #!/php -q