一、认识WebSocket
WebSocket 是HTML5一种新的协议。它实现了浏览器与服务器全双工通信(full-duplex)。一开始的握手需要借助HTTP请求完成。(摘自百度百科)
这是很简单一句描述,但是这也是很重要的一句话,WebSocket是一种协议,属于HTML5规范中的一部分,它可以通过javascript建立与远程服务的链接,所以不管是java WEB 还是PHP还是.net都可以使用WebSocket,而且如今,市面上的各种浏览器如Firefox、Chrome、safari、opera等对它都支持的不错。
关于WebSocket,我们需要知道一下几点:
1、WebSocket是一种异步通信协议
2、WebSocket采用的是ws或wss协议
3、WebSocket可以使客户端和服务端在任意时间进行通信
4、和Ajax方式对比,WebSocket不受区域限制
关于WebSocket的教程网上很多,Tomcat8对其也提供了优秀的实现,在这里我就不具体解析了,大家可以去翻翻这方面的资料。
二、初步认识Socket.IO
三、牛刀小试
使用Socket.IO的时候需要使用一个玩意:Node.js,这里我们先对它做个初步的认识和使用。
简单的说 Node.js 就是运行在服务端的 JavaScript。
Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台。
Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎,V8引擎执行Javascript的速度非常快,性能非常好。(摘自网络)
(1)安装Node.js
http://www.runoob.com/nodejs/nodejs-install-setup.html
接下去我要做的都是基于这个Node.js环境搭建成功的下,所以再没有搭建好时,不建议往下看。
我们要用到Node.js里面的一个nmp,在新版的Node.js中都已将自带了,所以不需要再安装,我们首先要做的一件事就是先执行一段命令,打开cmd,输入下面这段代码:
npm install express -g
如果跳出下面的界面,说明安装成功:
express -V
记住这个版本号,或者记住这个方法,后面我们还需要用到。
(2)搭建Socket.IO服务端:
再这个过程中,我的目录是在D:\SocketJsTest中,大家可以更具自己的实际情况来自行改动
1、先创建一个json文件,D:\SocketJsTest\package.json:
{
"name": "realtime-server",
"version": "1.0.0",
"description": "my first realtime server",
"dependencies": {
"express": "^4.13.4",
"socket.io": "^1.4.8"
}
}
创建成功后,进入这个文件目录:
然后执行下面的两句代码,但是注意要分开一段段执行:
npm install --save express
npm install --save socket.io
执行完之后,在这个目录(D:\SocketJsTest)下出现一个文件夹:node_modules,该文件夹下有两个子文件夹:express、socket.io。
接下去,我们在这个目录(D:\SocketJsTest)下创建一个index.js文件:
var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);
app.get('/', function(req, res){
res.send('<h1>Hello Wellcome</h1>');
});
server.listen(8888, function(){
console.log('listening on *:8888');
});
好了,接下去我们在任务管理器中,进行下面的操作:
接着,我们打开浏览器,输入:http://localhost:8888,出现下面的窗口
好了,都已经搭建好了,现在我们改一改这个index.js,现在在任务管理器中按Ctrl+C退出监听,然后我们接着改:
var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);
app.get('/', function(req, res){
res.send('<h1>Hello Wellcome</h1>');
});
//在线用户
var onlineUsers = {};
//当前在线人数
var onlineCount = 0;
io.on('connection', function(socket){
//监听新用户加入
socket.on('login', function(obj){
//将新加入用户的唯一标识当作socket的名称,后面退出的时候会用到
socket.name = obj.userid;
//检查在线列表,如果不在里面就加入
if(!onlineUsers.hasOwnProperty(obj.userid)) {
obj["socketid"]=socket.id;//记录id
onlineUsers[obj.userid] = obj;//记录这个对象
//在线人数+1
onlineCount++;
}
//向所有客户端广播用户加入
io.sockets.emit('login', {onlineUsers:onlineUsers, onlineCount:onlineCount, user:obj});
});
//监听用户退出
socket.on('disconnect', function(){
//将退出的用户从在线列表中删除
if(onlineUsers.hasOwnProperty(socket.name)) {
//退出用户的信息
//var obj = {'userid':socket.name,'username':onlineUsers[socket.name]};
var obj = onlineUsers[socket.name];
//删除
delete onlineUsers[socket.name];
//在线人数-1
onlineCount--;
//向所有客户端广播用户退出
io.sockets.emit('logout', {onlineUsers:onlineUsers, onlineCount:onlineCount, user:obj});
}
});
//监听用户发布聊天内容
socket.on('message', function(obj){
//向所有客户端广播发布的消息
var socketid = onlineUsers[obj.senduserid].socketid;
//向对应的用户发送信息
io.sockets.sockets[socketid].emit('message', obj.username+'说:'+obj.content);
});
});
server.listen(8888, function(){
console.log('listening on *:8888');
});
好了,再运行一遍node index.js就好了。
(3)搭建Socket.IO客户端:
这个客户端就是一个简单的HTML文件,这个案列就是一个简单的,向某个在线的用户发送信息,为了方便大家的理解,没有添加任何多余的CSS和JS代码,界面有些丑陋,还望见谅:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="format-detection" content="telephone=no"/>
<meta name="format-detection" content="email=no"/>
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=0" name="viewport">
<title>发送给具体的</title>
<script src="http://192.168.1.110:8888/socket.io/socket.io.js"></script>
</head>
<body>
用户id:<input type="text" name="userId" id="userId"/>
用户名字:<input type="text" name="userName" id="userName"/>
<button οnclick="submit()">提交</button>
<div id="onlinecount"></div>
发送给别人的Id<input type="text" name="senduserid" id="senduserid"/>
发送给别人的内容<input type="text" name="content" id="content"/>
<button οnclick="sendMsg()">提交</button>
<div id="message"></div>
</body>
</html>
<script type="text/javascript">
var socket = io.connect('ws://192.168.1.110:8888');
function submit(){
var userid = document.getElementById("userId").value;
var username = document.getElementById("userName").value;
console.log(userid+"==="+username);
socket.emit('login', {userid:userid, username:username});
socket.on("login",function(o){
updateMessage(o,"login");
});
socket.on("logout",function(o){
updateMessage(o,"logout");
});
socket.on("message",function(str){
console.log(str);
updateMsg(str);
});
}
function sendMsg(){
var senduserid = document.getElementById("senduserid").value;
var content = document.getElementById("content").value;
console.log(senduserid+"========="+content);
socket.emit("message",{"senduserid":senduserid,"content":content,"username":document.getElementById("userName").value});
}
function updateMessage(o,action){
//当前在线用户列表
var onlineUsers = o.onlineUsers;
//当前在线人数
var onlineCount = o.onlineCount;
//新加入用户的信息
var user = o.user;
//更新在线人数
var userhtml = '';
var separator = '';
var html=""
html += user.username;
html += (action == 'login') ? ' 加入了聊天室' : ' 退出了聊天室';
for(key in onlineUsers) {
if(onlineUsers.hasOwnProperty(key)){
userhtml += separator+onlineUsers[key].username;
separator = '、';
}
}
document.getElementById("onlinecount").innerHTML =html+ '<br/>当前共有 '+onlineCount+' 人在线,在线列表:'+userhtml;
}
function updateMsg(msg){
console.log(msg);
var msgs = document.getElementById("message").innerHTML;
msgs +="<br/>"+msg;
document.getElementById("message").innerHTML=msgs;
}
</script>
注意:192.168.1.110是我本地的局域网IP,大家再使用的时候改称自己的IP就好。至于其中的代码我们后面再来慢慢分析。
我用的是Tomcat来部署这个web,当然这是一个最简单的web,只是有这个html文件,我Tomcat端口是8080,所以我用http://localhost:8080/SocketIOTest/这个链接来打开,界面如下:
用不同的浏览器多打开几个,我这里开了三个,首先需要输入id和用户名点击提交。注意三个的id是不同的,前面有提到,为了不添加多余的js我没有写多余的JS,所以有些麻烦,但是理解起来也方便。先看下这三个截图:
忽然发现有多个浏览器还是很幸福的,哈哈,现在发信息,1->2,2->3,3->1看看是否能收到信息:
看明白了?没明白的,多看看
四、代码解析
-
name - 包名。
-
version - 包的版本号。
-
description - 包的描述。
-
homepage - 包的官网 url 。
-
author - 包的作者姓名。
-
contributors - 包的其他贡献者姓名。
-
dependencies - 依赖包列表。如果依赖包没有安装,npm 会自动将依赖包安装在 node_module 目录下。
-
repository - 包代码存放的地方的类型,可以是 git 或 svn,git 可在 Github 上。
-
main - main 字段是一个模块ID,它是一个指向你程序的主要项目。就是说,如果你包的名字叫 express,然后用户安装它,然后require("express")。
-
keywords - 关键字
-
不要担心,虽然说有这么多的属性名,但是实际上用的不多,官网:http://socket.io/get-started/chat/也给出了一个最简单的搭配:
-
这个package.json是为node.js服务的,所以如果有什么疑惑可以去查查node.js的资料,在这里我不再深入。{ "name": "socket-chat-example", "version": "0.0.1", "description": "my first socket.io app", "dependencies": {} }
-
-
(2)关于服务端index.js的解析:
-
先看下这两句,表示初始化程序是一个处理函数,你可以根据这个处理函数提供一个HTTP服务器(server)。var app = require('express')(); var server = require('http').Server(app);
-
让我们的服务器监听8888端口,当成功监听这个端口的时候输出一个信息就是console.log里面的内容。server.listen(8888, function(){ console.log('listening on *:8888'); });
-
这句应该号理解,设置我们的主页,也就是打开这个端口的链接的时候,res代表的就是request,res代表的就是response,这下应该号理解了吧。app.get('/', function(req, res){ res.send('<h1>Hello Wellcome</h1>'); });
-
从某种角度来说,这样的写法不是很好,总部要吧整个网页都写进去吧?官方也提供了一种写法,如下:
-
这样就可以吧index.html这文件推送给访问的客户端。可能你会好奇这个index.html放在哪?仔细想想就知道了,反正我是在这个目录:D:\SocketJsTest。我们再看下下面的这句代码:app.get('/', function(req, res){ res.sendFile(__dirname + '/index.html'); });
-
这句什么意思?官网是这么说的:var io = require('socket.io')(server);
-
Notice that I initialize a new instance of
socket.io
by passing thehttp
(the HTTP server) object. Then I listen on theconnection
event for incoming sockets, and I log it to the console. -
说简单点,就是利用这个server创建一个socket.io。
-
-----------------------------------------------------------------------------------------------------------------------
-
加条分割线,下面说的就是就是socket.io的操作了;
io.on('connection', function(socket){
});
几乎所有的操操作都在这个函数下,都是关于参数socket的操作,它有哪些操作,我记不全,就给大家从官网(http://socket.io/docs/server-api/)截了几张图。
-
本人英语不太好,只能根据自己的经验略作解释,如有什么不妥,还望各位不吝赐教。
-
这里面的#相当于一个.前面跟着的是一个对象,后面跟着的是一个方法,这样就好理解了吧,我们先慢慢看:
-
rooms:列出这个socket所有加入的房间(就像聊天室,有各种聊天室房间,也相当于群聊),返回的是一个数组。
-
client:一个对底层客户端对象的引用
-
conn:一个参考的underyling客户端传输连接
-
request:用来获取请求头信息
-
id:获取Socket的id(每个Socket都有一个Id,用来区分链接用户)
-
emit:发送信息
-
join:加入一个房间(聊天室)
-
leave:离开一个房间(聊天室)
-
to:进入某个房间
-
in:在某个房间,可以在之后在这个房间广播自己的信息。
-
好了,官网的我也略微的做了解释,解释不好,还望见谅。还有一个很重要的一个on,这个on有什么用的,我在index.js中也写了几个on:login,logout,message。
-
可以这么理解on在监听一个事件,当然这个事件可以自己命名,当客户端传进login,或者logout,或者message的事件信息,这个服务器就会做出相应的反应。
<strong>socket.emit('event',message);</strong>
socket.broadcast.emit('event',message);
io.sockets.emit('event',message);
或者
<span style="font-size:24px;">io.emit('event',message);</span>
socket.broadcast.to('roomname').emit('event',message);
io.sockets.in('roomname').emit('event', message);
io.sockets.sockets[socketid].emit('event',message);