public function onAcceptEvent($bindSocket, $events, $arg) { $bindSocketId = $arg[0]; Debug::netEvent(get_class($this) . '::' . __METHOD__ . '(' . $bindSocketId . ') invoked.'); // add to accept next event // why not use EV_PERSIST event_add($this->bindSocketEvPool[$bindSocketId]); $connSocket = stream_socket_accept($this->bindSocketPool[$bindSocketId]); if (!$connSocket) { Debug::netErrorEvent(get_class($this) . ': can not accept new TCP-socket'); return; } stream_set_blocking($connSocket, 0); list($ip, $port) = explode(':', stream_socket_get_name($connSocket, true)); $connId = daemon::getNextConnId(); $evBuf = event_buffer_new($connSocket, array($this, 'onEvBufReadEvent'), array($this, 'onEvBufWriteEvent'), array($this, 'onEventEvent'), array($connId)); event_buffer_base_set($evBuf, daemon::$eventBase); event_buffer_priority_set($evBuf, 10); event_buffer_watermark_set($evBuf, EV_READ, $this->evBufLowMark, $this->evBufHighMark); if (!event_buffer_enable($evBuf, EV_READ | EV_WRITE | EV_PERSIST)) { Debug::netErrorEvent(get_class($this) . '::' . __METHOD__ . ': can not set base of buffer. #' . $connId); //close socket stream_socket_shutdown($connSocket, STREAM_SHUT_RDWR); fclose($connSocket); return; } // 调试这里时,浪费了很多时间,必须注意的是,以上 event 所用的变量如果在函数中,如果没有交给其他的变量引用,在函数结束时就会销毁, // 造成连接直接断或者bufferevent 不能触发。晕啊晕。 $this->connSocketPool[$connId] = $connSocket; $this->connEvBufPool[$connId] = $evBuf; $this->updateLastContact($connId); $this->onAccepted($connId, $ip, $port); }
public function connectTo($host, $port, $blockConnect = false) { Debug::netEvent(get_class($this) . '::' . __METHOD__ . '(' . $host . ':' . $port . ') invoked.'); $connSocket = stream_socket_client("tcp://{$host}:{$port}", $errno, $errstr, 30); //add err connect stream_set_blocking($connSocket, 0); if (!is_resource($connSocket)) { Debug::netErrorEvent(get_class($this) . ': can not add errorneus socket with address \'' . $host . ':' . $port . '\'.'); return false; } $connId = daemon::getNextSocketId(); //@todo 增加阻塞 connect , 这样可以返回 connId 的时候 socketSession 已经建立好 if ($blockConnect) { } $ev = event_new(); // why not use EV_PERSIST, is because first event is acceptEvent? if (!event_set($ev, $connSocket, EV_WRITE, array($this, 'onConnectedEvent'), array($connId))) { Debug::netErrorEvent(get_class($this) . '::' . __METHOD__ . ': can not set onAcceptEvent() on binded socket: ' . Debug::dump($connSocket)); return false; } event_base_set($ev, daemon::$eventBase); event_add($ev, 1 * 1000 * 1000); $this->checkConnSocketPool[$connId] = $connSocket; $this->checkConnEvPool[$connId] = $ev; return $connId; }
/** * Data decoding, according to related IETF draft * * @see http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10#page-16 */ protected function _dataDecode() { //参考 http://blog.csdn.net/fenglibing/article/details/6852497 //这里仅仅处理最基本的 $encodedData =& $this->webSocketSession->buf; // 至少有2个字节的头 while (($buflen = strlen($encodedData)) >= 2) { $len = 0; $isMasked = (bool) (ord($encodedData[1]) >> 7); $opcode = ord($encodedData[0]) & 15; $dataLength = ord($encodedData[1]) & 127; $len += 2; if ($dataLength === 126) { $extDataLength = hexdec(sprintf('%02x%02x', ord($encodedData[2]), ord($encodedData[3]))); $len += 2; } else { if ($dataLength === 127) { $extDataLength = hexdec(sprintf('%02x%02x%02x%02x%02x%02x%02x%02x', ord($encodedData[2]), ord($encodedData[3]), ord($encodedData[4]), ord($encodedData[5]), ord($encodedData[6]), ord($encodedData[7]), ord($encodedData[8]), ord($encodedData[9]))); $len += 8; } else { $extDataLength = $dataLength; } } if (webSocketSession::maxPacketSize <= $extDataLength) { // Too big packet $this->webSocketSession->close(); return; } if ($isMasked) { $maskingKey = Utils::binarySubstr($encodedData, $len, 4); $len += 4; } if ($extDataLength + $len > strlen($encodedData)) { //没有出现包 // not enough data yet return; } $data = Utils::binarySubstr($encodedData, $len, $extDataLength); //这里用的引用,所以会处理掉 socketSession->buf $encodedData = Utils::binarySubstr($encodedData, $len + $extDataLength); if ($opcode === self::CLOSE) { //客户端主动关闭连接 $this->webSocketSession->close(); return; } if ($opcode === self::PING) { $this->sendFrame('', 'PONG'); continue; } if ($opcode === self::PONG) { //todo: 收到 pong 包,不做任何事情,以后应该更新最后的连接时间 daemon::log(get_class($this) . '::' . __METHOD__ . ' : receive PONG packet. ' . $this->webSocketSession->addr); continue; } if ($isMasked) { $unmaskingFunc = function ($data, $mask) { for ($i = 0, $l = strlen($data); $i < $l; $i++) { // Avoid storing a new copy of $data... $data[$i] = $data[$i] ^ $mask[$i % 4]; } return $data; }; $this->webSocketSession->onFrame($unmaskingFunc($data, $maskingKey), $opcode); } else { $this->webSocketSession->onFrame($data, $opcode); } } }
<?php require_once 'stdin.php'; require_once 'noSQLite.php'; $stdin = new stdin(); $stdin->service = 'noSQLi'; $daemon = new daemon($stdin); $daemon->setService($stdin->service); $daemon->run(); class listener { public $socket; public $method; public $arguments = array(); public $meta = array(); public $header; const HEADER_LENGTH = 2; const ARGUMENT_HEADER_LENGTH = 4; const RESPONSE_LENGTH = 4; const SIGNAL_ACK = 'ACK'; const SIGNAL_ERR = 'ERROR'; public function __construct($socket) { $this->socket = $socket; } public function metaUnpackString($arguments) { $unpackString = 'Lmethod/'; for ($i = 0, $c = $arguments; $i < $c; $i++) { $unpackString .= 'Largument' . ($i + 1) . '/'; }