swoole

1.简介

网址:http://www.swoole.com/ 韩天峰

Swoole:面向生产环境的 PHP 异步网络通信引擎,Swoole 使用纯 C 语言编写,Swoole是PHP一个扩展的形式。

Swoole可以使 PHP开发人员编写高性能的异步并发 TCP、UDP、Unix Socket、HTTP,WebSocket 服务。

Swoole 可以广泛应用于互联网、移动通信、企业软件、云计算、网络游戏、物联网(IOT)、车联网、智能家居等领域。

注:这个几乎和nodejs一样的东西,但是它效率比nodejs高一点点(其实可以忽略不记),因为是纯c编写的,nodejs是对Chrome V8引擎进行了封装,v8是基于c++开发的。
http2.0默认支持websocket,未来肯定是潮流
rpc 远程过程调用(微服务)

2.下载和安装

swoole是一个PHP的扩展,所以安装的方式和安装其它的PHP扩展的方式一样。swoole不支持windows安装,没有windows扩展。 linux系统或Mac系统 Docker也是可以的

本篇操作的环境是 centos8,云上环境(腾讯云)、phpstorm
注:不管是安装redis还是memcache 都可以通过yum指令一键安装,下面是编译安装swoole的过程步骤。

1.下载地址

Github:

https://github.com/swoole/swoole-src/tags

php官方扩展库(推荐):

http://pecl.php.net/package/swoole

在这里插入图片描述

mkdir /src
cd /src
wget http://pecl.php.net/get/swoole-4.6.2.tgz

2.安装依赖环境

官网地址(查看依赖):

https://wiki.swoole.com/#/environment

2.1仅支持Linux,FreeBSD,MacOS,3类操作系统

2.2Linux内核版本2.3.32如centos必须6.6以上

 uname -r

在这里插入图片描述

2.3对php版本的要求(根据安装不同的swoole版本有不同的要求)

http://pecl.php.net/package/swoole/4.6.2

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这里我的php是通过yum方式安装的。
直接一条指令搞定,前提是你centos得用最新得8.x版本

yum -y install php

执行后默认就是7.2版本,好比centos7默认是5.4版本

2.4gcc4.4以上版本

 gcc --version

在这里插入图片描述
没有的话就自行安装

yum install -y gcc-c++
ubuntu  apt install -y g++

2.5cmake2.4以上版本

cmake --version

在这里插入图片描述

如果没有得话自定安装

yum install -y cmake

3.安装swoole

3.1解压swoole

#解压 那个-zxf 前面的中横线可写可不写 v一般不写,v是显示过程
tar zxf swoole-4.6.2.tgz

3.2观察目录结构

在这里插入图片描述
正常的在linux下安装源码编译安装无非就是三步走

./configure 
make
make install

但是我们观察得出,swoole目录里面没有./configure这个可执行文件,如何让它有呢?因为swoole是php的拓展,而在linux系统上编译安装php拓展要先通过phpize初始化一次,但是通过yum安装php的话 phpize这个可能不一定有,phpize在php-dev这个开发包里面,如果yum安装后面没有指定这个开发包的话,就没有phpize这个命令

3.3检测php开发包是否安装

表示这个指令依赖的开发包
在这里插入图片描述

yum search php |grep dev

这个开发包是用来安装php的拓展的
在这里插入图片描述

yum install -y php-devel.x86_64

在这里插入图片描述

3.4初始化

在这里插入图片描述

3.5配置安装和环境检查

./configure --with-php-config=/usr/bin/php-config

php-config就是因为上面安装了php-devel.x86_64这个开发包后才有此命令
在这里插入图片描述
一路正常
在这里插入图片描述

3.6编译安装

make && make install

编译安装成功,且已经把swoole.so文件复制到php的拓展目录下
在这里插入图片描述
在这里插入图片描述

3.7修改配置文件让其生效

我们发现默认swoole没有
在这里插入图片描述
yum方式安装php的配置文件目录/etc/php
在这里插入图片描述
yum方式不需要在php.ini修改,在php.d/里面修改,里面每个拓展就对应一个配置文件
在这里插入图片描述
我们随便找个配置打开看看里面的内容
在这里插入图片描述

在这里插入图片描述

我们也模仿着弄一个20-swoole.ini
在这里插入图片描述
在这里插入图片描述
再次查看swoole模块
在这里插入图片描述
swoole彻底安装完成

3.快速起步

官方手册:

https://wiki.swoole.com/#/consts

1.进程管理

在这里插入图片描述
swoole是常驻内存框架,可以做到防止内存泄漏的问题,因为比如有个进程一直不关闭,可能有个变量没有释放,久而久之可能产生泄露或是溢出的问题

swoole是一个多进程,多线程的服务

swoole启动的时候会分配一个master主进程,同时产生 manger进程,同时产生多个worker和taskworker进程

