Skip to content

Latest commit

 

History

History
234 lines (164 loc) · 8.65 KB

File metadata and controls

234 lines (164 loc) · 8.65 KB

十六、SocketIO 和 Express.js

SocketIO ( http://socket.io)是一个库,它提供了在客户机和服务器之间实时建立双向通信的能力。这种双向通信是由 WebSocket 技术驱动的。

WebSocket 技术在大多数现代浏览器中都可以使用,最简单的概念化方法是想象浏览器和服务器之间的持续连接,而不是传统的零星 HTTP 请求。WebSocket 和传统 HTTP 请求的另一个区别是,前者是一个双向通道,这意味着服务器可以发起数据传输。这对于实时更新页面非常有用。

WebSocket 并不是实时类系统的唯一选择。您可以使用轮询实现接近实时的系统,这是指浏览器代码在很短的时间间隔内(例如,100 毫秒)发出大量 HTTP 请求。和 SocketIO 不是唯一可以用来实现 WebSocket 的库。在 Practical Node.js (Apress,2014)中,我展示了一个使用ws作为服务器库和一个本地(根本没有库)浏览器 API 的例子。但是,使用 Socket 的好处。IO 在于它具有广泛的、跨平台的、跨浏览器的支持;WebSocket 不可用时回退到轮询;并使用事件作为它的主要实现模式。

插座全覆盖。IO 图书馆值得拥有自己的书。然而,用 Express.js 开始使用是如此的酷和容易,以至于我包含了这一章来向你展示一个基本的例子。

使用插座。超正析象管(Image Orthicon)

该示例将实时反向回显(浏览器到服务器,然后返回)我们的输入。这意味着我们将构建一个 Express.js 服务器,其中包含一个包含表单输入字段的 web 页面。该网页还将有一些前端 JavaScript 代码,在我们键入时将输入字段字符实时传输到服务器。服务器将反转字符串并将其发送回浏览器,我们的前端 JavaScript 代码将最终字符串打印到浏览器控制台。我们将使用插座。所有这些功能的 IO 方法和事件监听器。

在最终产品中,如果您键入“!stekcoS,”应用会将其转换为“插座!”如图图 16-1 所示。并且将显示浏览器控制台输出

!
sending ! to server
received !
!
sending !
received !
!s
sending !s to server
received s!
...

9781484200384_Fig16-01.jpg

图 16-1 。的投入!stekcoS 出品插座!

可以从一个$ express socket创建的 fresh Express.js app 开始,也可以从ch16/socket文件夹下载源代码。然后,用$ cd socket && npm install安装依赖项。

Image 提示如果需要 Express.js 3.x 的例子,可以参考这个 app 为 Express.js 3.3.5 编写的版本。源代码在 GitHub repo: https://github.com/azat-co/proexpressjs/tree/master/ch16/socket-express3

要包含 Socket.io,我们可以使用:

$ npm install socket.io@1.1.0 --save

或者,我们可以使用package.json:

{
  "name": "socket-app",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "start": "node app.js"
  },
  "dependencies": {
    "errorhandler": "1.1.1",
    "express": "4.8.1",
    "jade": "1.5.0",
    "morgan": "1.2.2",
    "serve-favicon": "2.0.1",
    "socket.io": "1.1.0"
  }
}

插座。从某种意义上来说,IO 可以被认为是另一个服务器。我们可以通过将 Express.js app对象传递给createServer()方法,然后调用套接字来重构自动生成的 Express.js 代码。木卫一listen()方法上server物体:

var server = http.createServer(app);
var io = require('socket.io').listen(server);

//...
server.listen(app.get('port'), function(){
  console.log('Express server listening on port '
  + app.get('port'));
});

io对象被实例化后,下面的代码可以用来建立一个连接:

io.sockets.on('connection', function (socket) {

一旦建立了连接——我们知道这是因为我们在回调中——我们附加了messageChange事件监听器,它将由浏览器上的用户操作触发:

socket.on('messageChange', function (data) {
  console.log(data);

一旦消息被更改(例如,用户输入了一些内容),我们可以反转字符串并以带有socket.emit():receive事件的形式将其发送回来

    socket.emit('receive',
    data.message.split('').reverse().join('') );
  })
});

