【swoole.1.02】初体验

一、什么是socket编程

1.socket编程和web编程的区别

1.数据传输方式

所谓socket通常也称作"套接字",实现服务器和客户端之间的物理连接,并进行数据传输,主要有udp和tcp两个协议。socket处于网络协议的传输层。

udp协议:广播式数据传输,不进行数据验证

tcp协议:传输控制协议,一种面向连接的协议,给用户进程提供可靠的全双工的字节流。

socket传输的特点:

优点

  1. 传输数据为字节级,传输数据可自定义,数据量小(对于手机应用讲:费用低)
  2. 传输数据时间短,性能高
  3. 适合于客户端和服务器端之间信息实时交互
  4. 可以加密,数据安全性强

缺点:

  1. 需对传输的数据进行解析,转化成应用级的数据
  2. 对开发人员的开发水平要求高
  3. 相对于http协议传输,增加了开发量

socket传输适用范围

基于socket传输的特点 : socket 传输方式适合于对传输速度,安全性,实时交互,费用等要求高的应用中,如网络游戏,手机应用,银行内部交互等

基于http协议传输的其特点

目前基于http协议传输的主要有http协议 和基于http协议的soap协议(web service),常见的方式是 http 的post 和get 请求,web 服务。

基于http协议的传输特点

优点:

  1. 基于应用级的接口使用方便
  2. 程序员开发水平要求不高,容错性强

缺点:

  1. 传输速度慢,数据包大(http协议中包含辅助应用信息)
  2. 如实时交互,服务器性能压力大。
  3. 数据传输安全性差

基于http协议传输的适用范围

基于http协议传输的特点:基于http协议传输方式适合于对传输速度,安全性要求不是很高,且需要快速开发的应用。如公司oa系统,互联网服务等

(以上摘自:网络编程(十一)——WebService与Socket的区别

从上文可以看出,socket编程和web编程的区别在于

  1. 数据量更小,应答更快
  2. 可以对数据包进行加密解密,交互更安全
  3. 客户端和服务器端之间信息实时交互,并且是全双工交互(不仅可以和web编程一样,由客户端发送请求,服务端进行应答,还可以由服务器发送数据给客户端以实现双向的应答模式,并且可以轻松实现广播,订阅等交互模式)

二、快速起步,体验swoole,创建一个客户端和服务端

参考文档:swoole手册-快速起步

参考文档:swoole手册-创建TCP服务器

参考文档:swoole手册-创建同步TCP客户端

参考文档:swoole手册-server事件

参考文档:swoole手册-server选项

参考文档:swoole-client相关

1.服务端 Swoole\Server

1)事件驱动

Swoole\Server是事件驱动模式,所有的业务逻辑代码必须写在事件回调函数中。当特定的网络事件发生后,底层会主动回调指定的PHP函数(类似于js:on(click,function(){})),你需要做的只是回调后的业务处理,不需要关心事件底层是怎么写的。

PHP语言有4种回调函数的写法

2)创建一个服务端并绑定基础事件

根据 参考文档:swoole手册-快速起步 得知,创建一个基础的swoole服务端需要5步

  1. 创建server对象,监听ip,端口
  2. 监听连接进入事件
  3. 监听数据接收事件
  4. 监听连接关闭事件
  5. 启动服务器

代码如下

<?php

//  创建server对象,监听所有ip,9001端口
$server = new \Swoole\Server('0.0.0.0', 9001);

//  监听连接进入事件
$server->on('connect', function () {
    echo "触发连接回调" . PHP_EOL;
});

//  监听数据接收事件
$server->on('receive', function () {
    echo "触发数据接收回调" . PHP_EOL;
});

//  监听连接关闭事件
$server->on('close', function () {
    echo "触发关闭回调" . PHP_EOL;
});

//  启动服务器
$server->start();

去服务器中启动服务端

[root@iZbp1acp86oa3ixxw4n1dpZ 1.02]# php server.php

启动后发现,没有任何输出,并且命令行处于阻塞状态,那是因为socket开发不同于web开发的请求后执行脚本应答,socket开发中服务端是作为一个进程长期执行的,查询进程就可以发现

[root@iZbp1acp86oa3ixxw4n1dpZ ~]# ps -aux | grep swoole
[root@iZbp1acp86oa3ixxw4n1dpZ ~]# ps -aux | grep server.php
root     20760  0.0  2.0 386388 38520 pts/0    Sl+  17:39   0:00 php server.php
root     20761  0.0  0.2 312348  5136 pts/0    S+   17:39   0:00 php server.php
root     20763  0.0  0.3 314892  7500 pts/0    S+   17:39   0:00 php server.php
root     20955  0.0  0.0 112712   992 pts/1    R+   17:42   0:00 grep --color=auto server.php

linux中有正在执行的 php server.php ,并且有3个。但是明明就执行了一次,为什么会有3个 php server.php 呢?

那是因为swoole的进程结构是由一个master,一个maneger和若干个worker构成的,在不特殊配置的情况下会自动生成一个master,一个maneger,等同于cpu核心数的worker。修改下配置再查看。

$server->set([
    'worker_num' => 2
]);
[root@iZbp1acp86oa3ixxw4n1dpZ ~]# ps -aux | grep server.php
root     21196  1.0  2.0 388440 38520 pts/0    Sl+  17:47   0:00 php server.php
root     21197  0.0  0.2 314400  5124 pts/0    S+   17:47   0:00 php server.php
root     21199  0.0  0.4 314892  7600 pts/0    S+   17:47   0:00 php server.php
root     21200  0.0  0.4 314892  7600 pts/0    S+   17:47   0:00 php server.php
root     21203  0.0  0.0 112712   996 pts/1    R+   17:47   0:00 grep --color=auto server.php

