经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » PHP » 查看文章
WebSocket 长连接 及超时问题解决
来源:cnblogs  作者:Sorr  时间:2019/1/14 10:00:23  对本文有异议
  1. <?php
    set_time_limit(0);
  2. class SocketService
  3. {
  4. private $address = 'localhost';
  5. private $port = 80;
  6. private $_sockets;
  7. public function __construct($address = '', $port='')
  8. {
  9. if(!empty($address)){
  10. $this->address = $address;
  11. }
  12. if(!empty($port)) {
  13. $this->port = $port;
  14. }
  15. }
  16. public function service(){
  17. //获取tcp协议号码。
  18. $tcp = getprotobyname("SOL_TCP"); # 获取与协议名称关联的协议号
  19. $sock = socket_create(AF_INET, SOCK_STREAM, $tcp); # 创建一个套接字(通讯节点)
  20. socket_set_option($sock, SOL_SOCKET, SO_REUSEADDR, 1); # 设置套接字选项
  21. if($sock < 0)
  22. {
  23. throw new Exception("failed to create socket: ".socket_strerror($sock)."\n");
  24. }
  25. socket_bind($sock, $this->address, $this->port); # 绑定
  26. socket_listen($sock, $this->port); # 监听套接字上的连接
  27. $this->_sockets = $sock;
  28. }
  29. public function run(){
  30. $this->service();
  31. $clients[] = $this->_sockets; # 数组存储 每个socket
  32. # 让服务器无限获取客户端传过来的信息
  33. while (true){
  34. $changes = $clients;
  35. $write = NULL;
  36. $except = NULL;
  37. socket_select($changes, $write, $except, NULL);
  38. foreach ($changes as $key => $_sock){
  39. if($this->_sockets == $_sock){ # 判断是不是新接入的socket
  40. if(($newClient = socket_accept($_sock)) === false){ # 接受新的套接字上的连接 socket_accept的作用就是接受socket_bind()所绑定的主机发过来的套接流
  41. die('failed to accept socket: '.socket_strerror($_sock)."\n"); # 返回描述套接字错误的字符串
  42. }
  43. $line = trim(socket_read($newClient, 1024)); # 读取客户端传过来的资源,并转化为字符串 socket_read的作用就是读出socket_accept()的资源并把它转化为字符串
  44. $this->handshaking($newClient, $line);
  45. //获取client ip
  46. socket_getpeername ($newClient, $ip); # 查询给定套接字的远程端,这可能导致主机/端口或UNIX文件系统路径,具体取决于其类型。
  47. $clients[$ip] = $newClient;
  48. } else {
  49. # 读取该socket的信息,注意:第二个参数是引用传参即接收数据,第三个参数是接收数据的长度
  50. $lenght = socket_recv($_sock, $buffer, 2048, 0); # 从已连接的socket接收数据 $lenght 接收到字符串长度
  51. $msg = $this->message($buffer); # 接收到的信息
  52. //在这里业务代码
  53. fwrite(STDOUT, 'Please input a argument:');
  54. $response = trim(fgets(STDIN));
  55. // $this->send($_sock, $response); # 第二个参数是获取数据 要发送的信息
  56. $this->send($_sock, '在线');
  57. }
  58. }
  59. }
  60. }
  61. /**
  62. * 握手处理
  63. * @param $newClient socket
  64. * @return int 接收到的信息
  65. */
  66. public function handshaking($newClient, $line){
  67. $headers = array();
  68. $lines = preg_split("/\r\n/", $line); # 通过一个正则表达式分隔字符串。
  69. foreach($lines as $line)
  70. {
  71. $line = chop($line); # 移除字符串右端的空白字符或其他预定义字符
  72. if(preg_match('/\A(\S+): (.*)\z/', $line, $matches))
  73. {
  74. $headers[$matches[1]] = $matches[2];
  75. }
  76. }
  77. $secKey = $headers['Sec-WebSocket-Key'];
  78. $secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
  79. $upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
  80. "Upgrade: websocket\r\n" .
  81. "Connection: Upgrade\r\n" .
  82. "WebSocket-Origin: $this->address\r\n" .
  83. "WebSocket-Location: ws://$this->address:$this->port/服务器地址\r\n".
  84. "Sec-WebSocket-Accept:$secAccept\r\n\r\n";
  85. return socket_write($newClient, $upgrade, strlen($upgrade)); # socket_write的作用是向socket_create的套接流写入信息,或者向socket_accept的套接流写入信息
  86. }
  87. /**
  88. * 解析接收数据
  89. * @param $buffer
  90. * @return null|string
  91. */
  92. public function message($buffer){
  93. $len = $masks = $data = $decoded = null;
  94. $len = ord($buffer[1]) & 127;
  95. if ($len === 126) {
  96. $masks = substr($buffer, 4, 4);
  97. $data = substr($buffer, 8);
  98. } else if ($len === 127) {
  99. $masks = substr($buffer, 10, 4);
  100. $data = substr($buffer, 14);
  101. } else {
  102. $masks = substr($buffer, 2, 4);
  103. $data = substr($buffer, 6);
  104. }
  105. for ($index = 0; $index < strlen($data); $index++) {
  106. $decoded .= $data[$index] ^ $masks[$index % 4];
  107. }
  108. return $decoded;
  109. }
  110. /**
  111. * 发送数据
  112. * @param $newClinet 新接入的socket
  113. * @param $msg 要发送的数据
  114. * @return int|string
  115. */
  116. public function send($newClinet, $msg){
  117. $msg = $this->frame($msg);
  118. socket_write($newClinet, $msg, strlen($msg)); # 写入套接字
  119. }
  120. public function frame($s) {
  121. $a = str_split($s, 125); # 把字符串分割到数组中 第二个长度参数
  122. if (count($a) == 1) {
  123. return "\x81" . chr(strlen($a[0])) . $a[0];
  124. }
  125. $ns = "";
  126. foreach ($a as $o) {
  127. $ns .= "\x81" . chr(strlen($o)) . $o;
  128. }
  129. return $ns;
  130. }
  131. /**
  132. * 关闭socket
  133. */
  134. public function close(){
  135. # socket_close的作用是关闭socket_create()或者socket_accept()所建立的套接流
  136. return socket_close($this->_sockets);
  137. }
  138. }
  139. $sock = new SocketService();
  140. $sock->run();

 