下面是ch16/socket/app.js的全部内容:

var http = require('http'),
  express = require('express'),
  path = require('path'),
  logger = require('morgan'),
  favicon = require('serve-favicon'),
  errorhandler = require('errorhandler'),
  bodyParser = require('body-parser');

var app = express();

app.set('view engine', 'jade');
app.set('port', process.env.PORT || 3000);
app.use(logger('combined'));
app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(express.static('public'));

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));

app.get('/', function(request, response){
  response.render('index');
});

app.use(errorhandler());

var server = http.createServer(app);
var io = require('socket.io').listen(server);

io.sockets.on('connection', function (socket) {
  socket.on('messageChange', function (data) {
    console.log(data);
    socket.emit('receive', data.message.split('').reverse().join('') );
  });
});

server.listen(app.get('port'), function(){
  console.log('Express server listening on port ' + app.get('port'));
});

最后,我们的应用在index.jade中需要一些前端程序,以输入框和 JavaScript 代码的形式发送和接收用户输入。输入框可以这样实现:

  input(type='text', class='message', placeholder='what is on your mind?', onkeyup='send(this)')

在每个按键事件中,它都调用send()函数,但是在我们编写这个函数之前,让我们包含这个套接字。IO 库:

  script(src="/socket.io/socket.io.js")

socket.io.js文件不是您需要下载的真实文件,例如 jQuery 文件。Node.js 套接字。IO 服务器将“自动”提供这个文件

现在,我们可以连接到 socket 服务器并附加receive事件监听器:

var socket = io.connect('http://localhost');
socket.on('receive', function (message) {
  console.log('received %s', message);
  document.querySelector('.received-message').innerText = message;
});

document.querySelector只是现代浏览器对 jQuery 选择器的模拟。它只是给了我们一个 HTML 元素,这样我们就不需要依赖 jQuery 了。我们使用innerText属性在页面上显示新的文本。

send函数发出将消息传递给服务器的事件messageChange。这种情况在每次击键时都会发生,因此变化会实时出现。

var send = function(input) {
  console.log(input.value)
  var value = input.value;
  console.log('sending %s to server', value);
  socket.emit('messageChange', {message: value});
    }

以下是ch16/socket/index.jade的完整源代码:

extends layout

block content
  h1= title
  p Welcome to
    span.received-message #{title}
  input(type='text', class='message', placeholder='what is on your mind?', onkeyup='send(this)')
  script(src="/socket.io/socket.io.js")
  script.
    var socket = io.connect('http://localhost');
    socket.on('receive', function (message) {
      console.log('received %s', message);
      document.querySelector('.received-message').innerText = message;
    });
    var send = function(input) {
      console.log(input.value)
      var value = input.value;
      console.log('sending %s to server', value);
      socket.emit('messageChange', {message: value});
    }

运行应用

现在一切都应该准备好了,你可以启动应用了($ node app)。转到主页(http://localhost:3000)输入“!stekcoS”,应用会将其转换为“插座!”如前图 16-1 所示。

当您查看浏览器输出时,您可能会认为代码只是在客户端进行了转换,而没有服务器的参与。事实并非如此,证据在服务器日志中,你会看到转换实际上发生在服务器上(见图 16-2 )。

9781484200384_Fig16-02.jpg

图 16-2 。Express.js 服务器实时捕捉和处理输入

更多 SocketIO 示例,转到http://socket.io/#how-to-use

摘要

同样,您已经看到了 Express.js 与另一个库的无缝集成。在这个双通道通信的简短示例中,我们使用了来自套接字的事件侦听器。IO 库。该库兼容 Node.js 和浏览器 JavaScript,这使得它更易于使用。SocketIO 为实时应用提供了良好的跨浏览器支持。它与 Express.js 堆栈无缝集成,与使用 WebSocket 的原生浏览器 API 相比具有许多优势。

在下一章,我们将讨论如何使用域来更好地处理异步错误。