当前位置: 首页> 最新文章列表> 如何配合 socket_sendmsg 函数实现基于 socket_recvmsg 的双向通信?

如何配合 socket_sendmsg 函数实现基于 socket_recvmsg 的双向通信?

M66 2025-07-04

在 PHP 中,基于 Socket 的双向通信是一种常见的网络编程任务。当涉及到高效的消息传递时,socket_sendmsgsocket_recvmsg 这两个函数可以提供更高级的功能,帮助开发者实现更加灵活和复杂的通信模式。本文将探讨如何使用 socket_sendmsg 配合 socket_recvmsg 来实现双向通信。

1. socket_sendmsgsocket_recvmsg 简介

首先,我们需要了解这两个函数的基本使用:

  • socket_sendmsg:此函数用于向指定的 Socket 发送数据,并且可以通过它传递附加的标志、控制信息或附件数据。它与 socket_send 的主要区别在于,socket_sendmsg 提供了更强大的灵活性,能够支持更复杂的数据结构。

  • socket_recvmsg:此函数则是用于从一个 Socket 接收消息。它可以接收的消息不仅包含数据,还可能包含一些控制信息,如发送者的地址、数据的元数据等。与 socket_recv 相比,它能够返回更多的通信信息,使得双向通信更为灵活。

2. 双向通信的工作原理

在实际的应用中,双向通信往往意味着双方需要能够互相发送和接收数据。借助于 socket_sendmsgsocket_recvmsg,我们能够高效地实现这一目标。这两个函数可以通过控制消息头部和数据部分的方式,进行更加灵活的交互。

2.1 初始化 Socket 连接

首先,我们需要初始化一个 Socket,通常通过 socket_create 函数创建一个 TCP 或 UDP 类型的 Socket。接下来,我们可以使用 socket_bindsocket_connect 来进行绑定或连接。

<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-keyword">if</span></span><span> (</span><span><span class="hljs-variable">$socket</span></span><span> === </span><span><span class="hljs-literal">false</span></span><span>) {
    </span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-string">"Socket 创建失败: "</span></span><span> . </span><span><span class="hljs-title function_ invoke__">socket_strerror</span></span><span>(</span><span><span class="hljs-title function_ invoke__">socket_last_error</span></span><span>());
    </span><span><span class="hljs-keyword">exit</span></span><span>();
}

</span><span><span class="hljs-comment">// 连接到服务器</span></span><span>
</span><span><span class="hljs-variable">$address</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-variable">$result</span></span><span> = </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">$address</span></span><span>, </span><span><span class="hljs-variable">$port</span></span><span>);
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-variable">$result</span></span><span> === </span><span><span class="hljs-literal">false</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-title function_ invoke__">socket_strerror</span></span><span>(</span><span><span class="hljs-title function_ invoke__">socket_last_error</span></span><span>());
    </span><span><span class="hljs-keyword">exit</span></span><span>();
}
</span></span>

2.2 使用 socket_sendmsg 发送消息

socket_sendmsg 函数能够让我们发送包含复杂数据结构的消息。通常,它会把数据封装到一个 msghdr 结构中,可以包含附加的控制信息。例如,指定发送的标志位、附加数据、目的地地址等。

<span><span><span class="hljs-variable">$msg</span></span><span> = </span><span><span class="hljs-string">"Hello, Server!"</span></span><span>;
</span><span><span class="hljs-variable">$len</span></span><span> = </span><span><span class="hljs-title function_ invoke__">strlen</span></span><span>(</span><span><span class="hljs-variable">$msg</span></span><span>);

</span><span><span class="hljs-comment">// 创建一个消息头</span></span><span>
</span><span><span class="hljs-variable">$msgHdr</span></span><span> = </span><span><span class="hljs-keyword">array</span></span><span>(
    </span><span><span class="hljs-string">'msg_name'</span></span><span> =&gt; </span><span><span class="hljs-literal">null</span></span><span>,           </span><span><span class="hljs-comment">// 默认目标地址为空</span></span><span>
    </span><span><span class="hljs-string">'msg_namelen'</span></span><span> =&gt; </span><span><span class="hljs-number">0</span></span><span>,
    </span><span><span class="hljs-string">'msg_iov'</span></span><span> =&gt; </span><span><span class="hljs-keyword">array</span></span><span>(</span><span><span class="hljs-keyword">array</span></span><span>(</span><span><span class="hljs-string">'iov_base'</span></span><span> =&gt; </span><span><span class="hljs-variable">$msg</span></span><span>, </span><span><span class="hljs-string">'iov_len'</span></span><span> =&gt; </span><span><span class="hljs-variable">$len</span></span><span>)),  </span><span><span class="hljs-comment">// 数据内容</span></span><span>
    </span><span><span class="hljs-string">'msg_iovlen'</span></span><span> =&gt; </span><span><span class="hljs-number">1</span></span><span>,
    </span><span><span class="hljs-string">'msg_control'</span></span><span> =&gt; </span><span><span class="hljs-literal">null</span></span><span>,
    </span><span><span class="hljs-string">'msg_controllen'</span></span><span> =&gt; </span><span><span class="hljs-number">0</span></span><span>,
    </span><span><span class="hljs-string">'msg_flags'</span></span><span> =&gt; </span><span><span class="hljs-number">0</span></span><span>
);