master主进程负责创建多个线程来接受和返回用户请求,同时生成一个manager进程,manager进程负责生成和管理N多个worker和task进程,worker和task进程是负责干活的

apache和nginx访问的时候会重新创建了一个进程进行访问,访问结束后http请求就会被apache或者nginx收回,但是swoole内存是常驻的,如果不释放内存,就会一直在内存中,因此manager进程就是用来监控这些内存,同时生成一个新的进程,因为一切都是新的所以效率也会提高。

进程:比如电脑上的计算器.exe 和画图.exe 运行后两个就会产生各自的进程
比如这个就是两个不同的进程
在这里插入图片描述

进程和进程之间是无法通信的,想要通信可以共享内存区域
线程是通过进程创建出来的,所以线程和线程之间天生就是可以通信的
这个可能不是很好理解,但是如果学过c++的同学,肯定很清楚这些。

2.环境准备

开发我们不可能在linux系统上使用vi或者vim编辑器进行开发,因此我们需要本地开发,通过 ftp或者sftp上传代码到linux系统中

这里使用phpstorm提供的ftp来直接保存上传代码

2.1配置phpstorm支持ftp上传

首先我们在linux系统中随便找个目录来存放代码(因为我们是使用php做服务,这里不用像apache或者nginx一样配置虚拟主机指定位置)
在这里插入图片描述
重新创建一个php项目
在这里插入图片描述
配置phpstorm
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
需要证书,点击yes
在这里插入图片描述
在这里插入图片描述
找到刚才创建的目录
在这里插入图片描述
映射配置
在这里插入图片描述
新创建一个测试文件
在这里插入图片描述
手动上传
在这里插入图片描述
查看详细上传信息
在这里插入图片描述
查看服务器已经被成功上传
在这里插入图片描述
如何配置保存就自动上传呢?
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
线上立马就同步了
在这里插入图片描述

2.2让phpstorm更好的支持swoole开发

插件地址:

https://github.com/wudi/swoole-ide-helper

下载后放到项目目录下就行了
在这里插入图片描述
就会有提示了
在这里插入图片描述

3.创建tcp服务器

步骤:
3.1构建Server对象
3.2设置运行时参数
3.3注册事件回调函数
3.4启动服务

3.1创建好tcp服务

我们查看一下端口

netstat -tunpl

我们发现apache就是tcp
在这里插入图片描述

我们来学习搭建一个自己的tcp服务器,新建一个01_tpc_server.php用于测试

官网的教程
在这里插入图片描述

$serv = new Swoole\Server('0.0.0.0', 9501, SWOOLE_PROCESS, SWOOLE_SOCK_TCP);
// 参数说明
$host参数用来指定监听的ip地址  0.0.0.0监听全部地址
$port监听的端口,如9501  0-1024之间,是系统默认保留的,所在建议从5000
$mode运行的模式 SWOOLE_PROCESS多进程模式(默认)  SWOOLE_BASE基本模式
$sock_type指定Socket的类型  支持TCP、UDP等

这里其实就能看到默认的常量值
在这里插入图片描述

在这里插入图片描述

回调事件
在这里插入图片描述
我们随便看一个回调函数的参数例如Connect
在这里插入图片描述

//监听事件
// 连接tcp事件
//参数一:server对象
//参数二:客户端的ID号
//参数三:接收处理的线程ID
$server->on('Connect', function(swoole_server $server, int $fd, int $reactorId){
    echo "有新的连接接入:".$fd."\n";
});

//参数一:server对象
//参数二:客户端的ID号
//参数三:接收处理的线程ID
//参数四:接收到的数据
$server->on('Receive', function(swoole_server $server, int $fd, int $reactor_id, string $data){
    echo "接收到的数据为:".$data."\n";
});

//参数一:server对象
//参数二:客户端的ID号
//参数三:接收处理的线程ID
$server->on('Close', function(swoole_server $server, int $fd, int $reactorId){
    echo $fd."离开了我们"."\n";
});

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

php执行该文件启动服务
在这里插入图片描述
发现我们刚才的服务被启动了

netstat -tunpl

在这里插入图片描述
查看进程

ps auxf | grep php

这个刚好符合上面的架构 woker进程是可以配置它的数量的
在这里插入图片描述
可以通过以下命令进行杀死

kill -9 pid

怎么配置看官网
在这里插入图片描述

$server->set([
    //启动worker进程的数量
    'worker_num'      => 1,
]);

在这里插入图片描述
再次查看进程
在这里插入图片描述

3.2 telnet进行测试

创建好tcp服务后,需要用telnet进行测试

在linux上下载telnet

yum install -y telnet

在这里插入图片描述

#telnet 地址 端口
telnet 127.0.0.1 9501

回车进入,按下ctrl+]再次回车,就可以发内容,退出,按ctrl+] 输入 quit 退出

