• websocket实时消息推送封装类


    1. class SocketService
    2. {
    3. private $address = '0.0.0.0';
    4. private $port = 8080;
    5. private $_sockets;
    6. public function __construct($address = '', $port='')
    7. {
    8. if(!empty($address)){
    9. $this->address = $address;
    10. }
    11. if(!empty($port)) {
    12. $this->port = $port;
    13. }
    14. }
    15. public function service(){
    16. //获取tcp协议号码。
    17. $tcp = getprotobyname("tcp");
    18. $sock = socket_create(AF_INET, SOCK_STREAM, $tcp);
    19. socket_set_option($sock, SOL_SOCKET, SO_REUSEADDR, 1);
    20. if($sock < 0)
    21. {
    22. throw new Exception("failed to create socket: ".socket_strerror($sock)."\n");
    23. }
    24. socket_bind($sock, $this->address, $this->port);
    25. socket_listen($sock, $this->port);
    26. echo "listen on $this->address $this->port ... \n";
    27. $this->_sockets = $sock;
    28. }
    29. public function run(){
    30. $this->service();
    31. $clients[] = $this->_sockets;
    32. while (true){
    33. $changes = $clients;
    34. $write = NULL;
    35. $except = NULL;
    36. socket_select($changes, $write, $except, NULL);
    37. foreach ($changes as $key => $_sock){
    38. if($this->_sockets == $_sock){ //判断是不是新接入的socket
    39. if(($newClient = socket_accept($_sock)) === false){
    40. die('failed to accept socket: '.socket_strerror($_sock)."\n");
    41. }
    42. $line = trim(socket_read($newClient, 1024));
    43. $this->handshaking($newClient, $line);
    44. //获取client ip
    45. socket_getpeername ($newClient, $ip);
    46. $clients[$ip] = $newClient;
    47. echo "Client ip:{$ip} \n";
    48. echo "Client msg:{$line} \n";
    49. } else {
    50. socket_recv($_sock, $buffer, 2048, 0);
    51. $msg = $this->message($buffer);
    52. //在这里业务代码
    53. echo "{$key} clinet msg:",$msg,"\n";
    54. fwrite(STDOUT, 'Please input a argument:');
    55. $response = trim(fgets(STDIN));
    56. $this->send($_sock, $response);
    57. echo "{$key} response to Client:".$response,"\n";
    58. }
    59. }
    60. }
    61. }
    62. /**
    63. * 握手处理
    64. * @param $newClient socket
    65. * @return int 接收到的信息
    66. */
    67. public function handshaking($newClient, $line){
    68. $headers = array();
    69. $lines = preg_split("/\r\n/", $line);
    70. foreach($lines as $line)
    71. {
    72. $line = chop($line);
    73. if(preg_match('/\A(\S+): (.*)\z/', $line, $matches))
    74. {
    75. $headers[$matches[1]] = $matches[2];
    76. }
    77. }
    78. $secKey = $headers['Sec-WebSocket-Key'];
    79. $secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
    80. $upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
    81. "Upgrade: websocket\r\n" .
    82. "Connection: Upgrade\r\n" .
    83. "WebSocket-Origin: $this->address\r\n" .
    84. "WebSocket-Location: ws://$this->address:$this->port/websocket/websocket\r\n".
    85. "Sec-WebSocket-Accept:$secAccept\r\n\r\n";
    86. return socket_write($newClient, $upgrade, strlen($upgrade));
    87. }
    88. /**
    89. * 解析接收数据
    90. * @param $buffer
    91. * @return null|string
    92. */
    93. public function message($buffer){
    94. $len = $masks = $data = $decoded = null;
    95. $len = ord($buffer[1]) & 127;
    96. if ($len === 126) {
    97. $masks = substr($buffer, 4, 4);
    98. $data = substr($buffer, 8);
    99. } else if ($len === 127) {
    100. $masks = substr($buffer, 10, 4);
    101. $data = substr($buffer, 14);
    102. } else {
    103. $masks = substr($buffer, 2, 4);
    104. $data = substr($buffer, 6);
    105. }
    106. for ($index = 0; $index < strlen($data); $index++) {
    107. $decoded .= $data[$index] ^ $masks[$index % 4];
    108. }
    109. return $decoded;
    110. }
    111. /**
    112. * 发送数据
    113. * @param $newClinet 新接入的socket
    114. * @param $msg 要发送的数据
    115. * @return int|string
    116. */
    117. public function send($newClinet, $msg){
    118. $msg = $this->frame($msg);
    119. socket_write($newClinet, $msg, strlen($msg));
    120. }
    121. public function frame($s) {
    122. $a = str_split($s, 125);
    123. if (count($a) == 1) {
    124. return "\x81" . chr(strlen($a[0])) . $a[0];
    125. }
    126. $ns = "";
    127. foreach ($a as $o) {
    128. $ns .= "\x81" . chr(strlen($o)) . $o;
    129. }
    130. return $ns;
    131. }
    132. /**
    133. * 关闭socket
    134. */
    135. public function close(){
    136. return socket_close($this->_sockets);
    137. }
    138. }
    139. $sock = new SocketService();
    140. $sock->run();

    浏览器发出webSocket的连线请求,服务器发出响应,这个过程称为握手,握手的过程只需要一次,就可以实现持久连接

  • 相关阅读:
    ESP8266-Arduino编程实例-PCT2075温度数字转换器驱动
    【设计模式】Java设计模式 - 状态模式
    RocketMQ的适用场景有哪些?
    Leetcode算法解析——三数之和
    OpenTelemetry 深度定制:跨服务追踪的实战技巧
    力扣 6080. 使数组按非递减顺序排列
    Spring修炼之路--基础知识
    安装Ant 保姆级别教程
    egg-token码的生成与验证
    俄语第二格
  • 原文地址:https://blog.csdn.net/smallmww/article/details/133818366