【 在 hgoldfish (老鱼) 的大作中提到: 】
: 还不如用 python + gevent,或者 asyncio 标准库。
php也能写event loop的:
<?php
$_channels = array();
$_ev_base = event_base_new();
main(1616);
class Channel {
function handle_request ($server, $post_data) {
var_dump($server); var_dump($post_data);
static $kennel;
$kennel['1st_cat'] = $kennel['1st_cat'] + 1;
$kennel['1000000th_cat'] = 'Tom';
return "Content-type: text/html\r\n\r\n{$kennel['1st_cat']}\r\n";
}
function on_read () {
global $_channels;
$this->req .= socket_read($this->skt, 8192); $req_len = strlen($this->req);
for ($i = 0;;) {
if ($i + 8 > $req_len)
return; // Incomplete header
//if (strncmp($this->req, "tuichuba", 8) == 0)
// return event_base_loopbreak($_ev_base);
$a = unpack('C8', substr($this->req, $i, 8));
if ($a[1] != 1) // Check version ($a is one-based)
return;
$content_len = ($a[5] << 8) | $a[6];
$body_len = $content_len + $a[7];
if ($i + $body_len > $req_len)
return; // Incomplete body
$i += 8 + $body_len;
if ($a[2] == 5 && !$content_len)
break; // Got an empty STDIN
}
$post_data = '';
$server = array();
$i = 0;
$end = false;
while (!$end) {
$a = unpack('C8', substr($this->req, $i, 8));
$content_len = ($a[5] << 8) | $a[6];
$body_len = $content_len + $a[7];
if ($a[2] == 1) { // BEGIN_REQUEST
$this->req_id = substr($this->req, $i + 2, 2);
$this->flags = $this->req[$i + 8 + 2];
if ($this->flags == "\x01")
$this->keep_conn = true;
}
else if ($a[2] == 4 && $content_len) {
for ($j = $i + 8; $j < $i + 8 + $content_len;) {
$name_len = $this->get_len($j);
$val_len = $this->get_len($j);
$server[substr($this->req, $j, $name_len)] = substr($this->req, $j + $name_len, $val_len);
$j += $name_len + $val_len;
}
}
else if ($a[2] == 5) {
if ($content_len)
$post_data .= substr($this->req, $i + 8, $content_len);
else
$end = true;
}
$i += 8 + $body_len;
}
// Usually the client doesn't send next request until we send respond, but doing so is safer.
$this->req = substr($this->req, $i);
$out = $this->handle_request($server, $post_data);
$out_len = strlen($out);
$response = '';
for ($i = 0; $i < $out_len;) {
$chunk = substr($out, $i, 65520);
$content_len = pack('n', $chunk_len = strlen($chunk));
$response .= "\x01\x06" . $this->req_id . $content_len . "\x00\x00" . $out;
$i += $chunk_len;
}
socket_write($this->skt, $response . "\x01\x03" . $this->req_id .
"\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00");
if (!$this->keep_conn)
$this->close();
}
private function get_len(&$j) {
$len = unpack('C', $this->req[$j])[1]; ++$j;
if ($len & 0x80) {
$a = unpack('C3', $this->req[$j]); $j += 3;
$len = ($a[2] << 8) | $a[3];
}
return $len;
}
function close () {
global $_channels;
unset($this->req);
event_free($this->event);
socket_close($this->skt);
unset($_channels[$this->id]); unset($this);
}
function __destruct () {
echo "__destruct\n";
}
function __construct ($id, $listen, $_ev_base) {
$this->id = $id;
$skt = $this->skt = socket_accept($listen);
socket_set_nonblock($skt);
$event = $this->event = event_init($skt, 'cb_read', $this);
$this->req = '';
$this->keep_conn = false;
}
};
function cb_accept($skt, $what, $_ev_base) {
global $_channels;
if ($what & EV_TIMEOUT) {
$pid = getmypid();
$name = "/run/shm/php.die.{$pid}";
if (file_exists($name)) {
unlink($name);
event_base_loopbreak($_ev_base);
}
return;
}
static $id = 0;
$_channels[$id] = new Channel($id, $skt, $_ev_base);
++$id;
}
function cb_read($skt, $what, $that) {
if ($what & EV_TIMEOUT) {
if (socket_last_error($skt))
$that->close();
}
else
$that->on_read();
}
function event_init ($skt, $callback, $arg) {
$event = event_new();
event_set($event, $skt, EV_READ | EV_PERSIST, $callback, $arg);
// The additional flag EV_PERSIST makes the event to persist until event_del() is called, otherwise the callback is invoked only once.
// [warn] event_assign: EV_SIGNAL is not compatible with EV_READ or EV_WRITE
global $_ev_base;
event_base_set($event, $_ev_base); event_add($event, 5000000);
return $event;
}
function main ($port) {
if (!($skt = socket_create_listen($port)))
exit(socket_strerror(socket_last_error($skt)));
global $_ev_base;
$event = event_init($skt, 'cb_accept', $_ev_base);
event_base_loop($_ev_base);
echo "Bye\n";
}
?>
--
FROM 106.121.160.*