用telnet一连接 服务器哪里就有会提示
在这里插入图片描述
telnet退出也有反应

在这里插入图片描述
再次连接后,发现它不是从1开始计算,因为它想着你还可能再次连接,
因为1的内存还没释放,防止1还要再回来,这是linux内核的机制
在这里插入图片描述
服务端挂掉客户端也有反应
在这里插入图片描述
在这里插入图片描述

windows下也有telnet工具,可能默认没有开启,需要手动开启

在这里插入图片描述
注意此时这里是在本地需要通过远程的IP地址(腾讯云)连接

telnet 192.168.xxx.xxx 端口

但是这个windows上的这个telnet并不好用,因为它没输入任何一个字符它都会响应
在这里插入图片描述

而linux下的telnet则是按下回车才有反应

3.3tcp客户端链接tcp

客户端连接分为同步和异步,但是swoole4不再支持异步客户端,相应的需求完全可以用协程客户端代替
在这里插入图片描述

在这里插入图片描述
新建一个php文件02_tcp_client.php

<?php
//tcp客户端
$client = new \Swoole\Client(SWOOLE_SOCK_TCP);

//连接
//参数3是 超时时间
$client->connect('127.0.0.1','9501',10);

//发送数据
$client->send('大家好,我是新来的客户端');

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


测试
执行02_tcp_client.php
在这里插入图片描述
测试服务器就会有反应
在这里插入图片描述
接收服务器端发送过来的数据
修改两个地方
服务端
在这里插入图片描述
客户端
在这里插入图片描述

测试

因为swoole是常驻内存,我们改动了代码,需要重新再次启动服务端
在这里插入图片描述
在这里插入图片描述

3.4原生php实现tcp客户端

新建03_php_tcp_client.php测试

在这里插入图片描述

<?php
//原生php 发起一个 tcp
// socket 当作是网络中的一个文件
$socket = stream_socket_client('tcp://42.194.192.22:9501',$errno,$errstr,30);

//发送数据
fwrite($socket,"我是原生php的tcp客户端");

//接受数据
$buffer = fread($socket,9000);

//关闭
fclose($socket);

echo $buffer;


重启服务端
在这里插入图片描述
测试
在这里插入图片描述
浏览器端访问
在这里插入图片描述

3.5rpc

RPC,是一种远程调用方式(Remote Procedure Call),通过RPC我们可以像调用本地方法一样调用别的机器上的方法,用户将无感服务器与服务器之间的通讯。RPC在微服务当中起到相当大的作用,当然RPC不是微服务必须的一种方式,有别的方式也可以实现这种远程调用例如RESTful API就可以实现远程调用。如果有用过SOAP那么你使用RPC将会觉得很类似,都是可以直接调用别的机器上的方法。

随着业务的发展我们的项目从简单的单体结构逐渐的演化成微服务结构,我们为什么要拆分成微服务呢?那我们来说说微服务和单体架构的优缺点。

3.5.1单体架构图示

在这里插入图片描述
简单点说,就好比是tp框架中每个控制器就对应着一个模块,所有的功能模块都在一个服务器上就完成。

3.5.2微服务架构图示

在这里插入图片描述
在2014年被提出,现在国内很多公司已经使用,微服务是一种架构设计,并不是说什么框架或者代替什么。微服务做的事情是按照项目颗粒度进行服务的拆分,把模块单独拿出来做成每一个单独的小项目。微服务的主要特点有:每一个功能模块是一个小项目、独立运行在不同进程或者机器上、不同功能可以又不同的人员开发独立开发不松耦合、独立部署不需要依赖整体项目就可以启动单个服务、分布式管理。每一个服务只要做好自己的事情就好了。在设计微服务的时候还需要考虑到数据库的问题,是所有微服务使用共同一个数据库还是每一个服务单个数据库

3.5.3单体架构优点
  • 部署容易,如php写的项目,只要一个文件夹复制到支持php的环境就可以了,java只需要一个jar包

  • 测试容易,我们整体项目只要改了一个地方马上就可以测试得出结果

  • 负载均衡就可以解决,快速部署多个一模一样的项目在不同的机器运行分流

3.5.4单体架构的缺点
  • 部署的问题,对于php来说这点还好,但是对于java的项目来说,我们需要重新打包整个项目耗费的时间是很长的

  • 代码维护,由于所有的代码都写在一个项目里面,要想要修改某一个功能点那么需要对项目的整体逻辑和设计有较深的理解,否则代码耦合严重,导致维护难,特别对于新入职的员工来说这将是最容易出现问题的地方

  • 开发效率低,随着项目需求的不断改变和新的功能新增,老旧的代码又不敢随便删除,导致整个项目变得笨重,这将会增加你阅读代码的时间

  • 扩展性,在高并发的情况下,我们往往不是整个项目的每一个功能都处于高流量高请求的情况下的,很多时候都是某一个功能模块使用的人数比较多,在单体结构下我们没有办法针对单个功能实现分布式扩展,必须整个项目一起部署