</span><span><span class="hljs-comment">// 发送消息</span></span><span>
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-title function_ invoke__">socket_sendmsg</span></span><span>(</span><span><span class="hljs-variable">$socket</span></span><span>, </span><span><span class="hljs-variable">$msgHdr</span></span><span>) === </span><span><span class="hljs-literal">false</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-title function_ invoke__">socket_strerror</span></span><span>(</span><span><span class="hljs-title function_ invoke__">socket_last_error</span></span><span>());
    </span><span><span class="hljs-keyword">exit</span></span><span>();
}
</span></span>

2.3 使用 socket_recvmsg 接收消息

socket_recvmsg 函数与 socket_sendmsg 相反,用于从 Socket 接收包含复杂数据的消息。它返回的消息除了数据部分,还可能包含发送者的地址、标志等信息。

<span><span><span class="hljs-comment">// 准备接收消息的缓冲区</span></span><span>
</span><span><span class="hljs-variable">$buf</span></span><span> = </span><span><span class="hljs-string">""</span></span><span>;
</span><span><span class="hljs-variable">$bufLen</span></span><span> = </span><span><span class="hljs-number">1024</span></span><span>;
</span><span><span class="hljs-variable">$msgHdrRecv</span></span><span> = </span><span><span class="hljs-keyword">array</span></span><span>(
    </span><span><span class="hljs-string">'msg_name'</span></span><span> =&gt; </span><span><span class="hljs-literal">null</span></span><span>,
    </span><span><span class="hljs-string">'msg_namelen'</span></span><span> =&gt; </span><span><span class="hljs-number">0</span></span><span>,
    </span><span><span class="hljs-string">'msg_iov'</span></span><span> =&gt; </span><span><span class="hljs-keyword">array</span></span><span>(</span><span><span class="hljs-keyword">array</span></span><span>(</span><span><span class="hljs-string">'iov_base'</span></span><span> =&gt; &amp;</span><span><span class="hljs-variable">$buf</span></span><span>, </span><span><span class="hljs-string">'iov_len'</span></span><span> =&gt; </span><span><span class="hljs-variable">$bufLen</span></span><span>)),
    </span><span><span class="hljs-string">'msg_iovlen'</span></span><span> =&gt; </span><span><span class="hljs-number">1</span></span><span>,
    </span><span><span class="hljs-string">'msg_control'</span></span><span> =&gt; </span><span><span class="hljs-literal">null</span></span><span>,
    </span><span><span class="hljs-string">'msg_controllen'</span></span><span> =&gt; </span><span><span class="hljs-number">0</span></span><span>,
    </span><span><span class="hljs-string">'msg_flags'</span></span><span> =&gt; </span><span><span class="hljs-number">0</span></span><span>
);

</span><span><span class="hljs-comment">// 接收消息</span></span><span>
</span><span><span class="hljs-variable">$bytesReceived</span></span><span> = </span><span><span class="hljs-title function_ invoke__">socket_recvmsg</span></span><span>(</span><span><span class="hljs-variable">$socket</span></span><span>, </span><span><span class="hljs-variable">$msgHdrRecv</span></span><span>);
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-variable">$bytesReceived</span></span><span> === </span><span><span class="hljs-literal">false</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-title function_ invoke__">socket_strerror</span></span><span>(</span><span><span class="hljs-title function_ invoke__">socket_last_error</span></span><span>());
    </span><span><span class="hljs-keyword">exit</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">$buf</span></span><span> . </span><span><span class="hljs-string">"\n"</span></span><span>;
</span></span>

3. 典型的双向通信流程

在实际使用中,双向通信的基本流程可以概括为以下几个步骤:

  1. 连接建立:客户端通过 socket_connect 连接到服务器,服务器通过 socket_accept 等待客户端连接。

  2. 发送消息:客户端或服务器可以通过 socket_sendmsg 发送消息。需要注意的是,可以传递附加的数据或标志位来表示特定的控制信息。

  3. 接收消息:接收方通过 socket_recvmsg 接收消息。收到的消息不仅仅包括数据部分,还包括一些元数据(如消息标志、发送者等信息)。

  4. 交替发送和接收:客户端和服务器交替使用 socket_sendmsgsocket_recvmsg 进行双向数据交换。每次发送时,可以附加不同的控制信息或更复杂的数据结构。

<span><span><span class="hljs-comment">// 服务器端代码示例(简化版)</span></span><span>
</span><span><span class="hljs-variable">$serverSocket</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">$serverSocket</span></span><span>, </span><span><span class="hljs-string">'127.0.0.1'</span></span><span>, </span><span><span class="hljs-number">8080</span></span><span>);
</span><span><span class="hljs-title function_ invoke__">socket_listen</span></span><span>(</span><span><span class="hljs-variable">$serverSocket</span></span><span>);

</span><span><span class="hljs-comment">// 等待客户端连接</span></span><span>
</span><span><span class="hljs-variable">$clientSocket</span></span><span> = </span><span><span class="hljs-title function_ invoke__">socket_accept</span></span><span>(</span><span><span class="hljs-variable">$serverSocket</span></span><span>);

