× 警告!旧版文档已经暂停维护,请查看新版文档。点击前往新版文档

WebSocket\Server

1.7.9增加了内置的WebSocket服务器支持,通过几行PHP代码就可以写出一个异步非阻塞多进程的WebSocket服务器。

$server = new Swoole\WebSocket\Server("0.0.0.0", 9501);

$server->on('open', function (Swoole\WebSocket\Server $server, $request) {
    echo "server: handshake success with fd{$request->fd}\n";
});

$server->on('message', function (Swoole\WebSocket\Server $server, $frame) {
    echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n";
    $server->push($frame->fd, "this is server");
});

$server->on('close', function ($ser, $fd) {
    echo "client {$fd} closed\n";
});

$server->start();

onRequest回调

WebSocket\Server 继承自 Http\Server

  • 设置了onRequest回调,WebSocket\Server也可以同时作为http服务器
  • 未设置onRequest回调,WebSocket\Server收到http请求后会返回http 400错误页面
  • 如果想通过接收http触发所有websocket的推送,需要注意作用域的问题,面向过程请使用globalWebSocket\Server进行引用,面向对象可以把WebSocket\Server设置成一个成员属性

1、面向过程代码

$server = new Swoole\WebSocket\Server("0.0.0.0", 9501);
$server->on('open', function (Swoole\WebSocket\Server $server, $request) {
        echo "server: handshake success with fd{$request->fd}\n";
    });
$server->on('message', function (Swoole\WebSocket\Server $server, $frame) {
        echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n";
        $server->push($frame->fd, "this is server");
    });
$server->on('close', function ($ser, $fd) {
        echo "client {$fd} closed\n";
    });
$server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) {
    global $server;//调用外部的server
    // $server->connections 遍历所有websocket连接用户的fd,给所有用户推送
    foreach ($server->connections as $fd) {
        // 需要先判断是否是正确的websocket连接,否则有可能会push失败
        if ($server->isEstablished($fd)) {
            $server->push($fd, $request->get['message']);
        }
    }
});
$server->start();

2、面向对象代码

class WebsocketTest {
    public $server;
    public function __construct() {
        $this->server = new Swoole\WebSocket\Server("0.0.0.0", 9501);
        $this->server->on('open', function (swoole_websocket_server $server, $request) {
            echo "server: handshake success with fd{$request->fd}\n";
        });
        $this->server->on('message', function (Swoole\WebSocket\Server $server, $frame) {
            echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n";
            $server->push($frame->fd, "this is server");
        });
        $this->server->on('close', function ($ser, $fd) {
            echo "client {$fd} closed\n";
        });
        $this->server->on('request', function ($request, $response) {
            // 接收http请求从get获取message参数的值,给用户推送
            // $this->server->connections 遍历所有websocket连接用户的fd,给所有用户推送
            foreach ($this->server->connections as $fd) {
                // 需要先判断是否是正确的websocket连接,否则有可能会push失败
                if ($this->server->isEstablished($fd)) {
                    $this->server->push($fd, $request->get['message']);
                }
            }
        });
        $this->server->start();
    }
}
new WebsocketTest();

