start at 2025-05-21T18:36:37+02:00, pid 203148, host lx13.hoststar.hosting, php 8.3.21
socket uri = ?

Socket: Sever mit php

REQUEST_URI 8 GET /inf/php/f01Socket.php get Array ( ) post Array ( )

Socket: Client mit javascript

Output

Source /home/ch45859/web/wlkl.ch/public_html/inf/php/f01Socket.php

<html>
 <head>
  <title><?php $cnt=7; echo basename(__file__, '.php'); ?></title>
  <script type="text/javascript">
<?php
    $hostnm = gethostname();
    $prot = $hostnm === 'wk13' ? 'ws' : 'wss';
    $host = $hostnm === 'wk13' ? "192.168.1.135" : 'wlkl.ch';
    $port = 4321;
    echo "        const sockUri = '$prot://$host:$port';";
?>
        const outLi = [];
        var cSock = null;
        var cnt = 0;
        function out(t) {
            outLi.unshift(t);
            while (outLi.length > 10)
                outLi.pop();
            var o = document.getElementById('out');
            o.innerHTML = outLi.reduce((a, c) => a + '<li>' + c + '</li>', '');
        }
        function cOpen() {
            if (cSock !== null)
                return out('client open, but already open');
            try {
            cSock = new WebSocket(sockUri);
            cSock.onmessage = (event) => out("received: " + event.data);
            cSock.onerror = (event) => cOut(event);
            out('client open setup');
            } catch (e) {out('catch opening ' + e)}
        }
    function cOut(event) {
        var m = "sock error: " + event.constructor.name + ' ' + event.type + ':';
        for (const p in event) 
            m += ', ' + p + '=' + event[p];
        out(m + ' * ' + event);
        }
        function cClose() {
            if (cSock === null)
                return out('client close, but already closed');
            const c = cSock;
            cSock = null;
            c.close();
            out('client closed');
        }

        function cSend() {
            /* out('readyState ' + cSock.readyState); */
            if (cSock === null)
                return out('client send, but socket notopen');
            const t = document.getElementById('cTxt').value;
            const m = 'client send ' + t + " #" + ++cnt + " at " + Date().toString();
            cSock.send(m);
            out(m);
        }
  </script>
</head>
<body>
<?php echo 'start at ' . date('c') . ', pid ' . getmypid() . ", host $hostnm, php " . phpversion(); ?>
<br>socket uri = <b id="uri">?</b>
<script type="text/javascript">
document.getElementById('uri').innerHTML = sockUri; 
</script>
<h1>Socket: Sever mit php</h1>
<form action="f01Socket.php" method="post">
<input type ="submit" name="server" value="start"/> 
<input type ="submit" name="server" value="ps"/> 
REQUEST_URI <?php echo ++$cnt . ' ' . $_SERVER["REQUEST_METHOD"]. ' ' . $_SERVER["REQUEST_URI"] . " get " . print_r($_GET, true) . " post " . print_r($_POST, true); ?>
<br>
<?php
if (isset($_POST['server'])) {
    echo "<br> server " . $f = $_POST['server'];
    if ($f === 'start') {  
        echo "<br>starting server";
        $r = exec('./f01SocketServer.php >> f01SocketServer.out 2>&1 & echo shell $0 soSe $? jobPid $!', $o, $c); # ; disown $!; echo disown $! rc $?
        # $r = exec('ls', $o, $c);
        echo "<br>exec code=$c, r=$r, output=" . print_r($o, true);
    } else if ($f === 'ps') {  
        $ps = [];
        exec('ps', $ps, $c); 
        echo "<br>--- ps code=$c<br>" . implode('<br>', $ps);
        $r = exec('echo pwd $(pwd) <br>--- tail f01SocketServer.out; tail -15 f01SocketServer.out;', $o, $c); # ; disown $!; echo disown $! rc $?
        echo "<br>code=$c<br>" . implode('<br>', $o);
        echo '<br>match ' . preg_match_all('/^\s*(\S*)\s.*\s(f01SocketSer.*)/m', implode("\n", $ps), $mm) . ', mm = ' . print_r($mm, true);
        for ($i=0; $i<count($mm[1]); $i++) {
            $o=[];
            echo "<br> kill {$mm[1][$i]} {$mm[2][$i]}: " . exec("kill {$mm[1][$i]}; echo kill code $?", $o, $c) . "code=$c, output=" . implode('<br>', $o);
        }
    } else {
        echo "<br>errot not implemented $f";
    }
}
?>
</form>
<h1>Socket: Client mit javascript</h1>
<input type="button" value="open" onclick="cOpen();" />
<input type="button" value="close" onclick="cClose();" />
<input type="button" value="send" onclick="cSend();" />
<input type="text" id="cTxt" maxlength="800" size="30" />

