ugrás a tartalomhoz

Websocket, socket kezelés - üzenetküldés a klienseknek

lacy · 2012. Már. 28. (Sze), 12.38
Sziasztok!

Websocketen keresztül próbálok megvalósítani kétirányú kommunikációt a kliens és szerver között. Odáig jutottam el, hogy ha a kliensek küldenek valamit a szerverre, akkor az a szöveg kimegy a többi felcsatlakozott kliensre is, mint egy chat szoba.

A problémám az, hogy nem tudom, hogy tudnék a klienseknek úgy adatot küldeni, hogy előtte nem keresik fel a szervert / nem küldenek semmit a szerverre (azaz nem response-ként akarok adatot kiküldeni).

A következő megvalósítást használom: http://code.google.com/p/phpwebsocket/
Az Issues részben van egy patch hozzá amit felraktam, így a legújabb stable Chrome-al működik a kommunikáció a legújabb specifikációknak megfelelően. A következő kódot használom a szerveren (részlet):

$master  = WebSocket("0",12345);
$sockets = array($master);
$users   = array();
$debug   = true;
$sender = '';
$msg = '';
while(true){
  $changed = $sockets;
  socket_select($changed,$write=NULL,$except=NULL,NULL);
  foreach($changed as $socket){
    if($socket==$master){
      $client=socket_accept($master);
      if($client<0){ console("socket_accept() failed"); continue; }
      else{ connect($client); }
    }
    else{
      $bytes = @socket_recv($socket,$buffer,2048,0);
      if($bytes==0){ disconnect($socket); }
      else{
        $user = getuserbysocket($socket);
        if(!$user->handshake){ dohandshake($user,$buffer); }
        else{
          process($user,$buffer); 
          
          // itt megszórom a többi klienst az üzenettel
          foreach($sockets as $socket) {
            $user2 = getuserbysocket($socket);
            if(is_object($user2) && $user->socket != $user2->socket) {
              send($user2->socket, unwrap($buffer));
            }
          }
        }
      }
    }
  }
}

function WebSocket($address,$port){
  $master=socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("socket_create() failed");
  socket_set_option($master, SOL_SOCKET, SO_REUSEADDR, 1) or die("socket_option() failed");
  socket_bind($master, $address, $port) or die("socket_bind() failed");
  socket_listen($master,20) or die("socket_listen() failed");
  echo "Server Started : ".date('Y-m-d H:i:s')."\n";
  echo "Master socket  : ".$master."\n";
  echo "Listening on   : ".$address." port ".$port."\n\n";
  return $master;
}
A következő a gond. A fenti kód (és a hiányzó részek) folyamatosan futtatva van PHP-val. Akkor fut le, amikor az egyik socketen van bejövő adat. Így meg tudja szórni az üzenettel a többi klienst.

Nekem viszont a rendszerem egy Symfony alapú cucc, ahol én adott action-ökben (metódusokban) szeretném lekérdezni a felkapcsolódott socketeket, hogy küldeni tudjak nekik valamit socket_write-al. Hogy tudom ezt megcsinálni? Próbáltam a socket_connect-et így:

$master=socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("socket_create() failed");
socket_set_option($master, SOL_SOCKET, SO_REUSEADDR, 1) or die("socket_option() failed");
socket_connect($master, '0', 12345) or die("socket_connect() failed");
Ez talán még sikerült is, legalábbis nem kaptam hibát. De most, hogy tudnám megsorozni a klienseket üzenettel? Hogy kérjem le a socketeket? Próbáltam így:

$sockets = array($master);
foreach($sockets as $socket){
  echo "socket";
}
De csak 1x írja ki, hogy socket, pedig a 2 kliens is fel van csatlakozva.:( Van valakinek ötlete?

Nagyon köszönöm!
 
1

Problema

janoszen · 2012. Már. 28. (Sze), 14.02
Remelem, jol ertettem a dolgot. A meglatasom a kovetkezo:

A PHP egyik nagyon komoly gyengepontjara tapintottal ra, ugyanis csak nagyon korlatozottan kepes aszinkron mukodesre. Tudsz pl. mysqli_multi_query()-t hivni, de azt mar a socket muveletekkel nem tudod osszehazasitani. Vagyis ha egy MySQL muveletre varsz, a socketen nem tudsz semmit csinalni.

A masik gond, hogy a PHP-nak a socket_read fuggvenynel van egy igen nehezen kidebugolhato memleakje, ami csak bizonyos esetekben jon elo, es a Symfony sem eppen arra lett megirva, hogy hosszu idon keresztul memoriatakarekosan fusson.

Ezen felul az is problema, hogy a websocket tamogatas meg igen korlatozott, tehat ha kicsit is mainstream piacra epitesz szolgaltatast, nem teheted meg, hogy csak erre epitesz.

Azt javaslom, hogy a websocket logikadat fuggetlenitsd a PHP alkalmazasodtol es ird meg NodeJS es socket.io komboval, a ketto kozotti kommunikaciot pedig oldd meg API hivasokkal vagy DB-be irassal, mert azt meg tudod oldani, hogy jol egyutt mukodjon az async loop-pal.

Ha ennek ellenere kitartasz a PHP mellett, szivesen segitek, de keszulj fel, hogy meg kell ismerkedned a GDB-vel.
3

nem hangzik túl jól

lacy · 2012. Már. 28. (Sze), 17.13
Köszi a választ. Ez a socket_read memleak dolog nem hangzik túl jól.:( Egyelőre még küzdök, de megfontolom a NodeJS-es megoldást. Hogy végül mi lett a megoldás, azt posztolni fogom.
5

Működni fog

janoszen · 2012. Már. 28. (Sze), 18.40
Működni fog az, csak nem jól. Időnként majd összeborul, lassú lesz, stb.
2

Megértés

Poetro · 2012. Már. 28. (Sze), 14.04
A WebSocket szervernek folyamatosan futnia kell a szerveren, es ahhoz a klienseknek csatlakozni kell. Így épül fel a $sockets tömböd. Egy másik szkriptben nem férhetsz hozzá ehhez a tömbhöz, mivel az egy másik kódban fut.
Azaz csak abban a kódban tudsz a klienseknek üzenetet küldeni, amiben már csatlakoztak a szerveredhez. Ha egy másik HTTP kérésről van szó, az nem jó, mert ott nem férsz hozzá a már futó WebSocket alkalmazásodhoz, hacsak nem csatlakozol te is hozzá előbb PHP segítségével, és küldesz üzenetet.
$sockets = array($master);  
foreach($sockets as $socket){  
  echo "socket";  
}
Itt nem tudom, miért lep meg, hogy csak egyszer írja ki, hogy socket. Te hoztad létre az egy elemű tömböt. Ahogy láttad a példakódban, a connect kódja bővíti ki a $sockets tömböt az új elemmel.
4

köszi

lacy · 2012. Már. 28. (Sze), 17.16
Köszi a választ. Valószínűleg ott lesz a trükk - ahogy Te is mondtad -, hogy előbb nekem is csatlakoznom kell hozzá PHP-val és utána már elérem kódból a többi klienst is. Kipróbálom, elvileg ez a megvalósítás tud ilyet:
https://github.com/lemmingzshadow/php-websocket