workerman实现PHP实时消息推送,getwayworker知足你的项目需求









为了带领你们深刻学习workerman以及getwayworker,让你们迅速在本身项目中使用workerman,使用Websocket协议,实现长链接,摆脱长轮询,详细完整的讲解如何利用workerman开发即时通信,而且部署到本身的项目里以及运行在Linux系统里面。 无论你的项目是基于ThinkPHP仍是laravel或者yii,甚至微擎,无论你是pc端应用仍是移动端应用,均可以在本身的项目里面完整的实现通信模块。
首先了解一下通信协议:

TCP/IP
TCP/IP是个协议组,可分为三个层次:网络层、传输层和应用层。
在网络层有IP协议、ICMP协议、ARP协议、RARP协议和BOOTP协议。
在传输层中有TCP协议与UDP协议。


在应用层有:
TCP包括FTP、HTTP、TELNET、SMTP等协议
UDP包括DNS、TFTP等协议
html

短链接
链接->传输数据->关闭链接
HTTP是无状态的,浏览器和服务器每进行一次HTTP操做,就创建一次链接,但任务结束就中断链接。
也能够这样说:短链接是指SOCKET链接后发送后接收完数据后立刻断开链接。

 
长链接
链接->传输数据->保持链接 -> 传输数据-> 。。。->关闭链接。
长链接指创建SOCKET链接后不论是否使用都保持链接,但安全性较差。


http的长链接
HTTP也能够创建长链接的,使用Connection:keep-alive,HTTP 1.1默认进行持久链接。HTTP1.1和HTTP1.0相比较而言,最大的区别就是增长了持久链接支持(貌似最新的 http1.0 能够显示的指定 keep-alive),但仍是无状态的,或者说是不能够信任的。
 
何时用长链接,短链接?
长链接多用于操做频繁,点对点的通信,并且链接数不能太多状况,。每一个TCP链接都须要三步握手,这须要时间,若是每一个操做都是先链接,再操做的话那么处理速度会下降不少,因此每一个操做完后都不断开,次处理时直接发送数据包就OK了,不用创建TCP链接。例如:数据库的链接用长链接, 若是用短链接频繁的通讯会形成socket错误,并且频繁的socket 建立也是对资源的浪费。
 
而像WEB网站的http服务通常都用短连接,由于长链接对于服务端来讲会耗费必定的资源,而像WEB网站这么频繁的成千上万甚至上亿客户端的链接用短链接会更省一些资源,若是用长链接,并且同时有成千上万的用户,若是每一个用户都占用一个链接的话,那可想而知吧。因此并发量大,但每一个用户无需频繁操做状况下需用短连好。


workerman是啥?
Workerman是一款纯PHP开发的开源高性能的PHP socket 服务器框架。被普遍的用于手机app、移动通信,微信小程序,手游服务端、网络游戏、PHP聊天室、硬件通信、智能家居、车联网、物联网等领域的开发。支持TCP长链接,支持Websocket、HTTP等协议,支持自定义协议。拥有异步Mysql、异步Redis、异步Http、异步消息队列等众多高性能组件。

开始步入正题:为了达到实时通信,不少时候咱们采用了ajax轮询机制,后面能够采用workerman方式来实现,项目也是tp写的,官方手册这么说到
与其它mvc框架结合建议以上图的方式(ThinkPHP为例):
一、ThinkPHP与Workerman是两个独立的系统,独立部署(可部署在不一样服务器),互不干扰。
二、ThinkPHP以HTTP协议提供网页页面在浏览器渲染展现。
三、ThinkPHP提供的页面的js发起websocket链接,链接workerman
四、链接后给Workerman发送一个数据包(包含用户名密码或者某种token串)用于验证websocket链接属于哪一个用户。
五、仅在ThinkPHP须要向浏览器推送数据时,才调用workerman的socket接口推送数据。
六、其他请求仍是按照本来ThinkPHP的HTTP方式调用处理。


总结:
把Workerman做为一个能够向浏览器推送的通道,仅仅在须要向浏览器推送数据时才调用Workerman接口完成推送。业务逻辑所有在ThinkPHP中完成。
ok,到这里,把workerman容器跑起来,注意这里是CLI模式运行


而后再咱们项目接收信息中这么写,附上代码laravel

<script>

    // 链接服务端
    var socket = io('http://127.0.0.1:2120');

    // uid能够是本身网站的用户id,以便针对uid推送
    uid = 123;

    // socket链接后以uid登陆
    socket.on('connect'function(){
        socket.emit('login', uid);
    });

    // 后端推送来消息时
    socket.on('new_msg'function(msg){
        console.log("收到消息:"+msg);  //本身业务逻辑处理
    });

</script>

接着,咱们在用户向用户发送信息的时候添加web

// 指明给谁推送,为空表示向全部在线用户推送
$to_uid = "123";

// 推送的url地址
$push_api_url = "http://127.0.0.1:2121/";

$post_data = array(
   "type" => "publish",
   "content" => "数据",
   "to" => $to_uid, 
);

$ch = curl_init ();
curl_setopt ( $ch, CURLOPT_URL, $push_api_url );
curl_setopt ( $ch, CURLOPT_POST, 1 );
curl_setopt ( $ch, CURLOPT_HEADER, 0 );
curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, 1 );
curl_setopt ( $ch, CURLOPT_POSTFIELDS, $post_data );
curl_setopt ($ch, CURLOPT_HTTPHEADER, array("Expect:"));
$return = curl_exec ( $ch );
curl_close ( $ch );
var_export($return);

其中,workerman里面的推送核心代码实现ajax

// 全局数组保存uid在线数据
$uidConnectionMap = array();

// 记录最后一次广播的在线用户数
$last_online_count = 0;

// PHPSocketIO服务
$sender_io = new SocketIO(2120);

// 客户端发起链接事件时,设置链接socket的各类事件回调
// 当$sender_io启动后监听一个http端口,经过这个端口能够给任意uid或者全部uid推送数据
$sender_io->on('workerStart'function(){

    // 监听一个http端口
    $inner_http_worker = new Worker('http://0.0.0.0:2121');

    // 当http客户端发来数据时触发
    $inner_http_worker->onMessage = function($http_connection, $data){
        global $uidConnectionMap;
        $_POST = $_POST ? $_POST : $_GET;

        // 推送数据的url格式 type=publish&to=uid&content=xxxx
        switch(@$_POST['type']){

            case 'publish':

                global $sender_io;
                $to = @$_POST['to'];
                $_POST['content'] = htmlspecialchars(@$_POST['content']);

                // 有指定uid则向uid所在socket组发送数据
                if($to){
                    $sender_io->to($to)->emit('new_msg', $_POST['content']);

                // 不然向全部uid推送数据
                }else{
                    $sender_io->emit('new_msg', @$_POST['content']);

                }

                // http接口返回,若是用户离线socket返回fail
                if($to && !isset($uidConnectionMap[$to])){
                    return $http_connection->send('offline');

                }else{
                    return $http_connection->send('ok');

                }
        }
        return $http_connection->send('fail');
    };
});


if(!defined('GLOBAL_START')) {
    Worker::runAll();
}


本文分享自微信公众号 - Laravel技术社区(Laravel360)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。sql

相关文章
相关标签/搜索