<h1>Output</h1> 
<ul id="out">
</ul>
<h1>Source <?php echo __file__; ?> </h1>
<?php $_POST = []; highlight_file(__file__) ?>
<h1>Source f01SocketServer.php;</h1>
<?php $_POST = []; highlight_file('f01SocketServer.php') ?>
 </body>
</html>

Source f01SocketServer.php;

<?php

require_once('env.php');
outBegin(__file__);
$dbg=9;
$hostnm = gethostname();
out('start at ' . date('c') . ', pid ' . getmypid() . ", host $hostnm, php " . phpversion());
$prot = $hostnm === 'wk13' ? 'ws' : 'wss' . err("security policy for websocket ports on host $hostnm?????") ;
$host = $hostnm === 'wk13' ? "192.168.1.135" : 'wlkl.ch';
$port = 4321;

function sErr($t) {
    err($t . ' ' . ($c = socket_last_error()) . ' => ' . socket_strerror($c));
}

function sSend($s, $d) { # send data to socket s
    if (false === $r = socket_send($s, $d, strlen($d), 0))
        sErr("socket_send $d");
    else
        out("senttt $r bytes data", preg_replace('/[[:^print:]]/', '?', $d));
}

function wsDecode($e, &$t) { # decode  websocket message e into clear text t, return opcode, according to rfc6455 https://www.rfc-editor.org/rfc/rfc6455.html
    ($fin = ord($e[0]) >> 7) || err("implement continuation wsFin $fin");
    $op = ord($e[0]) & 0x0f;
    if(0x7e > $len = ord($e[1]) & 0x7f) {
        $o = 2;
    } elseif ($len === 0x7e) { # 16bit 
        $o = 4;
        $len = unpack('n', $e, 2)[1];
    } else { # $len === 0x7f 64bit
        $o = 10;
        $len = unpack('J', $e, 2)[1];
    }
    if (! ($isMskd = ord($e[1]) >> 7)) {
        $t = $s = substr($e, 2);
    } else {
        (strlen($e) >= $o+4) || err("wsDecode to short for mask " . bin2hex($e));
        $msk = substr($e, $o, 4);
        $s = substr($e, $o+4);
    }
    if ($len != strlen($s))
        err("strlen " . strlen($s) . " <> len $len");
    if ($isMskd) { # decode with mask
        $t = $s ^ str_repeat($msk, (3+strlen($s)) >> 2);
    }
    dbg1( "wsDecode: fin $fin, opcode $op" 
        . ($isMskd ? (", mask " . bin2hex($msk)) : ', not masked') . ", len $len"
        . " decoded $t from " . bin2hex(substr($e, 0, 20))); 
    return $op;
}

function wsEncode($op, $t) { # encode text t with opCode op  and return encoded data, according to rfc6455 https://www.rfc-editor.org/rfc/rfc6455.html
    (($op & ~ 0xf) === 0) || err("encode bad op $op");
    $r = chr(0x80 | $op);
    $r .= (($l = strlen($t)) < 0x7e) ? chr($l) : ($l <= 0xffff ? "\x7e" . pack('n', $l) : "\x7f" . pack('J', $l));
    return $r . $t;
}

function wsSend($c, $m) { # send in websocket protocoll
    sSend($c, wsEncode(1, $m));
}

function sSendHTTP($s, $ii, $m) {
    out($m = "<html><body>$m <ul><li>$ii</li><li>at " .  date('c') . "</ul></li>");
    /* $hm = "HTTP/1.1 200 OK\nDate: Mon, 27 Jul 2009 12:28:53 GMT\nServer: Apache/2.2.14 (Win32)\nLast-Modified: Wed, 22 Jul 2009 19:15:56 GMT"
            . "\nContent-Length: " . strlen($m) ."\nContent-Type: text/html\nConnection: Closed\n\n$m"; */
    sSend($s, "HTTP/1.1 200 OK\nContent-Length: " . strlen($m) ."\nContent-Type: text/html\nConnection: keep-alive\n\n$m");
}


dbg1("Creating master socket...");
if (false === $sockSrv = socket_create(AF_INET, SOCK_STREAM, SOL_TCP))
    sErr('socket_create');
$max_clients = 10;