3.5.5微服务优点
  • 拆分业务,把整体大项目分割成不同小项目运行在不同进程或者机器上实现数据隔离

  • 技术栈,每个服务可以由不同的团队或者开发者进行开发,外部调用人员不需要操心具体怎么实现的,只需要类似调用自己方法一样或者接口一样按照服务提供者给出来的参数传递即可

  • 独立部署,每一个服务独立部署,部署一个服务不会影响整体项目,如果部署失败最多是这个服务的功能缺失,并不影响其他功能的使用

  • 按需部署,针对不同的需求可以给不同的服务自由扩展服务器,根据服务的规模部署满足需求的实例

  • 局部修改,当一个服务有新需求或者其他修改,不需要修改整体项目只要管好自己的服务就好了

3.5.6微服务缺点
  • 运维,微服务由于把业务拆分得细,有可能部署在不同机器上,因此对于运维人员的管理来说,这部分的成本会加大

  • 接口调整,微服务之间通过接口进行通信。如果修改某个微服务的API,可能所有使用了该接口的微服务都需要做调整;

  • 重复劳动,很多服务可能都会使用到相同的功能。而这个功能并没有达到分解为一个微服务的程度,这个时候,可能各个服务都会开发这一功能,导致代码重复。

  • 分布式,由于会把不同服务部署在不同机器上,那么对于这些服务的调用、容错、网络延迟、分布式事务等等都是一个很大的挑战,当然微服务不一定全部都是部署在不同服务器上

3.5.7RPC调用和RESTful API两者之间的区别

通过RPC或者RESTful API都可以实现微服务,区别就在于

  • TCP的支持保持连接,当调用服务的时候不需要每次都进行三次握手才实现。从性能和网络消耗来说RPC都具备了很好的优势。

  • RESTful API 基于HTTP的,也就是说每次调用服务都需要进行三次握手建立起通信才可以实现调用,当我们的并发量高的时候这就会浪费很多带宽资源

  • 服务对外的话采用RESTful API会比RPC更具备优势,因此看自己团队的服务是对内还是对外

3.6rpc实现首页显示

新建一个rpm/demo.php
在这里插入图片描述
默认展示就是这样
在这里插入图片描述
用rpc方式实现首页展示,分别把头部和身体和尾部都分别用一个服务给展示出来

为每个结构创建一个php文件
在这里插入图片描述

每个结构文件中都开启服务然后对应的内容,注意:端口要不一样
依次是9501,9502,9503
为了方便测试,这里配置参数,设置成守护进程,这样开启服务的时候就不会占用黑窗口
在这里插入图片描述
开启前把之前所有的服务杀死

pkill php

都清理干净了,把服务开启
在这里插入图片描述
在这里插入图片描述
编写一个调用文件,来分别调用这个三个服务
在这里插入图片描述

<?php
$socket1 = stream_socket_client('tcp://xxx.xxx.xxx.xxx:9501',$errno,$errstr,30);
//发送数据
fwrite($socket1,"我是原生php的tcp客户端");
//接受数据
$header = fread($socket1,9000);
//关闭
fclose($socket1);

$socket2 = stream_socket_client('tcp://xxx.xxx.xxx.xxx:9502',$errno,$errstr,30);
//发送数据
fwrite($socket2,"我是原生php的tcp客户端");
//接受数据
$main = fread($socket2,9000);
//关闭
fclose($socket2);

$socket3 = stream_socket_client('tcp://xxx.xxx.xxx.xxx:9503',$errno,$errstr,30);
//发送数据
fwrite($socket3,"我是原生php的tcp客户端");
//接受数据
$footer = fread($socket3,9000);
//关闭
fclose($socket3);

echo $header;
echo $main;
echo $footer;

访问的效果还是一样的,但是实际上已经分别调用了三个服务
在这里插入图片描述

在这里插入图片描述

3.7tp实现rpc调用

thinkphp框架改名Order模拟一个模块服务,同时准备一个order.php用于开启服务

在这里插入图片描述

找到线上框架的路径
在这里插入图片描述
修改框架路径,同时注意端口号
在这里插入图片描述

代码片段


```bash
<?php
//构建服务
$server = new \Swoole\Server('0.0.0.0', 9502);
//配置
$server->set(
    [
        //守护进程
        'daemonize'=>1,
        //启动worker进程的数量
        'worker_num' => 1,
    ]
);
$server->on(
    'Receive',
    function (swoole_server $server, int $fd, int $reactor_id, string $data) {


        static $import = true;
        if ($import) {
            define('APP_PATH', '/wwwdata/rpc/Order/application'); //框架目录
            define('RPC_RUN', true);



            $path = '/wwwdata/rpc/Order/thinkphp/base.php'; //base文件所在路径
            include $path;
        }
        $import = false;
        $app = new \think\App();
        $ret = $app->run();
        $server->send($fd, $ret);

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



开启服务,开启前记得先把之前的服务全部杀死 

```bash
pkill php

