WebSocket 长连接

<?php
class SocketService
{
    private $address  = ‘localhost‘;
    private $port = 80;
    private $_sockets;
    public function __construct($address = ‘‘, $port=‘‘)
    {
        if(!empty($address)){
            $this->address = $address;
        }
        if(!empty($port)) {
            $this->port = $port;
        }
    }

    public function service(){
        //获取tcp协议号码。
        $tcp = getprotobyname("SOL_TCP");           # 获取与协议名称关联的协议号
        $sock = socket_create(AF_INET, SOCK_STREAM, $tcp);  # 创建一个套接字(通讯节点)
        socket_set_option($sock, SOL_SOCKET, SO_REUSEADDR, 1);  # 设置套接字选项
        if($sock < 0)
        {
            throw new Exception("failed to create socket: ".socket_strerror($sock)."\n");
        }
        socket_bind($sock, $this->address, $this->port);    # 绑定
        socket_listen($sock, $this->port);      # 监听套接字上的连接
        $this->_sockets = $sock;
    }

    public function run(){
        $this->service();
        $clients[] = $this->_sockets;   # 数组存储 每个socket
        # 让服务器无限获取客户端传过来的信息
        while (true){
            $changes = $clients;
            $write = NULL;
            $except = NULL;
            socket_select($changes,  $write,  $except, NULL);
            foreach ($changes as $key => $_sock){
                if($this->_sockets == $_sock){  # 判断是不是新接入的socket
                    if(($newClient = socket_accept($_sock))  === false){    # 接受新的套接字上的连接   socket_accept的作用就是接受socket_bind()所绑定的主机发过来的套接流
                        die(‘failed to accept socket: ‘.socket_strerror($_sock)."\n");  # 返回描述套接字错误的字符串
                    }
                    $line = trim(socket_read($newClient, 1024));  # 读取客户端传过来的资源,并转化为字符串     socket_read的作用就是读出socket_accept()的资源并把它转化为字符串
                    $this->handshaking($newClient, $line);
                    //获取client ip
                    socket_getpeername ($newClient, $ip);                                   # 查询给定套接字的远程端,这可能导致主机/端口或UNIX文件系统路径,具体取决于其类型。
                    $clients[$ip] = $newClient;
                } else {
                    # 读取该socket的信息,注意:第二个参数是引用传参即接收数据,第三个参数是接收数据的长度
                    $lenght = socket_recv($_sock, $buffer,  2048, 0);               # 从已连接的socket接收数据   $lenght 接收到字符串长度
                    $msg = $this->message($buffer);          # 接收到的信息
                    //在这里业务代码
                    fwrite(STDOUT, ‘Please input a argument:‘);
                    $response = trim(fgets(STDIN));
//                    $this->send($_sock, $response);                                                       # 第二个参数是获取数据    要发送的信息
                    $this->send($_sock, ‘在线‘);
                }
            }
        }
    }

    /**
     * 握手处理
     * @param $newClient socket
     * @return int  接收到的信息
     */
    public function handshaking($newClient, $line){
        $headers = array();
        $lines = preg_split("/\r\n/", $line);   # 通过一个正则表达式分隔字符串。
        foreach($lines as $line)
        {
            $line = chop($line);    # 移除字符串右端的空白字符或其他预定义字符
            if(preg_match(‘/\A(\S+): (.*)\z/‘, $line, $matches))
            {
                $headers[$matches[1]] = $matches[2];
            }
        }
        $secKey = $headers[‘Sec-WebSocket-Key‘];
        $secAccept = base64_encode(pack(‘H*‘, sha1($secKey . ‘258EAFA5-E914-47DA-95CA-C5AB0DC85B11‘)));
        $upgrade  = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
            "Upgrade: websocket\r\n" .
            "Connection: Upgrade\r\n" .
            "WebSocket-Origin: $this->address\r\n" .
            "WebSocket-Location: ws://$this->address:$this->port/服务器地址\r\n".
            "Sec-WebSocket-Accept:$secAccept\r\n\r\n";
        return socket_write($newClient, $upgrade, strlen($upgrade));    # socket_write的作用是向socket_create的套接流写入信息,或者向socket_accept的套接流写入信息
    }

    /**
     * 解析接收数据
     * @param $buffer
     * @return null|string
     */
    public function message($buffer){
        $len = $masks = $data = $decoded = null;
        $len = ord($buffer[1]) & 127;
        if ($len === 126)  {
            $masks = substr($buffer, 4, 4);
            $data = substr($buffer, 8);
        } else if ($len === 127)  {
            $masks = substr($buffer, 10, 4);
            $data = substr($buffer, 14);
        } else  {
            $masks = substr($buffer, 2, 4);
            $data = substr($buffer, 6);
        }
        for ($index = 0; $index < strlen($data); $index++) {
            $decoded .= $data[$index] ^ $masks[$index % 4];
        }
        return $decoded;
    }

    /**
     * 发送数据
     * @param $newClinet 新接入的socket
     * @param $msg   要发送的数据
     * @return int|string
     */
    public function send($newClinet, $msg){
        $msg = $this->frame($msg);
        socket_write($newClinet, $msg, strlen($msg));   # 写入套接字
    }

