socket_export_stream 函数的作用是将一个原始的 socket 资源转换成一个可以与标准 PHP 流函数兼容的流资源。它使得底层的 socket 连接能够和像 fread 这样的流读取函数一起工作,从而使得数据的读取更为方便。
<span><span>resource </span><span><span class="hljs-title function_ invoke__">socket_export_stream</span></span><span> ( resource </span><span><span class="hljs-variable">$socket</span></span><span> )
</span></span>
返回值是一个标准的流资源,这样你就可以使用 fread, fwrite 或者其他流操作函数来读取或写入数据。
fread 是用于从文件指针或流中读取指定字节数数据的函数。它常与 fopen、socket_export_stream 等配合使用,用于读取流中的数据。
<span><span><span class="hljs-keyword">string</span></span><span> </span><span><span class="hljs-title function_ invoke__">fread</span></span><span> ( resource </span><span><span class="hljs-variable">$handle</span></span><span> , </span><span><span class="hljs-keyword">int</span></span><span> </span><span><span class="hljs-variable">$length</span></span><span> )
</span></span>
参数:
$handle 是流的资源,可以是 socket_export_stream 返回的流资源。
$length 是要读取的字节数。
fread 会返回读取的内容,如果读取不到数据,它会返回一个空字符串。
我们将创建一个简单的 TCP 客户端与服务器通信的实例。客户端会连接到服务器,通过 socket 发送请求,并使用 fread 从服务器读取响应。
<span><span><span class="hljs-comment">// 服务器端代码</span></span><span>
</span><span><span class="hljs-variable">$host</span></span><span> = </span><span><span class="hljs-string">'127.0.0.1'</span></span><span>;
</span><span><span class="hljs-variable">$port</span></span><span> = </span><span><span class="hljs-number">8080</span></span><span>;
</span><span><span class="hljs-comment">// 创建 socket 资源</span></span><span>
</span><span><span class="hljs-variable">$server</span></span><span> = </span><span><span class="hljs-title function_ invoke__">socket_create</span></span><span>(AF_INET, SOCK_STREAM, SOL_TCP);
</span><span><span class="hljs-title function_ invoke__">socket_bind</span></span><span>(</span><span><span class="hljs-variable">$server</span></span><span>, </span><span><span class="hljs-variable">$host</span></span><span>, </span><span><span class="hljs-variable">$port</span></span><span>);
</span><span><span class="hljs-title function_ invoke__">socket_listen</span></span><span>(</span><span><span class="hljs-variable">$server</span></span><span>);
</span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-string">"服务器已启动,等待客户端连接...\n"</span></span><span>;
</span><span><span class="hljs-comment">// 等待客户端连接</span></span><span>
</span><span><span class="hljs-variable">$client</span></span><span> = </span><span><span class="hljs-title function_ invoke__">socket_accept</span></span><span>(</span><span><span class="hljs-variable">$server</span></span><span>);
</span><span><span class="hljs-comment">// 向客户端发送数据</span></span><span>
</span><span><span class="hljs-variable">$message</span></span><span> = </span><span><span class="hljs-string">"Hello from server!"</span></span><span>;
</span><span><span class="hljs-title function_ invoke__">socket_write</span></span><span>(</span><span><span class="hljs-variable">$client</span></span><span>, </span><span><span class="hljs-variable">$message</span></span><span>, </span><span><span class="hljs-title function_ invoke__">strlen</span></span><span>(</span><span><span class="hljs-variable">$message</span></span><span>));
</span><span><span class="hljs-comment">// 关闭连接</span></span><span>
</span><span><span class="hljs-title function_ invoke__">socket_close</span></span><span>(</span><span><span class="hljs-variable">$client</span></span><span>);
</span><span><span class="hljs-title function_ invoke__">socket_close</span></span><span>(</span><span><span class="hljs-variable">$server</span></span><span>);
</span></span>
<span><span><span class="hljs-comment">// 客户端代码</span></span><span>
</span><span><span class="hljs-variable">$host</span></span><span> = </span><span><span class="hljs-string">'127.0.0.1'</span></span><span>;
</span><span><span class="hljs-variable">$port</span></span><span> = </span><span><span class="hljs-number">8080</span></span><span>;
</span><span><span class="hljs-comment">// 创建 socket 资源</span></span><span>
</span><span><span class="hljs-variable">$socket</span></span><span> = </span><span><span class="hljs-title function_ invoke__">socket_create</span></span><span>(AF_INET, SOCK_STREAM, SOL_TCP);
</span><span><span class="hljs-title function_ invoke__">socket_connect</span></span><span>(</span><span><span class="hljs-variable">$socket</span></span><span>, </span><span><span class="hljs-variable">$host</span></span><span>, </span><span><span class="hljs-variable">$port</span></span><span>);
</span><span><span class="hljs-comment">// 将 socket 资源转换为流资源</span></span><span>
</span><span><span class="hljs-variable">$stream</span></span><span> = </span><span><span class="hljs-title function_ invoke__">socket_export_stream</span></span><span>(</span><span><span class="hljs-variable">$socket</span></span><span>);
</span><span><span class="hljs-comment">// 从流中读取数据</span></span><span>
</span><span><span class="hljs-variable">$response</span></span><span> = </span><span><span class="hljs-title function_ invoke__">fread</span></span><span>(</span><span><span class="hljs-variable">$stream</span></span><span>, </span><span><span class="hljs-number">1024</span></span><span>); </span><span><span class="hljs-comment">// 读取最多 1024 字节数据</span></span><span>
</span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-string">"服务器响应: "</span></span><span> . </span><span><span class="hljs-variable">$response</span></span><span> . </span><span><span class="hljs-string">"\n"</span></span><span>;
</span><span><span class="hljs-comment">// 关闭连接</span></span><span>
</span><span><span class="hljs-title function_ invoke__">fclose</span></span><span>(</span><span><span class="hljs-variable">$stream</span></span><span>);
</span><span><span class="hljs-title function_ invoke__">socket_close</span></span><span>(</span><span><span class="hljs-variable">$socket</span></span><span>);
</span></span>
服务器端:首先,我们创建了一个 TCP socket 并绑定到本地地址和端口 8080。服务器会在等待客户端连接的状态下监听。当有客户端连接时,它会接受连接并发送一条消息,然后关闭连接。
客户端:客户端首先创建一个 TCP socket,并连接到服务器。然后,使用 socket_export_stream 将 socket 转换为流资源。接着,我们通过 fread 函数读取从服务器返回的数据。
在网络编程中,读取数据时常常遇到的问题是数据读取不完全。比如,服务器发送的数据量可能远大于一次读取的字节数。这时你需要做一些数据拼接工作,直到读取到所有的数据。
<span><span><span class="hljs-variable">$response</span></span><span> = </span><span><span class="hljs-string">''</span></span><span>;
</span><span><span class="hljs-keyword">while</span></span><span> (!</span><span><span class="hljs-title function_ invoke__">feof</span></span><span>(</span><span><span class="hljs-variable">$stream</span></span><span>)) {
</span><span><span class="hljs-variable">$chunk</span></span><span> = </span><span><span class="hljs-title function_ invoke__">fread</span></span><span>(</span><span><span class="hljs-variable">$stream</span></span><span>, </span><span><span class="hljs-number">1024</span></span><span>); </span><span><span class="hljs-comment">// 每次读取 1024 字节</span></span><span>
</span><span><span class="hljs-variable">$response</span></span><span> .= </span><span><span class="hljs-variable">$chunk</span></span><span>;
}
</span></span>
PHP 的 socket 默认是阻塞的,也就是说,fread 可能会一直等到数据完全到达才返回。如果需要非阻塞式的读取数据,可以使用 socket_set_nonblock 设置 socket 为非阻塞模式,这样 fread 就会立即返回,即使数据还未完全读取。
<span><span><span class="hljs-title function_ invoke__">socket_set_nonblock</span></span><span>(</span><span><span class="hljs-variable">$socket</span></span><span>);
</span></span>
通过 socket_export_stream 和 fread 函数,我们能够更加方便地操作 socket 数据流。这两个函数结合使用,可以让我们像操作文件流一样简单地读取来自远程服务器的网络数据。在实际的开发中,这种方式非常适合处理实时、持续的数据交互。
希望通过本文的示例,你能更好地理解如何将这两个函数结合起来进行实际开发,处理从 socket 获取的数据。