可以看到当设置worker_num为2的时候出现了4个进程。

其中worker_num在官方的推荐设置是:

  1. 业务代码是全异步非阻塞的,这里设置为CPU核数的1-4倍最合理
  2. 业务代码为同步阻塞,需要根据请求响应时间和系统负载来调整,例如:100-500
  3. 默认设置为SWOOLE_CPU_NUM,最大不得超过SWOOLE_CPU_NUM * 1000
  4. 比如1个请求耗时100ms,要提供1000QPS的处理能力,那必须配置100个进程或更多。但开的进程越多,占用的内存就会大大增加,而且进程间切换的开销就会越来越大。所以这里适当即可。不要配置过大。
  5. 假设每个进程占用40M内存,100个进程就需要占用4G内存

3)向服务端发送请求

发送请求是很简单的,因为服务端是tcp协议的,所以可以直接使用更高级的HTTP协议请求服务端

请求:

[root@iZbp1acp86oa3ixxw4n1dpZ ~]# curl http://www.noobcoder.cn:9001/
curl: (52) Empty reply from server

服务端:

[root@iZbp1acp86oa3ixxw4n1dpZ 1.02]# php server.php 
触发连接回调
触发数据接收回调

4)创建一个服务端请求服务端

同样的,创建服务端也需要5步

  1. 创建客户端
  2. 连接到服务器
  3. 向服务器发送数据
  4. 从服务器接收数据
  5. 关闭连接
<?php
//  创建客户端
$client = new \Swoole\Client(SWOOLE_SOCK_TCP);

//  连接到服务器
$client->connect('127.0.0.1', 9001);

//  向服务器发送数据
$client->send('我来了');

//  从服务器接收数据

//  关闭连接
$client->close();

因为服务端现在还没有做发送数据,所以接受数据先不做了

启动客户端

[root@iZbp1acp86oa3ixxw4n1dpZ 1.02]# php client.php

服务端(其中3个是上一次的实验)

[root@iZbp1acp86oa3ixxw4n1dpZ 1.02]# php server.php 
触发连接回调
触发数据接收回调
触发关闭回调
触发连接回调
触发数据接收回调
触发关闭回调

成功通信

5)服务端回复+客户端接受

在connect事件中,可以透传的参数如下

function onConnect(swoole_server $server, int $fd, int $reactorId);

参数解释

  1. $server是Swoole\Server对象
  2. $fd是连接的文件描述符,发送数据/关闭连接时需要此参数
  3. $reactorId来自哪个Reactor线程

服务端使用 Server->send 向客户端发送数据

bool Server->send(mixed $fd, string $data, int $serverSocket = -1);

参数解释:

  1. $fd,客户端的文件描述符
  2. $data,发送的数据,TCP协议最大不得超过2M,可修改 buffer_output_size 改变允许发送的最大包长度
  3. $serverSocket,向Unix Socket DGRAM对端发送数据时需要此项参数,TCP客户端不需要填写

其中,可能会有同学对fd有疑问

socket中的fd:fild descriptor,就是一个套接字描述器。 在UNIX中的一切事物都是文件(everything in Unix is a file!)。我们用int在描述socket,实际上,所有的文件描述符都是int,没错,用的是一个整数类型。文件是应用程序与系统(包括特定硬件设备)之间的桥梁,而文件描述符就是应用程序使用这个“桥梁”的接口。在需要的时候,应用程序会向系统申请一个文件,然后将文件的描述符返回供程序使用。返回socket的文件通常被创建在/tmp或者/usr/tmp中。我们实际上不用关心这些文件,仅仅能够利用返回的socket描述符就可以了。

摘自:socket中fd是什么意思以及如何通过socket获取对方地址

fd其实就是socket的标示,他代表的是某个客户端和服务端之间的socket套接字连接描述器,可以直接理解为socket的id,同一时间可能有N个客户端通过socket连接服务端,fd就是每个socket的身份标示。

我们加上发送数据的代码

//  监听连接进入事件
$server->on('connect', function (\Swoole\Server $server, int $fd, int $reactorId) {
    echo "触发连接回调" . PHP_EOL;
    $message = '欢迎,你的fd是:' . $fd;
    echo "我发送了【{$message}】" . PHP_EOL;
    $server->send($fd, $message);
});

在客户端中接受数据

//  从服务器接收数据
echo $client->recv();

效果如下

客户端:

[root@iZbp1acp86oa3ixxw4n1dpZ 1.02]# php client.php 
欢迎,你的fd是:1

服务器:

[root@iZbp1acp86oa3ixxw4n1dpZ 1.02]# php server.php 
触发连接回调
我发送了【欢迎,你的fd是:1】
触发数据接收回调
触发关闭回调

附录

希望各位可以多看文档,学习更多没有提到的知识

源码:点击下载

参考文档:swoole手册-快速起步

参考文档:swoole手册-创建TCP服务器

参考文档:swoole手册-创建同步TCP客户端

参考文档:swoole手册-server事件

参考文档:swoole手册-server选项

参考文档:swoole-client相关

程序幼儿员-龚学鹏
请先登录后发表评论
  • latest comments
  • 总共0条评论