Przeglądaj źródła

Fix socket listen handling

Nathaniel van Diepen 7 lat temu
rodzic
commit
900b14d953
1 zmienionych plików z 61 dodań i 23 usunięć
  1. 61 23
      Net/socket.class.php

+ 61 - 23
Net/socket.class.php

@@ -84,33 +84,57 @@
 			$this->fire('open', $this);
 			return $this;
 		}
-		public function handle(){
+		public function handle(int $length = 2048, bool $binary = false){
 			if(!$this->open){
 				$this->open();
 			}
+			socket_set_nonblock($this->socket);
 			if($this->socket_type == static::SERVER){
-				socket_set_nonblock($this->socket);
+				$regex = $binary ? '/\0/' : '/[\n\r]/';
+				$type = $binary ? PHP_BINARY_READ : PHP_NORMAL_READ;
 				do{
-					$client = socket_accept($this->socket);
-					if($client === false){
-						$this->open = $this->error !== 'A non-blocking socket operation could not be completed immediately.';
-					}elseif(!is_null($client)){
-						socket_set_nonblock($client);
-						$this->client = $client;
-						$this->fire('connect', $this);
-						while($this->open && $this->read() !== false){};
-						socket_set_block($client);
-						socket_close($client);
-						$this->client = null;
-						$this->fire('disconnect', $this);
+					$socket = socket_accept($this->socket);
+					if($socket === false){
+						usleep(100);
+					}elseif($socket  > 0){
+						socket_set_nonblock($socket);
+						$client = [$socket, ''];
+						$this->clients[] = $client;
+						$this->fire('connect', $this, $client);
+					}else{
+						$error = trim(socket_strerror($socket));
+						if($error !== 'A non-blocking socket operation could not be completed immediately.'){
+							$this->fire('error', $this, new \Exception($error));
+							$this->open = false;
+						}
+					}
+					$this->fire('tick', $this);
+					if($this->open){
+						foreach($this->clients as $client){
+							$data = socket_read($client[0], $length, $type);
+							if($data === false){
+								$this->drop($client);
+							}else{
+								if(strlen($data) > 0 && $this->fire('data', $this, $data, $client) !== false){
+									$client[1] .= $data;
+								}
+								$pos = preg_match($regex, $data, $matches, PREG_OFFSET_CAPTURE) == 1 ? $matches[0][1] : false;
+								if($pos === strlen($data) - 1){
+									$this->fire('read', $this, substr($client[1], 0, -1), $client);
+									$client[1] = '';
+								}
+							}
+							$this->fire('tick', $this);
+							if($this->open === false){
+								break;
+							}
+						}
 					}
 				}while($this->open);
-				socket_set_block($this->socket);
 			}elseif($this->socket_type == static::CLIENT){
-				socket_set_nonblock($this->client);
-				while($this->open && $this->read() !== false){};
-				socket_set_block($this->client);
+				while($this->open && $this->read($length, $binary) !== false){};
 			}
+			socket_set_block($this->socket);
 			$this->close(true);
 			return $this;
 		}
@@ -129,31 +153,45 @@
 				return false;
 			}
 			$regex = $binary ? '/\0/' : '/[\n\r]/';
+			$type = $binary ? PHP_BINARY_READ : PHP_NORMAL_READ;
 			$line = '';
 			do{
-				$data = socket_read($this->socket, $length, $binary ? PHP_BINARY_READ : PHP_NORMAL_READ);
+				$data = socket_read($this->socket, $length, $type);
 				if($data === false){
 					$this->open = false;
 				}elseif(strlen($data) > 0 && $this->fire('data', $this, $data) !== false){
 					$line .= $data;
 				}
 				$pos = preg_match($regex, $data, $matches, PREG_OFFSET_CAPTURE) == 1 ? $matches[0][1] : false;
-				$this->fire('tick', $this);
 			}while($pos !== strlen($data) - 1 && $this->open !== false);
 			$line = substr($line, 0, -1);
 			$this->fire('read', $this, $line);
 			return $line;
 		}
+		public function client_write(array $client, $data, bool $binary = false){
+			if(!$this->open){
+				throw new \Exception("You can't write to a socket that isn't open");
+			}
+			$stop = $binary ? "\0" : "\n";
+			if($this->fire('write', $this, $data) !== false){
+				$this->throw_if(socket_write($client[0], $data.$stop));
+			}
+			return $this;
+		}
 		public function close(bool $event = false){
-			$this->drop();
+			foreach($this->clients as $client){
+				$this->drop($client);
+			}
 			socket_close($this->socket);
 			$this->open = false;
 			$this->fire('close', $this);
 			return $this;
 		}
 		public function drop($client){
-			if(!is_null($client)){
-				socket_close($client);
+			socket_close($client[0]);
+			if(in_array($client, $this->clients)){
+				array_splice($this->clients, array_search($client, $this->clients), 1);
+				$this->fire('disconnect', $this, $client);
 			}
 		}
 	}