    public function frame($s) {
        $a = str_split($s, 125);    # 把字符串分割到数组中 第二个长度参数
        if (count($a) == 1) {
            return "\x81" . chr(strlen($a[0])) . $a[0];
        }
        $ns = "";
        foreach ($a as $o) {
            $ns .= "\x81" . chr(strlen($o)) . $o;
        }
        return $ns;
    }

    /**
     * 关闭socket
     */
    public function close(){
        # socket_close的作用是关闭socket_create()或者socket_accept()所建立的套接流
        return socket_close($this->_sockets);
    }
}

$sock = new SocketService();
$sock->run();

原文地址:https://www.cnblogs.com/G921123/p/10254762.html

时间: 2024-11-10 14:10:20

WebSocket 长连接的相关文章

HTML5 中websocket长连接的具体实现方法

HTML5中通过调用与数据通信相关的Web Socket API,实现从服务器中推送信息到客户端. Socket又称为套接字,是基于W3C标准开发在一个TCP接口中进行双向通信的技术.通常情况下,Socket用于描述IP地址和端口,是通信过程中的一个字符句柄.当服务器端又多个应用服务绑定一个Socket时,通过通信中的字符句柄,实现不同端口对应不同应用服务功能.目前,大部分浏览器都支持HTML5中Socket API的运行. WebSocket连接服务器和客户端,这个链接是一个实时的长连接,服务

实现单台测试机6万websocket长连接

本文由作者郑银燕授权网易云社区发布. 本文是我在测试过程中的记录,实现了单台测试机发起最大的websocket长连接数.在一台测试机上,连接到一个远程服务时的本地端口是有限的.根据TCP/IP协议,由于端口是16位整数,也就只能是0到 65535,而0到1023是预留端口,所以能分配的端口只是1024到65534,也就是64511个.也就是说,一台机器一个IP只能创建六万多个websocket长连接. 一.客户端参数调优 本文采用的测试机分别为黑mac系统和linux系统(由于黑mac机器本身性

Spring+Stomp+ActiveMq实现websocket长连接

stomp.js+spring+sockjs+activemq实现websocket长连接,使用java配置. pom.xml(只列出除了spring基本依赖意外的依赖,spring-version为4.3.3.RELEASE): <dependency> <groupId>javax.websocket</groupId> <artifactId>javax.websocket-api</artifactId> <version>1

用nginx的Push_stream_module推送模块管理WebSocket长连接会不会限制WebSocket的双向通信的能力

最近项目在用nginx的Push_stream_module推送模块去实现服务器端向客户端信息的推送,本来只想实现这个单向通信的需求的,可是给客户端推送完消息之后,如果想让客户端给一个反馈,就没办法监听获取到客户端的反馈事件,并及时作出反应.后来知道push_stream_module管理长连接是基于pub/sub模式的,而且好像模块中也没有给出类似websocket中onmessage的API,是不是就是限制了webSocket全双工的通信能力,这点好像nodejs的socket.io做的就更

websocket长连接

代码如下: using UnityEngine; using System.Collections; using System.Collections.Generic; using System.IO; using System.Net; using System.Net.Sockets; using System.Runtime.InteropServices; namespace SYW_WebScoket { public class WebSocketManager : Singleto

Vue+WebSocket 实现页面实时刷新长连接

Vue+WebSocket 实现页面实时刷新长连接:https://www.cnblogs.com/fmixue/p/9110074.html 看完让你彻底搞懂Websocket原理:https://blog.csdn.net/frank_good/article/details/50856585 webSocket长连接实现demo(场景:扫码自动跳转登录,或者替换轮询):https://blog.csdn.net/GordoHu/article/details/78293803 原文地址:h

Socket,长连接,消息推送,消息提醒,未读消息提醒,消息通知,未读消息通知

今天公司要搞个类似QQ空间的未读消息通知,于是想到用WebSocket长连接,搜索一些资料后,调试好久,还以为不行,结果发现是IIS版本的原因,我本地用的Win7的系统,是IIS6的,本地测试一直不行,后来查资料发现用这个WebSocket要IIS8,我直接放服务器上,测试就OK了,废话不多说,直接上代码.我用的MVC+EF的框架. 1.新建一个Controller,继承APIController [Description("消息")] public class MessageCont

RPC、基于netty的长连接和websocket

1 RPC RPC也采用C/S的编程模式,以模块调用的简单性忽略通讯的具体细节,以便程序员不用关心C/S之间的通讯协议,集中精力对付实现过程.这就决定了 RPC生成的通讯包不可能对每种应用都有最恰当的处理办法,与Socket方法相比,传输相同的有效数据,RPC占用更多的网络带宽. RPC实在socket的基础上实现的,但是它比socket需要更多的网络和资源系统. 2 基于netty的长连接 异步.高性能 Boss线程(一个服务器端口对于一个)---接收到客户端连接---生成Channel---

Websocket、长连接、循环连接

[转]转自知乎高票回答  https://www.zhihu.com/question/20215561 一.WebSocket是HTML5出的东西(协议),也就是说HTTP协议没有变化,或者说没关系,但HTTP是不支持持久连接的(长连接,循环连接的不算)首先HTTP有1.1和1.0之说,也就是所谓的keep-alive,把多个HTTP请求合并为一个,但是Websocket其实是一个新协议,跟HTTP协议基本没有关系,只是为了兼容现有浏览器的握手规范而已,也就是说它是HTTP协议上的一种补充可以