PHP <-> Java Socket 随机空包 [已解决]

Posted

技术标签:

【中文标题】PHP <-> Java Socket 随机空包 [已解决]【英文标题】:PHP <-> Java Socket random empty packet 【发布时间】:2021-10-14 08:46:33 【问题描述】:

我正在开发一个需要 php Web 应用程序通过 TCP 套接字访问 Java 服务器的项目。一切似乎都运行良好,除了一个小细节:如果采取特定步骤(在代码下方列出),总会有一个空包

我不确定原因是什么,这是我第一次让两种不同语言之间的套接字相互通信,也是第一次在 PHP 中使用它们,所以我的代码可能并不理想。

代码如下:

这些是PHP中发送和读取数据包的函数

function sendPacket($message) : void 
    if (!$this->connected && Config::get('debugging')) 
        echo("Cancelled packet send as we are not connected.");
        connectionFailed("There has been an error. Please, contact the administrators.");
    
    socket_write($this->socket, $message."\nend\n") or
        connectionFailed("There has been an error. Please, contact the administrators.");


function readPacket($length = 15) : string 
    if(!$this->connected) 
        if (Config::get('debugging'))
            echo("Cancelled read as we are not connected.");
        connectionFailed("There has been an error. Please, contact the administrators.");
    

    $line = socket_read($this->socket, $length, PHP_NORMAL_READ);
    if (substr($line, -1) === "\r")
        socket_read($this->socket, 1, PHP_BINARY_READ);

    if (str_ends_with($line, "\n"))
        $line = substr($line, 0, strlen($line) - 1);

    return $line;


然后我用它来向服务器发送令牌并检索响应

public function isUserTokenValid(string $userToken) : string 
    $this->sendPacket("user:validate-token:$userToken");
    usleep(500 * 1000); // I've tried to put this here in case it was a delay issue, but it wasn't
    $response = $this->readPacket();
    return $response;

这是我用来读取数据包的 Java 代码

private String readPacket() throws IOException 
    StringBuilder packet = new StringBuilder();
    String line = null;
    while ((line = in.readLine()) != null && !line.equals("end")) // in is a BufferedReader
        packet.append(line);

    return packet.toString();

这是我用来发送数据包的东西

private void sendPacket(String content) throws IOException 
    if ((!isConnected()) || out == null)
        return;

    System.out.println("OUT> " + content); // This is for debugging and proves that a value is being sent.
    out.println(content); // Out is a PrintStream
    out.flush(); // I've tried both flushing and not flushing, it doesn't change much

读取数据包后,我将其拆分然后解析。 在我发送user:validate-token:$userToken 数据包之前,这就是幕后发生的事情:

    PHP 发送一个数据包告诉 Java 服务器进行身份验证并给它一个令牌 Java 服务器检查令牌并告诉 PHP 服务器它是否有效(如果是后者则断开连接) 然后 PHP 将包含用户名和哈希密码的数据包发送到 Java 服务器 Java 服务器检查信息是否有效,如果有效则返回令牌 最后 PHP 发送数据包来验证令牌 如果令牌有效,Java 服务器返回 1,如果无效则返回 200

令牌验证 (5) 之前的每一步都很顺利。

不过,结果的 var_dump 始终显示 string(0) ""

跳过第 3 步和第 4 步会导致一切正常。

但是如果执行步骤 3 和 4 两次读取数据包(因此使用 $response = $this->readPacket() 两次)确实可以正常工作并正确读取数字。 虽然没有执行第 3 步和第 4 步时读取两次会使 Java 服务器挂起。

如果我不够清楚或需要更多代码,请告诉我

【问题讨论】:

我不确定,但我注意到您在 Java 端使用 out.println()。这将在数​​据之后附加一个换行符,这可能会让你绊倒。您确定要在其中包含换行符吗?你能用out.print()试试看问题是否消失? 是的,不幸的是我已经尝试了 out.print() 但是 PHP 服务器似乎不明白数据包已经收到 - 基本上它发送身份验证数据包,不承认它是收到一个数据包并且永远不会发送下一个 编辑:这是因为我使用的是 PHP_NORMAL_READ,它会一直读取到新行 (php.net/manual/en/function.socket-read.php) Java 服务器的操作系统是什么? 你带领我走向正确的道路。我将 PHP_NORMAL_READ 替换为 PHP_BINARY_READ(类似于 Java 的 InputStream#ready),并将 out.println() 替换为 out.print(),现在一切正常 我已经解决了这个问题,但是 PHP 和 Java 都在 PHP 上运行。我还发布了一个答案,描述了我如何解决我的问题 【参考方案1】:

感谢 markspace 的 comment,我也解决了我的问题。

他们建议尝试将out.println() 替换为out.print(),我已经尝试过,但不幸的是,这只会导致PHP 挂起。

但是,更多地阅读 PHP 的 socket_read 的参数,我了解到 PHP_BINARY_READ(这也是默认模式)与 Java 的 InputStream.ready() 相同,基本上是读取直到它发现没有输入(实际上,这正是我所需要的),而 PHP_NORMAL_MODE 会一直读取,直到找到一个换行符(\r\n)。 因此,在 Java 中将 out.println() 替换为 out.print() 并在 PHP 中将 $line = socket_read($this-&gt;socket, $length, PHP_NORMAL_READ); 替换为 $line = socket_read($this-&gt;socket, $length, PHP_BINARY_READ); 完全解决了这个问题。

我也取消了 out.flush() 的注释,但我不确定这是否有任何区别。

再次感谢 markspace,帮助我解决这个问题!

【讨论】:

以上是关于PHP <-> Java Socket 随机空包 [已解决]的主要内容,如果未能解决你的问题,请参考以下文章

PHP Socket服务器搭建和测试

Android Socket学习java与native_socket通信

vb和php 基于socket通信

用socket怎么获得javaWeb返回的json

php socket

html5的websocket和php的socket分别完成客户端与服务器端的通信过程。