WebSocket 长连接 及超时问题解决
<?php
set_time_limit(0);
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();
网上看到很多说会断开链接,设置心跳包也没有用
我这里直接配置了下 set_time_limit(0); 改变 php.ini中的 max_execution_time设置时间 然后就没有断线的问题了! 也保证了持久连接!
HTML部分
<!DOCTYPE html>
<html>
<head>
<title>Socket 测试</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1, maximum-scale=1, user-scalable=no">
<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>
</head>
<body>
<div class="container">
<div class="title">Socket 测试长连接</div>
<div class="content">
<div class="show-area"></div>
<div class="write-area">
<div><button class="btn btn-default send" >发送</button></div>
<div><input name="name" id="name" type="text" placeholder="input your name"></div>
<div>
<textarea name="message" id="message" cols="38" rows="4" placeholder="input your message..."></textarea>
</div>
</div>
</div>
</div> <script src="http://libs.baidu.com/jquery/1.9.1/jquery.min.js"></script>
<script src="https://cdn.bootcss.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>
<script>
var wsurl = 'ws://localhost:80/websocket/test2.php';
var websocket;
websocket = new WebSocket(wsurl);
//连接建立
websocket.onopen = function(evevt){
console.log("Connected to WebSocket server.");
$('.show-area').append('<p class="bg-info message"><i class="glyphicon glyphicon-info-sign"></i>Connected to WebSocket server!</p>');
}
//收到消息
websocket.onmessage = function(event) {
console.log(event);
$('.show-area').append('<p class="bg-info message"><i class="glyphicon glyphicon-info-sign"></i>'+event.data+'</p>');
} //发生错误
websocket.onerror = function(event){
console.log("Connected to WebSocket server error");
$('.show-area').append('<p class="bg-danger message"><a name=""></a><i class="glyphicon glyphicon-info-sign"></i>Connect to WebSocket server error.</p>');
} //连接关闭
websocket.onclose = function(event){
console.log('websocket Connection Closed. ');
$('.show-area').append('<p class="bg-warning message"><a name=""></a><i class="glyphicon glyphicon-info-sign"></i>websocket Connection Closed.</p>');
} // 发送信息
function send(){
var name = $('#name').val();
var message = $('#message').val();
if(!name){
alert('请输入用户名!');
return false;
}
if(!message){
alert('发送消息不能为空!');
return false;
}
var msg = {
message: message,
name: name
};
try{
websocket.send(JSON.stringify(msg));
} catch(ex) {
console.log(ex);
}
}
//点发送按钮发送消息
$('.send').bind('click',function(){
send();
}); </script>
</body>
</html>
WebSocket 长连接 及超时问题解决的更多相关文章
- 当web应用包含了websocket长连接,如何在web应用前加一层nginx转发
1 通过在web应用的前面加一层nginx ,可以实现一台主机部署多个应用,每个应用都可以用不同的域名去访问,并且端口都是80 2 nignx 转发websocket长连接 1 每个web应用,他们运 ...
- 实现单台测试机6万websocket长连接
本文由作者郑银燕授权网易云社区发布. 本文是我在测试过程中的记录,实现了单台测试机发起最大的websocket长连接数.在一台测试机上,连接到一个远程服务时的本地端口是有限的.根据TCP/IP协议,由 ...
- Spring+Stomp+ActiveMq实现websocket长连接
stomp.js+spring+sockjs+activemq实现websocket长连接,使用java配置. pom.xml(只列出除了spring基本依赖意外的依赖,spring-version为 ...
- WebSocket长连接
WebSocket长连接 1.概述 1.1 定义 1.2 原理 2.Django中配置WebSocket 2.1安装第三方法包 pip install channels 2.2 Django 中的配置 ...
- Python WebSocket长连接心跳与短连接
python websocket 安装 pip install websocket-client 先来看一下,长连接调用方式: ws = websocket.WebSocketApp("ws ...
- Nginx与Tomcat、Client之间请求的长连接配置不一致问题解决[转]
http://bert82503.iteye.com/blog/2152613 前些天,线上出现“服务端长连接与客户端短连接引起Nginx的Writing.Active连接数过高问题”,这个是由于“服 ...
- 雨露均沾的OkHttp—WebSocket长连接的使用&源码解析
前言 最近老板又来新需求了,要做一个物联网相关的app,其中有个需求是客户端需要收发服务器不定期发出的消息. 内心OS:
- FTP被动模式连接及超时问题解决
问题: 1.FTPClient.listFiles()或者FTPClient.retrieveFile()方法时,就停止在那里,什么反应都没有,出现假死状态. 2.连接FTP服务器,长时间进行数据操作 ...
- Django websocket 长连接使用
下载 pip install dwebsocket WebSocket是一种在单个TCP连接上进行全双工通信的协议 WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客 ...
随机推荐
- jQuery之禁止Get请求缓存
如果两次Get请求的URL完全一样,则IE浏览器会调用上次缓存的结果,不会发起新的Http请求. 解决办法:在URL最后面加上时间戳. jQuery全局设置禁止缓存 $.ajaxSetup({ cac ...
- 单例&多线程
单例模式,最常见的就是饥饿模式,和懒汉模式,一个直接实例化对象,一个在调用方法时进行实例化对象.在多线程模式中,考虑到性能和线程安全问题,我们一般选择下面两种比较经典的单例模式,在性能提高的同时,又保 ...
- ARM板移植udev-126
下载udev-126.tar.xz 下载的网址为: https://mirrors.edge.kernel.org/pub/linux/utils/kernel/hotplug/ 解压文件并且编译 # ...
- activity背景毛玻璃效果
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools= ...
- java命令行调用本地文件协议hikvideoclient://
最近在做一个视频项目,项目中需要通过调用海康本地协议打开视频播放器,起初尝试通过Process/ProcessBuilder无解,因为这个是调用本地应用程序的. 我要调用的是本地伪协议,最终通过一些研 ...
- CSS学习总结2:CSS框模型
1.CSS框模型概述 CSS框模型规定了元素框处理元素内容.内边框.边框和外边框的方式. 元素框的最内部分是实际的内容,直接包围内容的是内边距.内边距呈现了元素的背景.内边距的边缘是边框.边框以外是外 ...
- oracle 使用exp命令 sys用户登录 导出表存为dmp文件
在cmd下面,使用exp命令,格式为 exp \"sys/123456@dbname as sysdba\" file=d:\dmp\test.dmp tables=sys.tes ...
- mybatis进阶--输入映射和输出映射
我们知道,mapper.xml是我们配置操作数据库的sql语句的地方.其中每个sql语句对应着一个方法,每个方法都有自己的输入输出参数类型.那么这些类型都是怎么配置的呢?今天我们来一起学习下. 输入映 ...
- 深浅copy和字符串细节方法
copy a=[1,2,3]b=aid(a)55499272id(b)55499272 id()就是查看内存地址,是不是同一个对象. c=a.copy()id(c)57940040 可见copy()出 ...
- Centos安装配置Postfix邮件服务器--网址
http://www.haiyun.me/archives/centos-install-postfix.html http://blog.csdn.net/liuyunfengheda/articl ...