这是一个简单的基于php Swoole扩展实现的聊天室demo。
前言-浅析websocket
什么是websocket
WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端。
为什么会有websocket
长久以来, 创建实现客户端和用户端之间双工通讯的web app都会造成HTTP轮询的滥用: 客户端向主机不断发送不同的HTTP呼叫来进行询问。
这会导致一系列的问题:
1.服务器被迫为每个客户端使用许多不同的底层TCP连接:一个用于向客户端发送信息,其它用于接收每个传入消息。
2.有线协议有很高的开销,每一个客户端和服务器之间都有HTTP头。
3.客户端脚本被迫维护从传出连接到传入连接的映射来追踪回复。
一个更简单的解决方案是使用单个TCP连接双向通信。 这就是WebSocket协议所提供的功能。 结合WebSocket API ,WebSocket协议提供了一个用来替代HTTP轮询实现网页到远程主机的双向通信的方法。
WebSocket协议被设计来取代用HTTP作为传输层的双向通讯技术,这些技术只能牺牲效率和可依赖性其中一方来提高另一方,因为HTTP最初的目的不是为了双向通讯。
名称的由来
08年6月18日,一群WHATWG的工程师在讨论一些技术问题,一个工程师提到说「我们之前讨论的那个东西,不要叫TCPConnection 了,还是起个别的名字吧 」,接着几个名字被提及,DuplexConnection,TCPSocket,SocketConnection 。
Socket一直以来都被人用来表示网络中一个连接的两端,考虑到怎么让工程师更容易接受,后来Hixie提出了WebSocket ,大家都没有异议,紧接着mcarter在Comet Daily中发表了文章Independence Day: HTML5 WebSocket Liberates Comet From Hacks,后来随着各大浏览器对WebSocket的支持,它变成了实际的标准,IETF也沿用了这个名字。
接口定义
WHATWG文档中对WebSocket接口的定义
协议基础
WebSocket的目的是取代HTTP在双向通信场景下的使用,而且它的实现方式有些也是基于HTTP的(WS的默认端口是80,WSS是443)。现有的网络环境(客户端、服务器、网络中间人、代理等)对HTTP都有很好的支持,所以这样做可以充分利用现有的HTTP的基础设施,有点向下兼容的意味。
和http的关系(握手)
浏览器请求 GET /webfin/websocket/ HTTP/1.1
Host: localhost Upgrade: websocket
Connection: Upgrade Sec-WebSocket-Key: xqBt3ImNzJbYqRINxEFlkg== Origin: http://服务器地址 Sec-WebSocket-Version: 13
服务器回应
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: K7DJLdLooIwIG/MOpvWFB3y3FE8=
WebSocket借用http请求进行握手,相比正常的http请求,多了一些内容。其中,
Upgrade: websocket Connection: Upgrade
表示希望将http协议升级到Websocket协议。
Sec-WebSocket-Key是浏览器随机生成的base64 encode的值,用来询问服务器是否是支持WebSocket。
服务器返回
Upgrade: websocket Connection: Upgrade
告诉浏览器即将升级的是Websocket协议
Sec-WebSocket-Accept是将请求包“Sec-WebSocket-Key”的值,与”258EAFA5-E914-47DA-95CA-C5AB0DC85B11″这个字符串进行拼接,然后对拼接后的字符串进行sha-1运算,再进行base64编码得到的。用来说明自己是WebSocket助理服务器。
Sec-WebSocket-Version是WebSocket协议版本号。RFC6455要求使用的版本是13,之前草案的版本均应当被弃用。 更多握手规范详见RFC6455。
和socket的关系
就像Java和JavaScript,并没有什么太大的关系,但又不能说完全没关系。可以这么说:
1.命名方面,Socket是一个深入人心的概念,WebSocket借用了这一概念; 2.使用方面,完全两个东西。
环境准备
安装swoole
1 |
|
准备好jQuery.js
websocket_server.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?php
/**
* Created by PhpStorm.
* User: heimo
* Date: 2017/6/8
* Time: 下午10:52
*/
/*
创建wss
$ws = new swoole_websocket_server("0.0.0.0", 9502, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL);
$ws->set(array(
'ssl_cert_file' => '/ssl/xxxx.crt',
'ssl_key_file' => '/ssl/xxxx.key',
));
*/
//创建websocket服务器对象,监听0.0.0.0:9502端口
$ws = new swoole_websocket_server("0.0.0.0", 9502);
//监听WebSocket连接打开事件
$ws->on('open', function ($ws, $request) {
//var_dump($ws);
//var_dump($request);
echo "client-{$request->fd} is connected\n";
});
//监听WebSocket消息事件
$ws->on('message', function ($ws, $frame) {
echo "client-{$frame->fd} send message:".$frame->data."\n";
foreach($ws->connections as $fd)
{
echo "push message to client-{$fd} \n";
$ws->push($fd, $frame->data);
}
});
//监听WebSocket连接关闭事件
$ws->on('close', function ($ws, $fd) {
echo "client-{$fd} is closed\n";
});
$ws->start();
chat.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>websocket</title>
</head>
<style>
.sendBox { margin: 15px; }
.sendBox .input { width: 200px;height: 25px; }
.sendBox .button{ width: 60px;height: 25px; }
</style>
<body>
<div class="sendBox">
<input class="input" id="msg" />
<button class="button" type="button" onclick="send()">send</button>
</div>
<hr>
<div>
<ul id="chatMsg">
</ul>
</div>
</body>
<script type="text/javascript" src="jquery.js"></script>
<script>
var wsServer = 'ws://websocket_server_ip:9502';
var websocket = new WebSocket(wsServer);
//打开事件
websocket.onopen = function (evt) {
$("#chatMsg").append("<li style='color: green'>Connected to WebSocket server!</li>");
};
//关闭事件
websocket.onclose = function (evt) {
$("#chatMsg").append("<li style='color: red'>Disconnected!</li>");
};
//消息事件
websocket.onmessage = function (evt) {
$("#chatMsg").prepend("<li style='color: blue'>new message:"+escapeHtml(evt.data)+"</li>");
};
//异常事件
websocket.onerror = function (evt, e) {
$("#chatMsg").append("<li style='color: red'>Error occured:"+evt.data+"</li>");
};
//发送消息
function send() {
var msg = $('#msg').val();
if (msg.length/1024 > 1 || msg.length == 0){
alert('消息内容不符合规范');
}else {
websocket.send(msg);
$('#msg').val('');
}
}
document.onkeydown = function(e){
var ev = document.all ? window.event : e;
if(ev.keyCode==13) {
send();
$('#msg').val('');
}
}
//过滤
var entityMap = {
"&": "&",
"<": "<",
">": ">",
'"': '"',
"'": ''',
"/": '/'
};
function escapeHtml(string) {
return String(string).replace(/[&<>"'\/]/g, function (s) {
return entityMap[s];
});
}
</script>
</html>
启动服务
- 修改chat.html中wsServer的配置为自己的配置
1 |
|
-
配置Nginx使用户能够访问到chat.html
-
启动websocket_server
php websocket_server.php
访问页面
浏览器打开chat.html
构建整个IM需要考虑的问题
安全问题
1.身份认证:token
2.防止越权:代码逻辑
3.XSS:过滤
4.https和wss
性能问题
1.并发
2.异步
稳定性
1.心跳:服务端
2.断线重连:客户端
转载使用注明出处。原文链接 https://heimo-he.github.io/2018/03/30/php-swoole-webim/