上一节,我们学习了如何搭起简单的 Http、TCP 以及 UDP 服务。是不是发现在 Swoole 中搭建这三种服务非常地简单方便。对于 Http 客户端来说,我们可以直接使用浏览器来进行测试,或者普通的 Curl、Guzzle 也可以方便地从代码中进行 Http 的测试。因此,我们也就不会过多地说 Http 客户端的问题。等到进阶相关的文章时,我们会再看看在 协程 中的 Http 客户端如何使用。
今天的内容主要是针对于 TCP 和 UDP 的客户端。上篇文章中,我们使用的是命令行的 telnet 和 nc 工具来测试这两种服务的运行情况,今天我们直接通过 Swoole 的客户端对象来进行测试。
在 Swoole 中,有同步阻塞客户端和协程客户端两种类型的客户端,今天我们就行来简单地学习一下同步阻塞客户端。
什么叫 同步阻塞 ?其实就是我们正常的那种按照前后关系顺序执行的代码,也就是我们在传统开发中写的那种代码。代码是按照顺序从上往下执行的,前面的代码没有执行完,后面的代码也不会运行。如果中间遇到函数,则会通过类似栈的处理方式进入函数中进行处理。从本质上来说,其实 面向对象 这种编程方式是有部分跳出这种线性执行代码的模式的,但是,它还是同步执行的。
而多线程、协程这种东西,其实就是脱离了同步阻塞问题的,关于进程、线程、协程相关的问题,我们后面有专门的文章来说明。今天大家就只需要大概了解一下就可以了。或者,你把我们今天实现的代码就当做是一个 Swoole 中自带的 Guzzle TCP/UDP 版本客户端就好了。
$client = new Swoole\Client(SWOOLE_SOCK_TCP);
if (!$client->connect('127.0.0.1', 9501, -1)) {
exit("connect failed. Error: {$client->errCode}\n");
}
$client->send("hello world\n");
echo $client->recv();
$client->close();
实现一个 TCP 客户端非常简单,实例化一个 Swoole\Client 对象。它的构造参数可以传递 SWOOLE_SOCK_TCP 或者 SWOOLE_SOCK_UDP 等内容。从名字就可以看出,一个是 TCP 客户端,一个是 UDP 客户端。
接着,我们通过 connect() 方法进行连接,连接的就是本机的 TCP 端口。这里我们直接将上篇文章中的 TCP 服务启动起来就可以了。
接着 send() 方法用于发送数据到 服务端 ,recv() 方法用于接收服务端返回的信息,最后的 close() 用于关闭客户端句柄。
是不是非常简单,我们运行一下。
[root@localhost source]# php 2.3Http、TCP、UDP服务客户端.php
Server TCP:hello world
这个打印出来的内容,就是我们在服务端输出的数据。相信这一块的内容大家应该是没有什么难度的。我们直接再看看 UDP 客户端。
对于 UDP 来说,其实它的实现代码和上面的 TCP 差不多,而且更加简洁。为什么呢?我们都知道,TCP 是要建立稳定连接的,有三次握手四次挥手的过程,这也是 TCP 的基础知识。而 UDP 不需要,它不用建立稳定的连接,所以,我们可以在 UDP 中省略掉 connect() 的步骤。
$client = new Swoole\Client(SWOOLE_SOCK_UDP);
$client->sendto('127.0.0.1', 9501, "hello world\n");
echo $client->recv();
$lient->close();
够简单吧?另外,我们在这里使用的是 sendto() 方法,它的作用是向任意的地址和端口发送 UDP 数据包。当然,你在这里使用 connect() 并且通过 send() 发送 UDP 数据也是没问题的,大家可以自己尝试一下。
[root@localhost source]# php 2.3Http、TCP、UDP服务客户端.php
Server UDP:hello world
最后,我们再来看几个客户端对象的其它方法。
var_dump($client->isConnected()); // bool(true)
// var_dump($client->getSocket());
var_dump($client->getsockname());
//array(2) {
// ["port"]=>
// int(47998)
// ["host"]=>
// string(9) "127.0.0.1"
//}
第一个 isConnected() 用于返回客户端是否连接的布尔值。前提当然是要调用了 connect() 并成功建立连接之后才会返回 true 。
getSocket() 用于返回一个 socket 扩展的句柄资源符,目前我们的系统环境中暂时没有安装 socket 扩展,所以这个函数还用不了。
getsockname() 用于获取客户端的 socket 在本地的 host 和 port 端口。可以看到注释中我们程序自动在本地开了 47998 这个端口用于和服务端的 TCP 进行通信使用。
另外在 UDP 中,我们可以使用 getpeername() 获得对端 socket 的 IP 地址和端口。
var_dump($client->getpeername());
//array(2) {
// ["port"]=>
// int(0)
// ["host"]=>
// string(7) "0.0.0.0"
//}
这个方法仅支持 UPD 连接,因为 UDP 协议通信客户端向一台服务器发送数据包后,可能并非由此服务器向客户端发送响应。可以使用 getpeername() 方法获取实际响应的服务器 IP 和 PORT。当然,我们目前在本机没有这种情况,直接返回的全是零。
除了上述内容之外,还有证书相关的方法函数,另外也有建立长连接的常量参数,这些内容大家可以自己在下面的官方文档链接中找到,在这里我就不做过多的演示了。毕竟只是带大家入个门,直接搬文档可不是我的风格。
好了,最重要的三个网络服务及相关的客户端的入门展示我们就学习完成了,下一篇文章我们将再学习一个现在比较流行的服务应用,那就是 WebSocket 的使用。
测试代码:
https://github.com/zhangyue0503/swoole/blob/main/2.%E5%9F%BA%E7%A1%80/source/2.3Http%E3%80%81TCP%E3%80%81UDP%E6%9C%8D%E5%8A%A1%E5%AE%A2%E6%88%B7%E7%AB%AF.php
参考文档:
https://wiki.swoole.com/#/client
联系客服