bind = $bind; $this->port = $port; $this->type = $type; $this->createSocket(); if (socket_bind($this->server,$this->bind,$this->port) === FALSE) throw new SocketException(SocketException::CANT_BIND_SOCKET,socket_strerror(socket_last_error($this->server))); } public function __get($key) { switch ($key) { case 'handler': return $this->handler; default: throw new \Exception('Unknown key: '.$key); } } public function __set($key,$value) { switch ($key) { case 'handler': return $this->handler = $value; default: throw new \Exception('Unknown key: '.$key); } } /** * Create our Socket * * @throws SocketException */ private function createSocket(): void { /** * Check dependencies */ if (! extension_loaded('sockets')) throw new SocketException(SocketException::CANT_ACCEPT,'Missing sockets extension'); if (! extension_loaded('pcntl')) throw new SocketException(SocketException::CANT_ACCEPT,'Missing pcntl extension'); switch ($this->type) { case SOCK_STREAM: $this->server = socket_create(AF_INET|AF_INET6,$this->type,SOL_TCP); break; case SOCK_DGRAM: $this->server = socket_create(AF_INET|AF_INET6,$this->type,SOL_UDP); break; default: throw new \Exception('Unknown socket_type:'.$this->type); } if ($this->server === FALSE) throw new SocketException(SocketException::CANT_CREATE_SOCKET,socket_strerror(socket_last_error())); socket_set_option($this->server,SOL_SOCKET,SO_REUSEADDR,1); } /** * Our main loop where we listen for connections * * @throws SocketException */ public function listen() { if (! $this->handler) throw new SocketException(SocketException::CANT_LISTEN,'Handler not set.'); if (in_array($this->type,[SOCK_STREAM,SOCK_SEQPACKET])) if (socket_listen($this->server,$this->backlog) === FALSE) throw new SocketException(SocketException::CANT_LISTEN,socket_strerror(socket_last_error($this->server))); Log::info(sprintf('%s:- Listening on [%s:%d]',self::LOGKEY,$this->bind,$this->port)); switch ($this->type) { case SOCK_STREAM: $this->loop_tcp(); break; case SOCK_DGRAM: $this->loop_udp(); break; } socket_close($this->server); Log::info(sprintf('%s:= Closed [%s:%d]',self::LOGKEY,$this->bind,$this->port)); } /** * Manage and execute incoming connections * * @throws SocketException */ private function loop_tcp(): void { while (TRUE) { if (($accept = socket_accept($this->server)) === FALSE) throw new SocketException(SocketException::CANT_ACCEPT,socket_strerror(socket_last_error($this->server))); try { $r = new SocketClient($accept); } catch (HAproxyException $e) { Log::notice(sprintf('%s:! HAPROXY Exception [%s]',self::LOGKEY,$e->getMessage())); socket_close($accept); continue; } catch (\Exception $e) { Log::notice(sprintf('%s:! Creating Socket client failed? [%s]',self::LOGKEY,$e->getMessage())); socket_close($accept); continue; } // If the handler returns a value, then that is the main thread if (! $this->handler[0]->{$this->handler[1]}($r)) { $r->close(); exit(0); } } } private function loop_udp(): void { while (TRUE) { $r = new SocketClient($this->server); if ($r->hasData(30)) { if (! ($this->handler[0]->{$this->handler[1]}($r))) exit(0); // Sleep so our thread has a chance to pick up the data from our connection usleep(50000); } } } }