在这里插入图片描述
编写一个客户端tp_client.php用于连接测试

<?php
$socket = stream_socket_client('tcp://xxx.xx.xxx.xxx:9502',$errno,$errstr,10);
//发送数据
fwrite($socket,"我是原生php的tcp客户端");
//接受数据
$body = fread($socket,9000);
//关闭
fclose($socket);

echo $body;

在这里插入图片描述
成功调用tp框架
在这里插入图片描述
现在这个是写死的,因为tp框架默认就是访问inex/index/index,我们如何访问不同的模块控制器和方法呢?那就需要传递参数,实际开发中,这种调用微服务的方式,不会通过地址栏去调用,而是规定首页需要调用哪些服务,商品页需要访问哪些服务等等,为了方便测试这里我们通过地址栏方式访问实现访问框架中不同的方法

在框架中新建一个方法
在这里插入图片描述

在客户端中发送path_info给服务器

在这里插入图片描述
就是发送这一段
在这里插入图片描述

服务器中接受传递给框架
在之前的代码上加上这一段就可了

 $_REQUEST['argv_rpc'] = $data;

在这里插入图片描述
总结:在实际开发中,每个微服务应该返回json格式的数据,而不是输出html,这里只是为了学习测试。

4.搭建Web服务器

4.1关于性能

这是官网截图

https://wiki.swoole.com/#/question/swoole?id=%e6%80%a7%e8%83%bd%e9%97%ae%e9%a2%98

在这里插入图片描述

4.2构建web服务器

在这里插入图片描述

<?php
$http = new Swoole\Http\Server('0.0.0.0', 9501);


//配置(非必须)
$http->set(
    [
        //启动worker进程的数量
        'worker_num' => 1,
    ]
);
//事件监听,这个和node.js一样的。也是request
$http->on('request', function ($request, $response) {

    //可以修改状态码
    $response->status(404);
    //响应头,告诉浏览器是utf8,否则中文乱码
    $response->header("Content-Type", "text/html; charset=utf-8");
    //响应的内容
    $response->end("你好,世界");
});

$http->start();

访问
在这里插入图片描述
使用ab工具进行压测,新建一个会话用于测试
先尝试ping ip能否通信

在这里插入图片描述
测试端口是否能通,因为端口可能被防火墙规则给挡住了
可以通过 telnet工具进行测试

在这里插入图片描述
telnet: connect to address xx.xxx.xxx.xx: Connection refused
telnet:连接到地址xx.xxx.xxx.xx:连接被拒绝

当然我们用的最多的是 nc指令,因为telnet指令还要退出一下,比较麻烦

# nc  -v  -w 10 %IP%   -z  %PORT%

-v  显示指令执行过程。
-w  <超时秒数>   设置等待连线的时间。
-u  表示使用UDP协议
-z  使用0输入/输出模式,只在扫描通信端口时使用

在这里插入图片描述
要使用ab测试工具需要

yum install -y httpd-tools
ab -c100 -n1000 -k url地址
-c 并发的人数
-n 总的请求次数

-n 测试会话中所执行的请求个数,默认仅执行一个请求
-c 一次产生的请求个数,即同一时间发出多少个请求,默认为一次一个
-t 测试所进行的最大秒数,默认为无时间限制....其内部隐含值是[-n 50000],它可以使对服务器的测试限制在一个固定的总时间以内
-p 包含了需要POST的数据的文件
-T POST数据所使用的Content-type头信息
-v 设置显示信息的详细程度
-w 以HTML表格的形式输出结果,默认是白色背景的两列宽度的一张表
-i 以HTML表格的形式输出结果,默认是白色背景的两列宽度的一张表
-x 设置<table>属性的字符串,此属性被填入<table 这里>
-y 设置<tr>属性的字符串
-z 设置<td>属性的字符串
-C 对请求附加一个Cookie行,其典型形式是name=value的参数对,此参数可以重复
-H 对请求附加额外的头信息,此参数的典型形式是一个有效的头信息行,其中包含了以冒号分隔的字段和值的对("Accept-Encoding: zip/zop;8bit")
-A HTTP验证,用冒号:分隔传递用户名及密码
-P 无论服务器是否需要(即是否发送了401认证需求代码),此字符串都会被发送
-X 对请求使用代理服务器
-V 显示版本号并退出
-k 启用HTTP KeepAlive功能,即在一个HTTP会话中执行多个请求,默认为不启用KeepAlive功能
-d 不显示"percentage served within XX [ms] table"的消息(为以前的版本提供支持)
-S 不显示中值和标准背离值,且均值和中值为标准背离值的1到2倍时,也不显示警告或出错信息,默认会显示最小值/均值/最大值等(为以前的版本提供支持)
-g 把所有测试结果写入一个'gnuplot'或者TSV(以Tab分隔的)文件
-e 产生一个以逗号分隔的(CSV)文件,其中包含了处理每个相应百分比的请求所需要(从1%到100%)的相应百分比的(以微妙为单位)时间
-h 显示使用方法
-k 发送keep-alive指令到服务器端

