我一直在根据分散在互联网上的稀缺信息制作自己的websocket服务器。这是我得到的:
public function __construct() {
if (($this->Socket = stream_socket_server("tcp://192.168.0.14:12369", $errno, $errstr)) === false) {
echo "Creating socket server failed: " . socket_strerror(socket_last_error()) . "rn";
exit;
}
else {
echo "Socket server created on " . date("j F Y g:i:s") . "rn";
echo "Socket Resource is ".$this->Socket."rn";
}
$this->Sockets[] = $this->Socket;
}
public function Run() {
while (true) {
$ReadFromSockets = array();
foreach($this->Sockets as $_S) {
$ReadFromSockets[] = $_S;
}
foreach($this->Children as $_C) {
$ReadFromSockets[] = $_C;
}
stream_select($ReadFromSockets, $this->SocketsWrite, $this->SocketsExcept, null);
foreach($ReadFromSockets as $_Socket) {
if($_Socket === $this->Socket) {
if (($SocketResource = stream_socket_accept($_Socket)) === FALSE) {
echo "socket_accept() failed: reason: " . socket_strerror(socket_last_error($_Socket)) . "rn";
}
$this->Sockets[] = $SocketResource;
$this->SocketWrite = $this->Sockets;
$this->DoHandshake($SocketResource);
$pair = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);
$PID = pcntl_fork();
if($PID == -1) {
echo "There was an error forking the process";
} elseif($PID) {
fclose($pair[0]);
$this->Children[] = $pair[1];
} else {
fclose($pair[1]);
$this->Parent = $pair[0];
$this->Children = array();
unset($this->Sockets[array_search($this->Socket, $this->Sockets)]);
break;
}
} else {
$this->Listening($SocketResource);
}
}
}
socket_close($this->Socket);
}
private function Listening($SocketResource) {
$data = fread($SocketResource, 100000);
$data = $this->Unmask($data);
$data = $this->Mask($data);
if($this->Parent) {
fwrite($this->Parent, $data);
} else {
foreach($this->Sockets as $_S) {
fwrite($_S, $data);
}
}
}
我没有列出DoHandshake
、Mask
和Unmask
功能——它们都工作正常,仅用于握手。
列出的代码背后的想法是用户连接到套接字,进程分叉,主进程获取子进程和副 verca 的Resource #
。当用户将一些数据发送到(分叉(套接字时,它应该将数据发送到父进程。父进程应循环访问保存在数组中的所有客户端的列表$this->Sockets
并将消息发送给所有客户端。但它不起作用,我似乎真的不明白为什么。
答案隐藏在对用户连接到套接字的过程以及如何收听他们发送的数据的错误理解中。
不过,那 === 并且是错误的 === 用户连接,获取套接字进程的副本,然后通过主进程的副本与套接字服务器通信。在我的脑海中,它看起来像这样:
client -->> child process -->> parent process
^ |
^ |
| ⇩
|----------------- doing something with data
其中箭头表示正在发送的数据。
现在,正确的解决方案变得更加简单。我们必须有$this->Sockets
数组,但不包括 $this->Socket
,这是主套接字服务器。如果我们这样做,在每次通话中我们都会收到错误。尽管如此,我们还是将$this->Socket
添加到显式数组中,仅用于侦听。
在$this->Sockets
我们持有所有客户端的数组,仅此而已。而且我们不需要分叉流程,因为当客户端发送内容时,遍历$this->Sockets
就足够了。
这是最终代码:
public function __construct() {
if (($this->Socket = stream_socket_server("tcp://192.168.1.14:12369", $errno, $errstr)) === false) {
echo "Creating socket server failed: " . $errno . $errstr . "rn";
exit;
}
}
public function Run() {
while (true) {
$ReadFromSockets = $this->Sockets;
$ReadFromSockets[] = $this->Socket;
$null = null;
stream_select($ReadFromSockets, $null, $null, null);
foreach($ReadFromSockets as $_Socket) {
if($_Socket === $this->Socket) {
if (($_Socket = stream_socket_accept($_Socket,-1)) === FALSE) {
echo "failedrn";
}
$this->Sockets[] = $_Socket;
$this->DoHandshake($_Socket);
} else {
$this->Listening($_Socket);
}
}
}
socket_close($this->Socket);
}
private function Listening($SocketResource) {
$data = fread($SocketResource, 100000);
$data = $this->Unmask($data);
$data = $this->Mask($data);
// if we do not make Unmask/Mask process,
// we'll catch a forced disconnect by the client
foreach($this->Sockets as $_S) {
fwrite($_S, $data);
}
}
希望这有助于理解 websocket 背后的逻辑。最好知道它是如何工作的,而不是盲目地使用现成的解决方案,对吧,OIS?;)