dbg1("Setting socket options...");
socket_set_option($sockSrv, SOL_SOCKET, SO_REUSEADDR, 1) || sErr('socket_set_option');
dbg1("Binding socket host=$host, port=$port...");
socket_bind($sockSrv, $host, $port) || sErr('socket_bind');
dbg1("Listening...");
socket_listen($sockSrv, $max_clients) || sErr('socket_listen');

$sckts = [$sockSrv];
$sckII = ['sockSrv'];
dbg1('sckts', count($sckts), $sckts);
$nn = null;
while(TRUE) {
    dbg1("while ... select sckts " . count($sckts), $sckts);
    $reaS = $sckts;
    (false !== $ready = socket_select($reaS, $nn, $nn, null)) || sErr("socket_select ready=$ready");
    dbg1("socket_select returned " . $ready);
    foreach ($reaS as $k => $s) {
        if ($s !== ($sckts[$k] ?? '?none?')) {
            err("key changed $k => $s but sockets", $sckts);
            $kO = $k;
            (false === ($k = $array_search($s, $sckts, true))) && err('selected socket', $s, 'not in', $sckts);
        }
        if ($s === $sockSrv) { 
            (false !== $c = socket_accept($sockSrv)) || sErr('socket_accept');
            (false === (array_search($c, $sckts, true))) || err("accept", $c, 'already in sckts', $sckts);
            $sckts[$nx=count($sckts)] = $c;
            socket_getsockname($c, $cSH, $cSP) || sErr('socket_getsockname');
            socket_getpeername($c, $cPH, $cPP) || sErr('socket_getpeername');
            $sckII[$nx] = "socket from server $cSH:$cSP to peer $cPH:$cPP";
            if ($prot === 'http') {
                sSendHTTP($s, $sckII[$nx], 'accept connect');
            } elseif ($prot === 'ws') {
                (false === $handsh = socket_read($c, 5000)) && sErr('socket_read handshake');
                dbg1("handshake read $handsh");
                dbg1('match', preg_match('#Upgrade: +([^\r\n]*)#', $handsh, $matches), $matches);
                dbg1('match', preg_match('#Sec-WebSocket-Key: +([^\r\n]*)#', $handsh, $matches), $matches);
                dbg1("handshake sec key ". bin2hex($matches[1]) ." <{$matches[1]}>");
                $sah1 = sha1($matches[1] . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
                $rKey = base64_encode(pack('H*', $sah1));
                # sSend($c, "HTTP/1.1 101 Switching Protocols\nUpgrade: websocket\nConnection: Upgrade\nSec-WebSocket-Accept: $rKey\n\n");
                sSend($c, "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
                            "Upgrade: websocket\r\n" .
                            "Connection: Upgrade\r\n" .
                            "WebSocket-Origin: $host\r\n" .
                            "WebSocket-Location: ws://$host:$port\r\n".  # /deamon.php
                            "Sec-WebSocket-Accept:$rKey\r\n\r\n");
                wsSend($c, "ws connected 1 to {$sckII[$nx]}");    
            } else err("prot $prot");
        } else {
            $buf = ' ';
            socket_clear_error();
            if (false === $buf = @socket_read($s, 2048)) {
                if (SOCKET_ENOTCONN === $e = socket_last_error()) {
                    dbg1('recv buf', $buf);
                    out("endpoint not connected $sckII[$k] ===>" . ' => ' . socket_strerror($e));
                    socket_close($s);
                    $sckts[$k] = $sckts[count($sckts) -1];
                    $sckII[$k] = $sckII[count($sckts) -1];
                    array_pop($sckts);
                    array_pop($sckII);
                } else {
                    sErr('socket_recv');
                }
            } else {
                out("server received from client " . strlen($buf) . ' ' . bin2hex(substr($buf, 0, 10)), preg_replace('/[[:^print:]]/', '', $buf));
                $op = wsDecode($buf, $txt);
                if ($op === 1) { # text message
                    wsSend($s, "server received text from client $sckII[$k]: $txt"); 
                } elseif ($op === 8) { # close handshake
                    sSend($s, "\x88\x00"); # send back close handshake
                    socket_close($s);
                    dbg1("closed $sckII[$k] after closeHandshake");
                    $sckts[$k] = $sckts[count($sckts) -1];
                    $sckII[$k] = $sckII[count($sckts) -1];
                    array_pop($sckts);
                    array_pop($sckII);
               } else {
                    err("implement websocket opcode $op");
                }
            }
        }
    } #
    
    

} #while
outEnd(__file__);