在这里插入图片描述
在这里插入图片描述

4.3静态服务器

在这里插入图片描述
静态目录要配置正确
在这里插入图片描述

<?php
$http = new Swoole\Http\Server('0.0.0.0', 9501);


//配置(非必须)
$http->set(
    [
        //启动worker进程的数量
        'worker_num' => 1,
        //静态资源配置选项  项目中的静态资源:html htm css js 图片 视频 等
        'document_root'=>'/wwwdata/web',
        'enable_static_handler'=>true
    ]
);
//事件监听,这个和node.js一样的。也是request
$http->on('request', function ($request, $response) {

    //可以修改状态码
    $response->status(404);
    //响应头,告诉浏览器是utf8,否则中文乱码
    $response->header("Content-Type", "text/html; charset=utf-8");
    //响应的内容
    $response->end("你好,世界");
});

$http->start();

在这里插入图片描述
如果换成这样访问又访问不到了
在这里插入图片描述
只需要修改配置直接写到根目录即可
在这里插入图片描述
就好比你打开一个目录后,可以随意访问里面的目录或者文件一样
在这里插入图片描述

在这里插入图片描述

4.4动态服务器

如果动态解析php文件呢?
我们新建一个web/api.php文件
在这里插入图片描述


<?php
$arr = ['id'=>1,'name'=>'Jack'];

echo json_encode($arr,JSON_UNESCAPED_UNICODE);




同时新建一个04_http_server.php创建http服务

打印一下客户端的数据信息
在这里插入图片描述
返回结果
在这里插入图片描述
获取到几个重要的信息
在这里插入图片描述
我们知道 php是一个服务器端执行的语言,实际上其实就是客户端发送地址服务器接收地址,找到本地对应的php文件解析然后返回回去。

在这里插入图片描述

<?php
$http = new Swoole\Http\Server('0.0.0.0', 9501);


//配置(非必须)
$http->set(
    [
        //启动worker进程的数量
        'worker_num' => 1,
        //静态资源配置选项  项目中的静态资源:html htm css js 图片 视频 等
        'document_root'=>'/wwwdata',
        'enable_static_handler'=>true
    ]
);
//事件监听,这个和node.js一样的。也是request
$http->on('request', function ($request, $response) {

    //查看客户端的信息
//    var_dump($request);
//获取客户端请求的文件路径
    $req_file  = $request->server['request_uri'];
    //指定真实的文件路径
    $filepath = __DIR__.'/web/'.$req_file;



    //状态码
    $status = 404;
    //返回数据  JSON_UNESCAPED_UNICODE:是php5.4提供的解决中文乱码问题
    $ret = json_encode(['status'=>1000,'msg'=>'没数据'],JSON_UNESCAPED_UNICODE);

    //判断文件是否存在
    if(file_exists($filepath)){
        //文件存在
        $status = 200;
        //为了不让结果返回到服务端上,把结果放到缓冲区
        //开启缓冲区
        ob_start();
        include $filepath;
        //读取缓冲区的数据
        $ret = ob_get_contents();
        //清空缓冲区
        ob_clean();
    }

    //可以修改状态码
    $response->status($status);
    //响应头,告诉浏览器是utf8,否则中文乱码 这里返回的是json格式
     $response->header("Content-Type", "application/json; charset=utf-8");
    //响应的内容
    $response->end($ret);
});

$http->start();

成功访问
在这里插入图片描述
接收客户端传递过来的数据

用apache的时候正常这样是可以接收到数据的
在这里插入图片描述
封装$_get $_post $_files数据的获取
在这里插入图片描述

        $_GET = $request->get;
        $_POST = $request->post;
        $_FILES = $request->files;

成功接收到数据
在这里插入图片描述

5.搭建websocket服务

1.介绍

WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。

WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
websocket解决服务器端与客户端即时通信的问题。
协议名:ws 加密通信 wss 通信成功 状态码 101

单工:对讲机
双工:打电话
http2.0收录WebSocket

2.浏览器支持

从ie11开始都支持
对于ie11以前的可以用下面这个库来解决

https://socket.io/

3.html5中websocketApi

var ws = new WebSocket("ws://localhost:9501");

3.1Websocket事件