客户端

  • Chrome/Firefox/高版本IE/Safari等浏览器内置了JS语言的WebSocket客户端
  • 微信小程序开发框架内置的WebSocket客户端
  • 异步的PHP程序中可以使用Swoole\Http\Client作为WebSocket客户端
  • apache/php-fpm或其他同步阻塞的PHP程序中可以使用swoole/framework提供的同步WebSocket客户端
  • WebSocket客户端不能与WebSocket服务器通信

  • 张良晖

    message和close事件都是server上的?不应该是connection上的事件吗

  • nfer

    能否也对应的开发一个 swoole_websocket_client

  • 霸气千秋

    源码的 example/websocket 目录下有个 php 实现的 WebSocketClient

  • Just_Fee1ing

    PHP Fatal error: swoole_server::on(): Unknown event types[message] 就我一个人报这个error? swoole版本1.7.17

  • guoerwei

    Argument 2 passed to {closure}() must be an instance of swoole_http_request, integer given on.('open')里面第二个参数传进来的是一个整数…

  • 小马哥

    我怎么就 new swoole_server("127.0.0.1", 9501); 能连接上, new swoole_http_server("127.0.0.1", 9501); 和 new swoole_websocket_server("0.0.0.0", 9501); 都链接不上 ,版本 1.7.19

  • 爱好者

    有没有函数判断客户端还正在链接呢

  • 阿强

    今天试难成功,连上了,按照github上的例子连了一下,服务器端的信息: PHP Notice: Undefined property: swoole_http_request::$get in /var/www/html/ws.php on line 5 int(1) NULL array(9) { ["request_method"]=> string(3) "GET" ["request_uri"]=> string(1) "/" ["path_info"]=> string(1) "/" ["request_time"]=> int(1447532507) ["server_port"]=> int(9502) ["remote_port"]=> int(53188) ["remote_addr"]=> string(13) "192.168.1.102" ["server_protocol"]=> string(8) "HTTP/1.1" ["server_software"]=> string(18) "swoole-http-server" }

  • xcwenn@qq.com

    android 哪个websocket lib 合适?!

  • 他的流年

    您好,请问我如何在服务器主动关闭一个连接呢?

  • 他的流年

    不好意思,问题已解决。是我看得不够清楚,由继承关系直接调用close函数就可以关闭连接

  • seven

    我也想知道 怎么主动关闭一个连接 没看到你的什么意思 求大大给个代码案例看呢

  • 孙佳兵

    为什么我在客户端的js方法 socket.onopen能够相应,socket.opmessage响应不了?

  • jimmy

    swoole_websocket_server 的工作进程数怎么控制?

  • heice

    在吗?请问这个要怎么实现? 接收到redis的数据又把数据从websocket传出去 var app = require('http').createServer(handler); var io = require('socket.io')(app);

    var Redis = require('ioredis'); var redis = new Redis('6379','127.0.0.1');

    app.listen(6001, function() { console.log('Server is running!'); });

    function handler(req, res) { res.writeHead(200); res.end(''); }

    io.on('connection', function(socket) { // console.log('connected'); });

    redis.psubscribe('*', function(err, count) { // console.log('count'); });

    redis.on('pmessage', function(subscribed, channel, message) { console.log(subscribed); console.log(channel); console.log(message); message = JSON.parse(message); io.emit(channel + ':' + message.event, message.data); });

  • 骑驴泡美妞

    您好,可以问下是怎么解决的么?

  • 别怕

    页面直接写上这段js就行吗?为什么我在命令行运行之后 打开有这段js的页面什么也不显示?如果直接输入地址加上端口号的就出来一个websocket已经通电的话

  • 谭少民-倬余

    请问如何分房间?

  • 猫爸爸

    求助大大们,websocket 在IE11下上传超过50M的文件时提示,报文过长然后自动断开了,这是BUG还是我方式不对?chrome没有相关的问题,前端使用FileReader.readAsArrayBuffer()读取文件 WARNING swProtocol_recv_check_length: package is too big, remote_addr=192.168.5.1:52151, length=50772334. [/documents/bin/swoole-src-master/src/network/Port.c:340@swPort_onRead_check_length]Close Event.FD=32|From=0 client 1 quit

  • 谭少民-倬余

    请问单个服务端最大支持多少链接数?

  • 张三丰

    参照max_connection配置

  • 张三丰

    分房间应该是自己业务层去自己实现

  • d7788250

    Swoole\Http\Client作为WebSocket客户端有例程吗?

  • 快捷方式

    server: handshake success with fd1 receive from 1:hello yii,opcode:1,fin:1 client 1 closed [2017-04-20 15:14:42 #11571.2] WARNING swReactorThread_send: connection#18 is closed by client. server: handshake success with fd2 receive from 2:hello yii,opcode:1,fin:1 client 2 closed [2017-04-20 15:14:45 #11571.2] WARNING swReactorThread_send: connection#18 is closed by client. server: handshake success with fd3 receive from 3:hello yii,opcode:1,fin:1 client 3 closed [2017-04-20 15:14:48 #11571.2] WARNING swReactorThread_send: connection#18 is closed by client. server: handshake success with fd4 receive from 4:hello yii,opcode:1,fin:1 client 4 closed [2017-04-20 15:14:51 #11571.2] WARNING swReactorThread_send: connection#18 is closed by client. server: handshake success with fd5 receive from 5:hello yii,opcode:1,fin:1 client 5 closed [2017-04-20 15:14:53 #11571.2] WARNING swReactorThread_send: connection#18 is closed by client.

  • 快捷方式

    $webSocketClient = new \app\exts\WebSocketClient('121.11.71.33', 8988);

        $webSocketClient->connect();
    
        $webSocketClient->send('hello yii');
    
        $webSocketClient->close();
    

  • 快捷方式

    $server = new \swoole_websocket_server("121.11.71.33", 8988);

        $server->on('open', function (\swoole_websocket_server $server, $request) {
            echo "server: handshake success with fd{$request->fd}\n";
        });
    
        $server->on('message', function (\swoole_websocket_server $server, $frame) {
            echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n";
            $server->push($frame->fd, "this is server");
        });
    
        $server->on('close', function ($ser, $fd) {
            echo "client {$fd} closed\n";
        });
    
        $server->start();
    

  • 快捷方式

    为什么close之后还有warning,这是哪里给clien发送信息了吗

  • 王子

    请问如何使用websocket做分布式转发服务端?貌似swoole的websocket只有server,没有client

  • CodingMan

    failed: Error in connection establishment: net::ERR_CONNECTION_TIMED_OUT 两台服务器,一台能成功,另外一台运行了websocket的例子。浏览器端js连不上报这个错,谁碰到过啊

  • wHiLe(1)追;

            if(!($roomMember = RoomMembers::find($userid)))
            {
                return false;
            }
    
            $roomid = $roomMember->roomid;
    
            //座位找到了,但未找到对应的房间
            if(!($room = Room::find($roomid)))
            {
                $roomMember->delete();
                return false;
            }
    

    如果所示经过几轮异步数据请求后,RoomMembers所获取的对象是仍然是该对象本身,但是属性却变成了Room的属性了。数据在底层发生了混乱。 虽然这是业务逻辑的代码,本应该跟Swoole没有关系。但是同样用Swoole的Socket的时候,没有这个问题(其他代码完全一样,仅接入层不一样)。我是先写好Socket版本测试没问题后,打算接入小程序才尝试的Websocket。

    另外测试方面,我是自己写的机器人,Websocket几乎每次都是跑2~3轮就出现数据混乱。而Socket版本跑几个小时,没有任何问题。由于能力有限,只能根据现象猜测是Swoole的Websocket有问题。还请抽空帮忙看一下,急等接入小程序版本的游戏。多谢!

  • wHiLe(1)追;

    继续楼上:把worker_num 设置为1后,没有这个问题。但是这样不合理啊。 Socket的时候worker_num设置为4,也是正常工作的。 是我哪里设置有问题吗?

  • wHiLe(1)追;

    继续楼上:经过进一步测试,是在采用websocket并且worker_num>1并且并发请求同一个function资源的时候,会发生这个情况。 也已经在码云上面提交BUG了。

  • 初学者想学一下swoole,希望有人给予解答: 是不是一个类似网络棋牌游戏大厅这种网页游戏,服务端就可以用swoole_websocket_server? 客户端是用http_client还是直接js的websocket? 有没有大神给解释下swoole各服务器和客户端到底怎么用?以游戏大厅为例说一下整体系统架构。

  • phper

    请问一下,IE低版本浏览器怎样用flash去连接Websocket

  • 深圳联星美洲商旅

    峰哥,swoole_websocket_server怎么用SSL啊,找了半天没找到呢。

  • 深圳联星美洲商旅

    swoole_websocket_server怎么用SSL啊,找了半天没找到呢。哪位大神给回复一下?

  • 深圳联星美洲商旅

    继续楼上,SSL证书已经取得,就是不知道咋弄……^v^

  • 深圳联星美洲商旅

    问题解决了,还是自己回复吧。在第4个参数设置即可,如: $setConfig = [ 'ssl_key_file' => '/usr/local/nginx/conf/server.key', 'ssl_cert_file' => '/usr/local/nginx/conf/server.crt' ]; $sws = new swoole_websocket_server("0.0.0.0", 9501, SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); $sws->set($setConfig);

  • 注意:#onTask#方法中,不能push

  • 火端

    Websocket用起来很方便呀

  • 韩文博1

    设置onRequest回调后 依然 HTTP ERROR 400

  • Winds

    web_socket的server,为什么我每次onRequest后,push到客户端,server总会执行一次onClose,客户端那边我很确定是没有执行关闭操作的

  • rookie

    $ws = new swoole_websocket_server(); 这个是调用的哪里的方法啊 我好想没有这个类

  • 卿本佳人

    采用第二种方式,为神马出现这个错误。PHP Warning: swoole_websocket_server::push(): the connected client of connection[5] is not a websocket client.

  • 卿本佳人

    非WebSocket客户端不能与WebSocket服务器通信 这个意思是什么,只有客户段请求 requst 才可以么

  • xuping

    tick怎么结合websocket使用呢?

  • Heimo

    基于Swoole websocket实现的web聊天室demo https://heimo-he.github.io/2018/03/30/php-swoole-webim/

  • Heimo

    基于Swoole websocket实现的web聊天室demo源码 https://github.com/Heimo-He/webIM

  • Emotion

    3分钟搭建,基于Swoole-WebSocket的多人在线聊天简易版 https://github.com/ouhaohan8023/WebIM

  • 天智老师

    swoole搭建但聊天室确实非常简单,但是对于多聊天室,没看到手册?是临时不支持这个功能?

  • janray

    "Winds web_socket的server,为什么我每次onRequest后,push到客户端,server总会执行一次onClose,客户端那边我很确定是没有执行关闭操作的"

    关于这个问题:其实是浏览器默认会请求/favicon.ico 会触发一次onRequest 导致的,在head头里面加一句 即可屏蔽

  • Nemo

    是不是对面向对象有什么误解。。。。 写在类里就叫面相对象么。。。

  • sunnier

    有个问题啊,用了swoole第一天websocket很正常,第二天出现Invalid frame header,有没有类似的问题,谢谢

  • sunnier

    应该是进程已经挂了,我再试试

  • 寻路记

    websoket服务过一段时间就会连不上,重启服务就又可以了,不知道为什么

  • pandaguo

    PHP创建websocket服务器对象,监听0.0.0.0:9505端口 ,失败,没反应,这是怎么回事呢

  • Berzerk

    在学习中碰到什么问题希望能有人交流,加到群486583931一起探讨吧

  • バ 小丑男°乄°    ?

    为什么没有检测客户端链接类型的函数呐?

  • 拼个Léon

    能不能直接给swoole server增加对websocket的支持,而不是新弄个类——当然如果这个类是继承swoole server那到不必。如果不是继承swoole server,那么最好是swoole server直接支持websocket:收到客户端的第一条消息,判断下是不是websocket,如果是的话自动完成websocket握手,标记为websocket连接,以后收发都按照websocket协议来。这样,一个服务支持普通tcp和websocket,而上层根本不关心客户端用的啥协议。

  • 服务端开启一个服务后,如何主动关闭这个服务 ..只能手动关?

  • 【涩果】

    服务端如何主动推送给所有客户端

  • xfjpeter

    求救:用daemonize方式启动websocket服务器,为啥隔一段时间就会自动销毁进程呢?

  • 新用户(手机注册)

    为什么只能本机才能访问websocket服务端,外网的换了ip一直提示Provisional headers are shown

  • 二狗

    nginx配置一直搞不定。。。域名+端口的访问形式。谁能指点下。。

  • 2594647721

    安装上面示例,用http推送信息时,会报个错 “PHP Warning: swoole_websocket_server::push(): the connected client of connection[5] is not a websocket client.”,仔细排查了下,发现是 当http请求过来时,这个http请求会被加入到ws的客户端连接池中,当向这个httd请求连接push ws消息时自然会报错。虽然这个错误不影响程序运行,但是看着不爽 我的解决思路是:记录http请求时的fd,当广播推送时,排除掉这个fd就可以了

  • 15727370906

    $server->push($fd, $request->get['message']); 使用http request get 参数是获取不了参数的。 我通过url 传递参数,试了很久都没办法传递message

  • 18024309903

    跟楼上一个问题 但是我连request请求都没触发 请问你是怎么触发 request的?

  • java98

    onrequest 内一定要执行 $response->end(); 否则返回500

  • 18260165847

  • 18260165847

    $ws = new swoole_websocket_server("0.0.0.0", 9502);$ws->on('open', function ($ws, $request) { //var_dump($request->fd, $request->get, $request->server); //$ws->push($request->fd, "hello, welcome\n"); });$ws->on('message', function ($ws, $frame) { //echo "Message: {$frame->data}\n"; $message=json_decode($frame->data,true); $redis=new Redis(); $redis->connect('127.0.0.1',6379); if('login'==$message['message_type']){ //绑定用户id和websocket客户端id $user_id=$message['from']; $key='user_id_'.$user_id; $redis->set($key,$frame->fd);

        //绑定成功后,给客户端发送消息,提示成功
        $message['from']='server';
        $message['to']=$user_id;
        $message['message']='连接成功';
        $ws->push($frame->fd, json_encode($message));
         echo "Message to client Done!\n";
    } 
    if('chat'==$message['message_type']){
        //获取接收消息人的client_id
        $to_id=$message['to'];
        $key='user_id_'.$to_id;
        $to_client_id=$redis->get($key);
        $ws->push($to_client_id, $frame->data);
    }
    

    });$ws->on('close', function ($ws, $fd) { echo "client-{$fd} is closed\n"; });$ws->start();

  • 13913928641

    请问,time_wait一直不释放,一直增加,四天飙到一万多,该怎么解决 websocket客户端一般1500个人在线,http实时发送。 监听了9501端口,作为websocket服务器,开启了http服务,

  • 17717376862

    public function __construct()
    {
        // 配置信息
        $this->_conf = require('config.php');
        // 智能聊天文件
        $this->_chat = json_decode(file_get_contents('chat.txt'), true);
        // 服务
        $this->_serv = new Swoole\Websocket\Server($this->_conf['socket']['host'], $this->_conf['socket']['port'], SWOOLE_BASE);
        // 设置运行时参数
        $this->_serv->set($this->_conf['socket']['options']);
        // 监听连接事件
        $this->_serv->on('open', [$this, 'onOpen']);
        // 监听接收消息事件
        $this->_serv->on('message', [$this, 'onMessage']);
        // 监听关闭事件
        $this->_serv->on('close', [$this, 'onClose']);
        // 接收http请求
        $this->_serv->on('request', [$this, 'onRequest']);
        // 开启服务
        $this->_serv->start();
    
    }
    
    /**
     * 连接成功回调函数
     * 
     * @param $serv
     * @param $request
     */
    public function onOpen($serv, $request)
    {
        $this->log(__CLASS__, __FUNCTION__, __LINE__, $request);
    }
    

  • 15926395146

    用socket.io连接不上websocket,有没有大佬有解决方案的?

  • 18986008649

    swoole http或wesocket单个进程内的on->open等,会不会一个没处理完先暂停,就开启下一个? 我的理解是,on->open内的处理完,有协程等待的也要完成后,才开启下另一个on->open协程 不知道对不对? 但测试时有好像有并发的,按道理redis只要一个连接就够了,但并发一多就出现9个以上,reids不用协程版就始终只有一个符合预期

    $user = posix_getpwnam('www'); posix_setuid($user['uid']); posix_setgid($user['gid']); $redis=new Redis; $redis->pconnect('127.0.0.1', 6379); $redis->del('set'); $redis->set('redis_new',0); $pool = new RedisPool();

    //创建websocket服务器对象,监听0.0.0.0:9502端口 $ws = new swoole_websocket_server("0.0.0.0", 9502); $ws->set(array( 'max_connection'=>20000, 'worker_num' => 1, //开启两个worker进程 'max_request' => 0, //每个worker进程max request设置为3次 'dispatch_mode'=>2, //'task_worker_num'=>2,//配置异步Task进程的数量 'heartbeat_check_interval' => 1800,//5秒侦测一次心跳,一个TCP在10秒内未向服务器端发送数据,将会被切断 'heartbeat_idle_time' => 1810, 'user'=>'www', 'group'=>'www' )); //监听WebSocket连接打开事件 $ws->on('open', function ($ws, $request)use($pool) { $redis = $pool->get($ws->worker_id,$request->fd); $redis->sadd('set',$request->fd); $pool->put($redis); //$ws->push($request->fd, "open ok redis 你好".($redis->sismember('set',$request->fd)? 'ok':'fail')); echo "\n1|进程:".$ws->worker_id.'|fd:'.$request->fd.'|conns:'.count($ws->connections).'|协程:'.Swoole\Coroutine::getCid();
    });

    //监听WebSocket消息事件 $ws->on('message', function ($ws, $frame) { echo "\n2|进程:".$ws->worker_id.'|fd:'.$frame->fd.'|conns:'.count($ws->connections).'|协程:'.Swoole\Coroutine::getCid();
    });

    //监听WebSocket连接关闭事件 $ws->on('close', function ($ws, $fd)use($pool) { $redis = $pool->get($ws->worker_id,$fd); $redis->srem('set',$fd); $pool->put($redis); echo "\n3|进程:".$ws->worker_id.'|fd:'.$fd.'closed'.'|conns:'.count($ws->connections).'|协程:'.Swoole\Coroutine::getCid(); });

    $ws->start();

    class RedisPool { protected $pool;

    function __construct($size = 10000){
        $this->pool = new Swoole\Coroutine\Channel($size);
    }
    
    function put($redis){
        $this->pool->push($redis);
    }
    
    public function get($work_id,$fd){
        if($this->pool->length() > 0) {
            echo "\np|进程:".$work_id.'|fd:'.$fd.'|reids:'.$this->pool->length().'|协程:'.Swoole\Coroutine::getCid();
            return $this->pool->pop();
        }
        //无空闲连接,创建新连接
        $redis = new Swoole\Coroutine\Redis();
        do{
            $res = $redis->connect('127.0.0.1', 6379);
        }while(!$res);
        $redis->incr('redis_new');
        echo "\nc|进程:".$work_id.'|fd:'.$fd.'|reids:'.$this->pool->length().'|协程:'.Swoole\Coroutine::getCid();
        return $redis;
    }
    public function get_count(){
       return $this->pool->length();
    }
    

    }

  • 13883929936

    使用push方法向客户端发送消息报错 One or more reserved bits are on: reserved1

  • 13983087661

    66 打卡

  • 17625163979

    直接使用文档的例子,http请求会报错啊 PHP Fatal error: Uncaught Error: Call to undefined method Swoole\WebSocket\Server::isEstablished() Version => 2.0.8

  • 18561611656

    做websocket开发,可以自己搭建,也可以试试第三方的websocket框架。我近期在使用的GoEasy这个框架,在应对我们项目这种突发性的高并发需求上,表现不错。有websocket使用需求的也可以去看看,地址:https://www.goeasy.io/

  • 18561611656

    打卡