我在socket_recv
接收客户端发送到服务器的完整数据时遇到了一些问题,接收数据的代码如下:
while(true) {
$changedSockets = $this->sockets;
$result = socket_select($changedSockets,$write,$except,0);
foreach($changedSockets as $socket){
$buffer = '';
$message = '';
$bytes = @socket_recv($socket, $buffer, 4096, 0);
echo "Lenght: (".$bytes.length.")";
if ($bytes === false) {
$error = self::getLastError( $this->socket );
trigger_error( 'Failed to receive data from client #'.$client->id.': '.$error->message.' ['.$error->code.']', E_USER_WARNING );
continue;
}
$len = ord( $buffer[1] ) & 127;
$masks = null;
$data = null;
if ( $len === 126 ) {
$masks = substr( $buffer, 4, 4 );
$data = substr( $buffer, 8 );
}
elseif ( $len === 127 ) {
$masks = substr( $buffer, 10, 4 );
$data = substr( $buffer, 14 );
}
else {
$masks = substr( $buffer, 2, 4 );
$data = substr( $buffer, 6 );
}
for( $index = 0; $index < strlen( $data ); $index++ ) {
$message .= $data[$index] ^ $masks[$index % 4];
}
if ( $bytes == 0 ) {
$this->disconnectClient( $socket );
}
}
}
}
这个代码运行得很好,但有一个问题:如果我发送的文本超过5800个字符,我的代码就不会收到全文(只有一小部分)。
为了了解发生了什么,我只插入下面的代码,看看有多少字节:
$bytes = @socket_recv($socket, $buffer, 4096, 0);
echo "Lenght: (".$bytes.length.")";
我注意到,当您发送小于5800个字符的文本时,字节的大小与客户端发送的字节大小相同,现在,当文本超过5800个字时,socket_recv
的长度保持在1448个字符(如果我将值4096更改为更高的值,则会得到更多字符,但如果我打开php,则会产生内存问题)
为了解决这个问题,我尝试了很多方法,比如使用:MSG_DONTWAIT
、MSG_WAITALL
、while($bytes > 0){ ... }
。但它们都进入了一个无限循环,使得代码的其余部分(socket_recv下面)不再运行。
我该如何解决这个问题?(我正在本地主机的wampserver中进行测试)
可能的解决方案
当我绞尽脑汁解决这个问题时,我想到了一个临时的解决方案:
如果我们不能发送大于5800个字符的文本,我可以尝试在客户端制作一个代码,将字符切成小于5800的小块字符,并使用参数,表示该条目被截断,并且在套接字I可以尝试过滤它们并加入字符。
在那段时间里我实现了许多测试,我实现了一些事情。
接收的消息经过加密
当消息在服务器上接收时,它们在标准RFC 6455中被加密,如果您在变量$buffer
中运行echo
,您将看到以下内容:×RÄeÐÀlÓ[Ç)
、Ó[ÁhÒß
。。。示例:
对于字符串
stackoverflow
,你会看到iuj87xz
,如果你再次插入该字符串,你会发现一个不同的哈希,但它们遵循加密标准,例如:对于字符串stack stack
,我会为两者使用相同的哈希(rrtfgh
和rrtfgh
),但如果我再次发送到套接字,我会看到一个与以前不同的散列,但两者的散列都相同(mmjsht
和mmjsht
)。
消息以块形式接收
当我在屏幕上显示$buffer
并发送一条超过10000个字符的文本时,我看到foreach($changedSockets as $socket)
It一直被调用,直到消息的所有部分都被传递为止。
如果你看到我的代码,你会看到每一条新消息都被解密了。但我遇到了一个问题,在解密的第一个块消息中,我可以清楚地读取它(读取我发送到套接字的文本),但其他块不会发生这种情况。
分析变量$buffer
中包含的哈希,我可以注意到第一个块中有一些不同,例如:让我们将此文本发送到服务器:Hello Hello Hello....(repeated 10000X)
区块1:
þ@¨¿7ÀlÓ[Ç)×RÄeÐÀlÓ[Ç)×RÄeÐÀlÓ[Ç)×RÄeÐÀlÓ[Ç)×RÄeÐÀlÓ[Ç)×RÄeÐÀlÓ[Ç)
区块2:
×RÄeÐÀlÓ[Ç)×RÄeÐÀlÓ[Ç)×RÄeÐÀlÓ[Ç)×RÄeÐÀlÓ[Ç)×RÄeÐÀlÓ
块1解密:
Hello Hello Hello Hello...
块2解密:
LLE LLE LLE LLE LLE...
注意到区别了吗?看起来我在块1中得到了一个其他块中不存在的不同入口代码。它让我想起了转换为base64的图像,它在哈希之前有一个启动代码:data:image/png;base64,......
,并且在没有启动代码的情况下不起作用,因为这不起作用(正如我们在上面看到的)。
可能的解决方案
我可以将消息发送到套接字,消息的长度如下:在服务器端的"16049" Hello Hello Hello....
,我会取这个数量,除以4096
,这是我的Socket_Recv
的长度,这样我就会得到等于4的结果,这是接收完整消息的所有块所需的重复次数。
然后,在收到最后一个块之前,我不会运行代码来解密消息(同时,我在$buffer
中获取哈希并进行连接)。
我还没有测试过这个代码,似乎就是这样,在我完成代码测试后,我会回到这里发布完整的代码。