这篇文章是看国外的一篇博客写的,觉得国内的博客没有这篇写的这么深入浅出,所以决定翻译过来,让英文不好的童鞋们看起来更方便一些。只是按照原文大概的翻译哈。
原文链接:http://danielnill.com/nodejs-tutorial-with-socketio/
注:本示例是在node v6.2.0 和socket.io 1.4.8下写的,其他版本如果有运行不出来的请自行查看相关说明文档。
这篇文章要做什么
这篇博客会告诉你们怎么:
- 搭建一个Node.JS 服务器以及路由(刚做web开发的我对路由在web服务器怎么实现不是很懂,看了这个一目了然 router)
- 用socket.io和这个服务器连接
- 从服务器发送数据到客户端
- 从客户端发送数据到服务器
一个基础的服务器
这个其实官网上就有例子,而且很简单。但是我还是跟着他从这里开始敲了。你会看到一个服务器从最简单是怎么开始的。当然,这里原文说有很多模块可以用来给socket.io搭建服务器,但是都大同小异。我们这里还是使用Node的http库,新建一个文件,重命名为server.js,敲入以下代码:
var http = require('http');
var server = http.createServer();
server.listen(8001);
OK,到这里最简单的Node服务器就搭建起来了。在控制台里面cd到这个文件的文件夹,再使用命令node server.js,这个服务器就启动了。当然这时候你是看不到什么的,打开浏览器输入http://127.0.0.1:8001也不会看到什么东西。好吧,那可以在上面这段代码里面加一句话,说明这段代码运行了。像这样:
var http = require('http');
var server = http.createServer();
server.listen(8001);
console.log('服务器已经启动 地址:http://127.0.0.1:8001');
这样你在控制台输入node server.js的时候就能够看到输出的信息了。或者你也可以写得更Node Style一点,像这样:
var http = require('http');
var server = http.createServer();
server.listen(8001, function(){
console.log('服务器已经启动 地址:http://127.0.0.1:8001');
});
但是我们打开浏览器还是什么都看不到,那么再来做一些修改,让它能够在浏览器输出一些文字:
var http = require('http');
var server = http.createServer(function(request, response){
response.writeHead('Content-Type': 'text/html');
response.write('Hello From Node');
response.end();
});
server.listen(8001, function(){
console.log('服务器已经启动 地址:http://127.0.0.1:8001');
});
现在保存server.js然后在控制台node server.js,然后打开浏览器并在地址栏键入:http://127.0.0.1:8001然后就可以看到浏览器上显示了“Hello From Node”。
每次我们访问http://127.0.0.1:8001的时候,在createServer里面的函数就会运行。传给createServer的参数是一个回调函数,会给浏览器响应,而我们这里的响应就是给浏览器发送一个字符串。
给服务器加上路由
现在我们的服务器只能相应跟地址的请求,而其他路径是响应不到的,所以我们在这里改我们的server.js,给它加上路由:
var http = require('http');
var url = require('url');
var fs = require('fs');
var server = http.createServer(function(requset, response){
var path = url.parse(request.url).pathname;
switch(path){
case '/':
response.writeHead(200, {'Content-Type': 'text/html'});
response.write('Hello From Node');
response.end();
break;
case '/socket.html':
fs.readFile(__dirname + path, function(error, data){
if(error){
response.writeHead(404);
response.write("opps this doesn't exist - 404");
response.end();
}
else{
response.writeHead(200, {'Content-Type': "text/html"});
response.write(data, "utf-8");
response.end();
}
});
break;
default:
response.writeHead(404);
response.write("opps this doesn't exist - 404");
response.end();
break;
}
});
server.listen(8001, function(){
console.log('服务器已经启动 地址:http://127.0.0.1:8001');
});
保存我们的server.js文件,然后运行node server.js。这时候打开浏览器,输入http://127.0.0.1:8001,和之前没有任何变化。好吧,不过如果你输入http://127.0.0.1:8001/socket.html,就会开到一个404页面,也就是说我们还没有socket.html这个文件。那么先来看看我们做了哪些改变:
var url = require('url');
var fs = require('fs');
这两行代码,我们引入了两个新的模块,一个是url,一个是fs——file system。url模块是用来解析url的,更多更仔细的说明:[url参考文档](http://nodejs.org/docs/v0.3.1/api/url.html)。fs模块是用来操作文件的,它的说明:[fs模块参考文档](http://nodejs.org/api/fs.html)。
var path = url.parse(request.url).pathname;
这行代码会把跟在根路径之后的地址赋给path。如果不清楚url.parse()这个函数是干嘛的,可以去查一查文档。所以说,如果输入的是http://www.baidu.com/。那么path就等于'/',如果输入http://127.0.0.1/socket.html,那么path就等于'/socket.html'。如果你想看看path到底等于什么,可以在这行下面加上console.log(path)。
接下来的代码里,我们用了一个switch语句,用来根据path的值决定我们响应的方式。如果是http://127.0.0.1,也就是path是'/'的时候,还是会响应一个字符串:"Hello From Node",如果地址栏输入的是http://127.0.0.1/socket.html的话,就会把socket.html这个页面返回给浏览器。这里
fs.readFile(function(error, data){...});
是一个异步方法。如果读取成功,就会运行error里面的代码,如果成功,就把socket.html这个页面返回给客户端。
所以我们可以在server.js同一目录下新建一个socket.html文件,看看是否能够成功进到这里。
<html>
<head></head>
<body>这就是socket.html页面</body>
</html>
保存socket.html,确保server.js还在运行,在浏览器输入http://127.0.0.1:8001 就可以看到我们写的页面了。好了,到这里为止,我们已经给我们的服务器添加了路由。接下来就要加上socket.io模块了。
添加socket.io模块
首先,当然是要安装socket.io的。如果你已经安装了npm的话,直接在你的server.js和socket.html的文件夹下,用命令npm install socket.io --save就可以了。如果你还没安装npm,请先安装npm。
首先要引用这么模块,然后初始化之。我们的server.js现在看起来应该是这样。注意第四行和最后一行。
var http = require("http");
var url = require('url');
var fs = require('fs');
var io = require('socket.io');
var server = http.createServer(function(request, response){
var path = url.parse(request.url).pathname;
switch(path){
case '/':
response.writeHead(200, {'Content-Type': 'text/html'});
response.write('hello world');
response.end();
break;
case '/socket.html':
fs.readFile(__dirname + path, function(error, data){
if (error){
response.writeHead(404);
response.write("opps this doesn't exist - 404");
response.end();
}
else{
response.writeHead(200, {"Content-Type": "text/html"});
response.write(data, "utf8");
response.end();
}
});
break;
default:
response.writeHead(404);
response.write("opps this doesn't exist - 404");
response.end();
break;
}
});
server.listen(8001);
io.listen(server);
第四行是引入socket.io这个包,最后一行是说当这个服务器启动的时候,就给socket.io一个监听的实例。每当客户端有websocket请求的时候,就交给socket.io来处理。
现在服务器已经做好处理websocket请求的准备了,但是客户端还没有做好准备,所以我们要修改我们的socket.html代码:
<html>
<head>
<script src="/socket.io/socket.io.js"></script>
</head>
<body>
<script>
var socket = io.connect();
</script>
<div>This is our socket.html file</div>
</body>
</html>
注意这里的<script src="/socket.io/socket.io.js"></script>并不是socket.io.js的相对地址,我也不知道什么机制就能够引用到的,baidu了一下,并没有确切答案,又不能FQ,反正这么写没毛病。
这样就做好准备了,下面就要从服务器向客户端发送消息了。
从服务器向客户端发送消息
现在的目标是要从服务器向客户端发送消息。所有的消息都通过socket.io发送,在浏览器也使用socket.io接收。我们用Node最常用的方式,用on来监听一个事件,用emit发送事件,这样就能够完成数据的发送和接收了。具体看代码。
server.js:
var http = require("http");
var url = require('url');
var fs = require('fs');
var io = require('socket.io');
var server = http.createServer(function(request, response){
var path = url.parse(request.url).pathname;
switch(path){
case '/':
response.writeHead(200, {'Content-Type': 'text/html'});
response.write('hello world');
response.end();
break;
case '/socket.html':
fs.readFile(__dirname + path, function(error, data){
if (error){
response.writeHead(404);
response.write("opps this doesn't exist - 404");
response.end();
}
else{
response.writeHead(200, {"Content-Type": "text/html"});
response.write(data, "utf8");
response.end();
}
});
break;
default:
response.writeHead(404);
response.write("opps this doesn't exist - 404");
response.end();
break;
}
});
server.listen(8001);
var listener = io.listen(server);
listener.sockets.on('connection', function(socket){
socket.emit('message', {'message': 'hello world'});
});
这里我们在代码的最后面加上了事件的监听和发送。每次有一个客户端连上服务器的时候,都会有一个'connection'事件产生。在服务器接收到'connection'事件的时候,就会调用函数来想客户端发送一个json格式的消息:socket.emit('message',{'message','hello world'});。
同样,在客户端我们也要进行相应的处理,才能够接收到服务器传来的数据。可以写成这样:
socket.html:
<html>
<head>
<script src="/socket.io/socket.io.js"></script>
</head>
<body>
<script>
var socket = io.connect();
socket.on('message', function(data){
console.log(data.message);
});
</script>
<div>This is our socket.html file</div>
</body>
</html>
在客户端,我们也是用socket.on()来接收'message'消息的。下面的console.log()会在浏览器的控制台打印相应的字符串。现在重新运行server.js然后刷新你的页面,在浏览器按下F12,就可以看到控制台上面打印出来hello world了。这说明服务器已经成功向客户端发送消息。
当然,我们可以做一点更有意思的事情,比如每一秒钟,向客户端发送一条消息。有一个函数是setInterval(),可以指定间隔多少毫秒做一件事情。所以,我们可以把server.js的代码改成:
server.listen(8001);
var listener = io.listen(server);
listener.sockets.on('connection', function(socket){
//send data to client
setInterval(function(){
socket.emit('date', {'date': new Date()});
}, 1000);
});
这样我们就每一秒钟给客户端发送当前的时间。当然,socket.html的代码也要做修改:
<html>
<head>
<script src="/socket.io/socket.io.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.js"></script> </head>
<body>
<script>
var socket = io.connect();
socket.on('date', function(data){
$('#date').text(data.date);
});
</script>
<div id="date"></div>
</body>
</html>
这样再次运行,就会看到div里面显示的时间每秒钟跳一次。
这里有一点需要注意:引用jquery如果是使用本地的jquery.js文件的话,会出现404的错误。这是因为我们在设置路由的时候,如果不是’/socket.html’的路径,我们都把它转到404了,所以会找不到jquery.js文件。需要对路由稍作修改,就能够找到了。在switch里面加上:
switch(path){
...
case '/jquery.js':
fs.readFile(__dirname + path, function(error, data){
if (error){
response.writeHead(404);
response.write("opps this doesn't exist - 404");
response.end();
}
else{
response.writeHead(200, {"Content-Type": "text/html"});
response.write(data, "utf8");
response.end();
}
});
break;
...
}
从客户端向服务端发送消息
从客户端向服务端发送消息的形式也差不多,只不过在客户端用socket.emit()然后在服务端用socket.on()接收。
socket.html:
<html>
<head>
<script src="/socket.io/socket.io.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.js"></script>
</head>
<body>
<script>
var socket = io.connect();
socket.on('date', function(data){
$('#date').text(data.date);
});
$(document).ready(function(){
$('#text').keypress(function(e){
socket.emit('client_data', {'letter': String.fromCharCode(e.charCode)});
});
});
</script>
<div id="date"></div>
<textarea id="text"></textarea>
</body>
</html>
server.js:
var listener = io.listen(server);
listener.sockets.on('connection', function(socket){
//send data to client
setInterval(function(){
socket.emit('date', {'date': new Date()});
}, 1000);
//recieve client data
socket.on('client_data', function(data){
process.stdout.write(data.letter);
});
});
再次运行,在textarea中敲入字母,在控制台里面就能够看到。