事件事件处理程序描述
openSocket.onopen连接建立时触发
messageSocket.onmessage客户端接收服务端数据时触发
errorSocket.onerror通信发生错误时触发
closeSocket.onclose连接关闭时触发

3.2WebSocket 方法

方法描述
Socket.send()使用连接发送数据
Socket.close()关闭连接

3.3swoole实现websocket服务

WebSocket\Server 继承自 Http\Server
新建一个ws_server.php
在这里插入图片描述

<?php
//创建WebSocket Server对象,监听0.0.0.0:9502端口
$ws = new Swoole\WebSocket\Server('0.0.0.0', 9502);

//监听WebSocket连接打开事件
$ws->on('open', function ($ws, $request) {
    $ws->push($request->fd, "欢迎连接我们的聊天室\n");
});

//监听WebSocket消息事件(必不可少)
$ws->on('message', function ($ws, $frame) {
    //服务端给客户端发送消息
    $ws->push($frame->fd, "server: {$frame->data}");
});

//监听WebSocket连接关闭事件
$ws->on('close', function ($ws, $fd) {
    echo "client-{$fd} is closed\n";
});

$ws->start();

3.4mvp方式实现一个聊天功能

新建一个ws.htmlhtml5文件用来连接websoket
在这里插入图片描述

<!doctype html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>

<script>
    //客户端连接websoket服务
    const ws = new WebSocket('ws://xx.xxx.xxx.xx:9502');

    //事件监听
    //建立链接时的监听
    ws.onopen = ()=>{
        console.log("链接建立");
    }
    //接受消息事件
    // ws.onmessage =  evt =>{
    //     console.log(evt)
    // }
    //解构赋值
    ws.onmessage =  ({data}) =>{
        console.log(data)
    }
    //关闭事件
    ws.onclose = ()=>{
        console.log("服务器断开了");
    }
</script>
</body>
</html>

刷新页面就会触发事件
在这里插入图片描述

同时每刷新一次则会重新连接一次
在这里插入图片描述

修改ws.html构建输入框用于发送信息
在这里插入图片描述

<div id="msglist"></div>
<input type="text" id="msg" placeholder="请输入消息内容">
<button id="send">发送消息</button>



    //mvp方式  dom操作
    //获取消息显示
    let msglist = document.querySelector('#msglist');
    //获取输入框
    let msg = document.querySelector('#msg');
    //获取点击按钮
    let send = document.querySelector('#send');
    //点击事件
    send.onclick = ()=>{
        //获取消息框中的内容
        let data = msg.value;
        //清空输入框
        msg.value  = '';

        console.log(data);



    }

在这里插入图片描述
向服务端发送消息
在这里插入图片描述

        //发送消息到服务器
        ws.send(data);

服务端响应数据
在这里插入图片描述
把服务器返回的内容显示到页面上去
在这里插入图片描述

        //生成dom
        let p = document.createElement('p');
        //赋值
        p.innerText = data;
        //追加到msglist
        msglist.appendChild(p);

在这里插入图片描述
全部ws.html代码

<!doctype html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>

<div id="msglist"></div>
<input type="text" id="msg" placeholder="请输入消息内容">
<button id="send">发送消息</button>

<script>
    //mvp方式  dom操作
    //获取消息显示
    let msglist = document.querySelector('#msglist');
    //获取输入框
    let msg = document.querySelector('#msg');
    //获取点击按钮
    let send = document.querySelector('#send');
    //点击事件
    send.onclick = ()=>{
        //获取消息框中的内容
        let data = msg.value;
        //清空输入框
        msg.value  = '';

        // console.log(data);
        //发送消息到服务器
        ws.send(data);



    }

    //客户端连接websoket服务
    const ws = new WebSocket('ws://42.194.192.22:9502');

    //事件监听
    //建立链接时的监听
    ws.onopen = ()=>{
        console.log("链接建立");
    }
    //接受消息事件
    // ws.onmessage =  evt =>{
    //     console.log(evt)
    // }
    //解构赋值
    ws.onmessage =  ({data}) =>{
        //生成dom
        let p = document.createElement('p');
        //赋值
        p.innerText = data;
        //追加到msglist
        msglist.appendChild(p);

    }
    //关闭事件
    ws.onclose = ()=>{
        console.log("服务器断开了");
    }
</script>
</body>
</html>

3.5mvvm方式实现一个聊天室

新建一个caht/chat.php来创建websoket服务

然后为了美化,我们把准备好的静态聊天室的静态html文件拖进去
在这里插入图片描述
在这里插入图片描述
访问其中的web.html
在这里插入图片描述
引入vue
在这里插入图片描述
在这里插入图片描述