网上看到很多说会断开链接,设置心跳包也没有用

我这里直接配置了下 set_time_limit(0);   改变 php.ini中的 max_execution_time设置时间 然后就没有断线的问题了! 也保证了持久连接!

 

HTML部分

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>Socket 测试</title>
  5. <meta charset="utf-8">
  6. <meta name="viewport" content="width=device-width,initial-scale=1, maximum-scale=1, user-scalable=no">
  7. <link href="https://cdn.bootcss.com/bootstrap/3.3.2/css/bootstrap.min.css" rel="stylesheet">

     <style type="text/css">
            html, body {
                min-height: 100%; }

            body {
                margin: 0;
                padding: 0;
                width: 100%;
                font-family: "Microsoft Yahei",sans-serif, Arial; }

            .container {
                text-align: center; }

            .title {
                font-size: 16px;
                color: rgba(0, 0, 0, 0.3);
                position: fixed;
                line-height: 30px;
                height: 30px;
                left: 0px;
                right: 0px;
                background-color: white; }

            .content {
                background-color: #f1f1f1;
                border-top-left-radius: 6px;
                border-top-right-radius: 6px;
                margin-top: 30px; }
            .content .show-area {
                text-align: left;
                padding-top: 8px;
                padding-bottom: 168px; }
            .content .show-area .message {
                width: 70%;
                padding: 5px;
                word-wrap: break-word;
                word-break: normal; }
            .content .write-area {
                position: fixed;
                bottom: 0px;
                right: 0px;
                left: 0px;
                background-color: #f1f1f1;
                z-index: 10;
                width: 100%;
                height: 160px;
                border-top: 1px solid #d8d8d8; }
            .content .write-area .send {
                position: relative;
                top: -28px;
                height: 28px;
                border-top-left-radius: 55px;
                border-top-right-radius: 55px; }
            .content .write-area #name{
                position: relative;
                top: -20px;
                line-height: 28px;
                font-size: 13px; }
        </style>
  8. </head>
  9. <body>
  10. <div class="container">
  11. <div class="title">Socket 测试长连接</div>
  12. <div class="content">
  13. <div class="show-area"></div>
  14. <div class="write-area">
  15. <div><button class="btn btn-default send" >发送</button></div>
  16. <div><input name="name" id="name" type="text" placeholder="input your name"></div>
  17. <div>
  18. <textarea name="message" id="message" cols="38" rows="4" placeholder="input your message..."></textarea>
  19. </div>
  20. </div>
  21. </div>
  22. </div>
  23.  
  24. <script src="http://libs.baidu.com/jquery/1.9.1/jquery.min.js"></script>
  25. <script src="https://cdn.bootcss.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>
  26. <script>
  27. var wsurl = 'ws://localhost:80/websocket/test2.php';
  28. var websocket;
  29. websocket = new WebSocket(wsurl);
  30. //连接建立
  31. websocket.onopen = function(evevt){
  32. console.log("Connected to WebSocket server.");
  33. $('.show-area').append('<p class="bg-info message"><i class="glyphicon glyphicon-info-sign"></i>Connected to WebSocket server!</p>');
  34. }
  35. //收到消息
  36. websocket.onmessage = function(event) {
  37. console.log(event);
  38. $('.show-area').append('<p class="bg-info message"><i class="glyphicon glyphicon-info-sign"></i>'+event.data+'</p>');
  39. }
  40. //发生错误
  41. websocket.onerror = function(event){
  42. console.log("Connected to WebSocket server error");
  43. $('.show-area').append('<p class="bg-danger message"><a name=""></a><i class="glyphicon glyphicon-info-sign"></i>Connect to WebSocket server error.</p>');
  44. }
  45. //连接关闭
  46. websocket.onclose = function(event){
  47. console.log('websocket Connection Closed. ');
  48. $('.show-area').append('<p class="bg-warning message"><a name=""></a><i class="glyphicon glyphicon-info-sign"></i>websocket Connection Closed.</p>');
  49. }
  50. // 发送信息
  51. function send(){
  52. var name = $('#name').val();
  53. var message = $('#message').val();
  54. if(!name){
  55. alert('请输入用户名!');
  56. return false;
  57. }
  58. if(!message){
  59. alert('发送消息不能为空!');
  60. return false;
  61. }
  62. var msg = {
  63. message: message,
  64. name: name
  65. };
  66. try{
  67. websocket.send(JSON.stringify(msg));
  68. } catch(ex) {
  69. console.log(ex);
  70. }
  71. }
  72. //点发送按钮发送消息
  73. $('.send').bind('click',function(){
  74. send();
  75. });
  76. </script>
  77. </body>
  78. </html>

 

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号