</span><span><span class="hljs-comment">// 接收并发送数据</span></span><span>
</span><span><span class="hljs-variable">$buffer</span></span><span> = </span><span><span class="hljs-string">''</span></span><span>;
</span><span><span class="hljs-variable">$header</span></span><span> = </span><span><span class="hljs-keyword">array</span></span><span>(
    </span><span><span class="hljs-string">'msg_name'</span></span><span> =&gt; </span><span><span class="hljs-literal">null</span></span><span>,
    </span><span><span class="hljs-string">'msg_namelen'</span></span><span> =&gt; </span><span><span class="hljs-number">0</span></span><span>,
    </span><span><span class="hljs-string">'msg_iov'</span></span><span> =&gt; </span><span><span class="hljs-keyword">array</span></span><span>(</span><span><span class="hljs-keyword">array</span></span><span>(</span><span><span class="hljs-string">'iov_base'</span></span><span> =&gt; &amp;</span><span><span class="hljs-variable">$buffer</span></span><span>, </span><span><span class="hljs-string">'iov_len'</span></span><span> =&gt; </span><span><span class="hljs-number">1024</span></span><span>)),
    </span><span><span class="hljs-string">'msg_iovlen'</span></span><span> =&gt; </span><span><span class="hljs-number">1</span></span><span>,
    </span><span><span class="hljs-string">'msg_control'</span></span><span> =&gt; </span><span><span class="hljs-literal">null</span></span><span>,
    </span><span><span class="hljs-string">'msg_controllen'</span></span><span> =&gt; </span><span><span class="hljs-number">0</span></span><span>,
    </span><span><span class="hljs-string">'msg_flags'</span></span><span> =&gt; </span><span><span class="hljs-number">0</span></span><span>
);
</span><span><span class="hljs-keyword">while</span></span><span> (</span><span><span class="hljs-literal">true</span></span><span>) {
    </span><span><span class="hljs-variable">$bytesReceived</span></span><span> = </span><span><span class="hljs-title function_ invoke__">socket_recvmsg</span></span><span>(</span><span><span class="hljs-variable">$clientSocket</span></span><span>, </span><span><span class="hljs-variable">$header</span></span><span>);
    </span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-variable">$bytesReceived</span></span><span> === </span><span><span class="hljs-literal">false</span></span><span>) {
        </span><span><span class="hljs-keyword">break</span></span><span>;
    }
    </span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-string">"收到来自客户端的消息: <span class="hljs-subst">$buffer</span></span></span><span>\n";

    </span><span><span class="hljs-comment">// 向客户端发送回应</span></span><span>
    </span><span><span class="hljs-variable">$msg</span></span><span> = </span><span><span class="hljs-string">"Hello from server!"</span></span><span>;
    </span><span><span class="hljs-variable">$msgHdrSend</span></span><span> = </span><span><span class="hljs-keyword">array</span></span><span>(
        </span><span><span class="hljs-string">'msg_name'</span></span><span> =&gt; </span><span><span class="hljs-literal">null</span></span><span>,
        </span><span><span class="hljs-string">'msg_namelen'</span></span><span> =&gt; </span><span><span class="hljs-number">0</span></span><span>,
        </span><span><span class="hljs-string">'msg_iov'</span></span><span> =&gt; </span><span><span class="hljs-keyword">array</span></span><span>(</span><span><span class="hljs-keyword">array</span></span><span>(</span><span><span class="hljs-string">'iov_base'</span></span><span> =&gt; </span><span><span class="hljs-variable">$msg</span></span><span>, </span><span><span class="hljs-string">'iov_len'</span></span><span> =&gt; </span><span><span class="hljs-title function_ invoke__">strlen</span></span><span>(</span><span><span class="hljs-variable">$msg</span></span><span>))),
        </span><span><span class="hljs-string">'msg_iovlen'</span></span><span> =&gt; </span><span><span class="hljs-number">1</span></span><span>,
        </span><span><span class="hljs-string">'msg_control'</span></span><span> =&gt; </span><span><span class="hljs-literal">null</span></span><span>,
        </span><span><span class="hljs-string">'msg_controllen'</span></span><span> =&gt; </span><span><span class="hljs-number">0</span></span><span>,
        </span><span><span class="hljs-string">'msg_flags'</span></span><span> =&gt; </span><span><span class="hljs-number">0</span></span><span>
    );
    </span><span><span class="hljs-title function_ invoke__">socket_sendmsg</span></span><span>(</span><span><span class="hljs-variable">$clientSocket</span></span><span>, </span><span><span class="hljs-variable">$msgHdrSend</span></span><span>);
}
</span></span>

4. 总结

使用 socket_sendmsgsocket_recvmsg 函数可以大大提高基于 Socket 的通信效率,特别是在需要处理复杂消息或传递附加控制信息的场景下。它们的灵活性让开发者能够更好地实现双向通信,通过轮询和控制消息来完成数据的交换。理解这些函数的使用方式,对于实现高效的网络通信至关重要。