给发送按钮绑定事件,并发送消息给服务器
在这里插入图片描述
在这里插入图片描述
修改caht/chat.php让其分辨是自己还是它人,并返回json格式数据
在这里插入图片描述

        //客户端消息
        $data = $frame->data;
        $ret['data'] = $data;
        //$ws->connections 所有已经连接过的客户端
        //广播群发
        foreach ($ws->connections as $client){
            //判断是否是客户端自己本人
            if($frame->fd  == $client){
                $ret['style'] = 'bubble me';
            }else{
                $ret['style'] = 'bubble you';
            }
            //传递json给客户端
            @$ws->push($client, json_encode($ret,JSON_UNESCAPED_UNICODE));

web.html中接受服务器返回的数据赋值给vue中的msglist消息列表数组
在这里插入图片描述

		//接受消息事件
		ws.onmessage =  ({data}) =>{
			//返回的是json字符串
			let json = JSON.parse(data);
			//使用vue提供的一个变异方法
			app.msglist.push(json);
		}
		data:{
				msg: "",
				//消息列表
				msglist:[]
			},

遍历数据到页面上template标签
在这里插入图片描述

				<template v-for="item in msglist">

					<div :class="item.style">
						{{item.data}}
					</div>

					</template>

重新开启websoke服务
在这里插入图片描述
开启两个标签用于测试
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

vue实现一下通过enter回车发送消息

在这里插入图片描述

web.html完整代码


<!DOCTYPE html>
<html lang="zh" >

<head>
	<meta charset="UTF-8">
	<title>在线聊天室</title>
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<link rel="stylesheet" href="css/reset.min.css">
	<link rel="stylesheet" href="css/style.css">
</head>

<body>
	<div class="wrapper">
		<div class="container">
			<div class="left">
				<div class="top"> 在线人员 </div>
				<ul class="people">
					<li class="person" data-chat="person1">
						<img src="img/thomas.jpg" alt="" />
						<span class="name">张三</span>
						<span class="time">10:09</span>
					</li>
					<li class="person" data-chat="person2">
						<img src="img/dog.png" alt="" />
						<span class="name">李四</span>
						<span class="time">10:44</span>
					</li>
					<li class="person" data-chat="person3">
						<img src="img/louis-ck.jpeg" alt="" />
						<span class="name">王五</span>
						<span class="time">10:50</span>
					</li>
				</ul>
			</div>
			<div class="right">
				<div class="top"><span><span class="name">聊天室</span></span></div>
				<div class="chat" data-chat="person2">
					<template v-for="item in msglist">

					<div :class="item.style">
						{{item.data}}
					</div>

					</template>
				</div>
				
				<div class="write">
					<input type="text" v-model="msg" placeholder="输入内容" @keydown.enter="send" />
<!--					vue修饰符-->
					<a @click.prevent="send" class="write-link send"></a>
				</div>
			</div>
		</div>
	</div>

	<script  src="js/index.js"></script>
	<script  src="js/vue.js"></script>



	<script>
		//客户端连接websoket服务
		const ws = new WebSocket('ws://xx.xxx.xxx.xx:9502');
		//建立链接时的监听
		ws.onopen = ()=>{
			console.log("链接建立");
		}
		//接受消息事件
		ws.onmessage =  ({data}) =>{
			//返回的是json字符串
			let json = JSON.parse(data);
			//使用vue提供的一个变异方法
			app.msglist.push(json);
		}

		//实例化vue
		const app = new Vue({
			el:'.wrapper',
			data:{
				msg: "",
				//消息列表
				msglist:[]
			},
			methods:{
				send(){
					// evt.preventDefault();
					ws.send(this.msg);
					//发送完毕后把输入框数据清空
					this.msg = '';
				}
			}

		});

	</script>


</body>

</html>

chat/chat.php完整代码

<?php

//创建WebSocket Server对象,监听0.0.0.0:9502端口
$ws = new Swoole\WebSocket\Server('0.0.0.0', 9502);

//监听WebSocket连接打开事件
$ws->on(
    'open',
    function ($ws, $request) {
//        $ws->push($request->fd, "欢迎连接我们的聊天室\n");
    }
);

//监听WebSocket消息事件(必不可少)
$ws->on(
    'message',
    function ($ws, $frame) {

        //客户端消息
        $data = $frame->data;
        $ret['data'] = $data;
        //$ws->connections 所有已经连接过的客户端
        //广播群发
        foreach ($ws->connections as $client){
            //判断是否是客户端自己本人
            if($frame->fd  == $client){
                $ret['style'] = 'bubble me';
            }else{
                $ret['style'] = 'bubble you';
            }
            //传递json给客户端
            @$ws->push($client, json_encode($ret,JSON_UNESCAPED_UNICODE));
        }
    }
);

//监听WebSocket连接关闭事件
$ws->on(
    'close',
    function ($ws, $fd) {
        echo "client-{$fd} is closed\n";
    }
);

$ws->start();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值