PHP socket_write 失败仅在下次调用时返回错误

标签 php sockets tcp arduino iot

我正在尝试通过PHP套接字发送数据,客户端是一个Arduino设备,当我多次发送时它接收数据正常,但是如果我重置客户端(Arduino设备),它会在几秒钟内重新启动,它说它连接到 PHP 套接字,然后当我想通过 socket_send() 再次发送数据时,它会默默失败,PHP socket_send() 不会返回错误第一个实际错误,只有在我第二次尝试(并失败)时,才会返回错误(“发送零字节”)。收到此错误时,我创建另一个 socket_accept() 并成功发送消息。

什么可能导致这种情况?我希望它能够正确检测丢失的连接,以便我可以在需要时重新发送数据。

感觉就像它向旧连接发送数据,并且只有在第二次尝试时才意识到,这可能吗?

可以通过socket_select()解决这个问题吗?我很难理解它的作用。

澄清:如果客户端重新启动并再次连接,则向其发送数据将返回 int,然后返回 falsefalsefalse (除非我取消设置 $accept 并再次执行 socket_accept())。如果客户端保持离线状态,则发送始终返回 intintintint 是发送的字符串的大小。

$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("Could not create socket\n");

// reuse any existing open port to avoid error
socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);

$result = socket_bind($socket, $host, $port) or die("Could not bind to socket\n");

$result = socket_listen($socket) or die("Could not set up socket listener\n");

    
do{

    if(!isset($accept)){
        echo "\nwaiting for clients";
        $accept = @socket_accept($socket) or die("Could not accept incoming connection");
        echo "\nclient connected";
    }
    
    // memcached will return a message here like: "my message\r\n"
    $message_to_send = $memcached->get('my_socket_message');
    
    if($message_to_send!=''){

        echo "\nsending: ".$message_to_send;
    
        $total_data_sent = @socket_send($accept, $message_to_send, strlen($message_to_send), MSG_EOR);
    
        // if data was not send (sent to an old connection ?!)...
        // then clear $accept, so a new connection is accepted
        // and keep the my_socket_message variable, so message is sent again

        if($total_data_sent === false){
            echo "\nSEND FAILED, will retry message: ".$message_to_send;
            unset($accept);
        } else {
            $memcached->delete('my_socket_message');
        }
    
    }
    
} while (true);

最佳答案

我进行了一些尝试,并能够重现该问题。这是我的解决方案(也许不是最好的方法,但有效):

@socket_send($accept, $message_to_send, strlen($message_to_send), MSG_EOR);
$dataSent = @socket_write($accept, '', 0);

    if ($dataSent === false) {
        unset($accept);
        // ...

编辑

我想出了一个更简洁的解决方案。我们只做 socket_send 函数应该做的事情。返回 falseint 接收到的字节数。

在接收端,您只需解码消息 ($data) 并发回 $data['payload']strlen 。客户端也可以验证它是否已收到这样的所有数据。为此,只需将 $data['payload']strlen$data['length'] 进行比较即可。如果你真的很偏执,你也可以实现一些校验和。

服务器代码使用send而不是socket_send:

function send($client, $message) {
    $messageLength = strlen($message);

    $data = [
        'length' => $messageLength,
        'payload' => $message
    ];
    $jsonData = json_encode($data);

    try {
        @socket_send($client, $jsonData, strlen($jsonData), MSG_EOR);
        $response = @socket_read($client, 1024);

        if (intval($response) !== $messageLength) {
            return false;
        }
    } catch (Exception $e) {
        return false;
    }
    
    return $response;
}

尽管这可行,但我认为 TCP 正是这样做的 - 确保已收到数据。也许我们仍然缺少一些东西。

关于PHP socket_write 失败仅在下次调用时返回错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63199965/

相关文章:

python-3.x - 我应该如何与pyaudio直播

tcp - 协议(protocol) header 中的十六进制值

php - bbPress - 论坛不显示主题,而主题显示论坛

php - 如何在 behat 中访问多个类的 div?

php - 使用 php 将表单发送到我的电子邮件。提交表单时遇到 405 不允许的问题

sockets - 使用 boost::asio async_read_some 的高 CPU 和内存消耗

使用原始套接字的 c ping 请求失败

java - ServerSocket 和来自同一 IP 的多个客户端

带有 Jetty 的 Java Web 服务器 - TCP 连接需要很长时间

php - 如何在 MySQL 中存储基于时间的 OTP key