Socket.io浅尝


一、认识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

在上面我门讲了WebSocket,这里讲的是Socket.IO, 这两个又有什么联系呢?首先,他们都是Socket,什么是Socket: 网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。
Socket.IO其封装了WebSocket,但是它和WebSocket又有些不同,比如说,它包含WebSocket的同时也包含了Ajax的链接方式。但是我们在使用的时候就无需关心到底使用的是WebSocket还是Ajax的链接方式。说到这里大家也应该知道了,为什么又Socket.IO的存在,为什么好多人都选择用Socket.IO,虽然现在很多浏览器都很好的支持了WebSocket,相信也会有越来越多的浏览器支持这一协议,但是同时也要认清的一个事实,那就是也有一部分并不支持,所以在很多时候我们会直接使用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

安装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看看是否能收到信息:


看明白了?没明白的,多看看



四、代码解析


(1)关于package.json的解释:
  • 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/也给出了一个最简单的搭配:

  • {
      "name": "socket-chat-example",
      "version": "0.0.1",
      "description": "my first socket.io app",
      "dependencies": {}
    }
    这个package.json是为node.js服务的,所以如果有什么疑惑可以去查查node.js的资料,在这里我不再深入。


  • (2)关于服务端index.js的解析:

  • var app = require('express')();
    var server = require('http').Server(app);
    先看下这两句,表示初始化程序是一个处理函数,你可以根据这个处理函数提供一个HTTP服务器(server)。

  • server.listen(8888, function(){
    	console.log('listening on *:8888');
    });
    让我们的服务器监听8888端口,当成功监听这个端口的时候输出一个信息就是console.log里面的内容。

  • app.get('/', function(req, res){
    	res.send('<h1>Hello Wellcome</h1>');
    });
    这句应该号理解,设置我们的主页,也就是打开这个端口的链接的时候,res代表的就是request,res代表的就是response,这下应该号理解了吧。

  • 从某种角度来说,这样的写法不是很好,总部要吧整个网页都写进去吧?官方也提供了一种写法,如下:

  • app.get('/', function(req, res){
      res.sendFile(__dirname + '/index.html');
    });
    这样就可以吧index.html这文件推送给访问的客户端。可能你会好奇这个index.html放在哪?仔细想想就知道了,反正我是在这个目录:D:\SocketJsTest。我们再看下下面的这句代码:

  • var io = require('socket.io')(server);
    这句什么意思?官网是这么说的:

  • Notice that I initialize a new instance of socket.io by passing the http (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);
注意:这里得socketid,是Socke.IO自己生成的随机得一个值





















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值