MEAN Web 开发 #8:Sockets 将让你大吃一惊!






4.95/5 (5投票s)
MEAN Web开发系列文章的第八篇。
本周我计划简短进行。我们将探讨Socket以及如何使用Node.js来利用它们。幸运的是,这对于Node.js而言是一项相对容易的任务,它以易用性而闻名。Socket使您能够构建实时网站,而使用HTTP做到这一点充其量是困难的。
这也是本系列中我们将要了解新技术的最后一篇文章。如果您一直读到现在,那么您已经走上了成为一名MEAN Web开发者的道路!下周我们将进行总结,之后,谁知道呢
- MEAN Web 开发 #1:MEAN 是什么以及为什么使用它
- MEAN Web 开发 #2:Node.js 在后端
- MEAN Web 开发 #3:更多 Node.js
- MEAN Web 开发 #4:Node.js Express 全面起航!
- MEAN Web 开发 #5:Jade 和 Express
- MEAN Web 开发 #6:AngularJS 在前端
- MEAN Web 开发 #7:MongoDB 和 Mongoose
- MEAN Web 开发 #8:Sockets 将让你大吃一惊!
- MEAN Web 开发 #9:一些最后的说明
像往常一样,您可以在我的GitHub页面下的mean8-blog存储库中找到本文的代码示例。
什么是Socket?
我不会深入探讨Socket或网络Socket的技术细节。重要的是要知道,Socket本质上是一个端点连接,它允许网络中的计算机之间进行通信。在较早的文章Web开发 #1:互联网和万维网中,我简要讨论了互联网协议TCP/IP(传输控制协议/互联网协议),并且提到了UDP(用户数据报协议)等其他协议。这些协议利用Socket通过Web传输数据。
正如我们所见,HTTP(和HTTPS)之类的协议利用TCP/IP协议,而TCP/IP协议又利用Socket。众所周知,HTTP是单向的,尽管数据流不是。想一想,使用HTTP我们可以请求数据,但是数据也必须发回给客户端。HTTP无法做到的,但Socket显然可以,就是将数据发送给客户端。那么,即使客户端没有(在那一刻)请求数据,服务器是否也能将数据发送给客户端?别再寻找了!这正是Socket编程越来越受欢迎的原因!
用于这种双向数据交换的协议称为WebSockets,并于2011年标准化。由于这仍然是相当新的技术,请记住,较旧的Web浏览器不支持此协议。需要注意的一点是,WebSockets实际上是HTTP的升级。Socket连接实际上是使用HTTP请求建立的。
Node.js使得处理Socket变得非常容易。实际上,市面上已经有很多教程了,其中大多数都允许您构建聊天应用程序。当然,您可以构建任何需要实时数据的应用程序。例如实时的Facebook或Twitter更新。特别是考虑到最近的物联网(IoT),其中一切都与Web相连,这一点尤其有趣。假设有人按了门铃,门铃已连接到Web,并在有人按门铃时向您发送一张门廊照片。您会想使用HTTP来不断轮询/请求是否有人刚按了门铃吗?您不会!在这种情况下,Socket是必不可少的!
Socket和Node.js
因此,让我们设置一个支持Socket的小型Node.js服务器。我们可以为此使用“纯粹的”WebSockets,但我将使用一个名为socket.io的库。这将使我们在后端和前端的生活都轻松很多。socket.io是WebSockets之上的一个层,它简化了API,更重要的是,如果不支持WebSockets,它会回退到其他方法。
我们将开始创建一个提供HTML页面的Node.js服务器。我们之前做过几次,所以这应该不是问题。请记住,WebSockets需要HTTP才能工作,因此我们还需要HTTP模块。而且,一如既往,我也会使用Express。
var express = require('express');
var app = express();
var http = require('http').Server(app);
app.use(express.static('public'));
app.get(['/', '/index'], function (req, res) {
res.sendFile(__dirname + '/public/client.html');
});
http.listen(80, '127.0.0.1');
之后,我们可以安装socket.io(npm install socket.io)。
var express = require('express');
var app = express();
var http = require('http').Server(app);
var io = require('socket.io')(http);
app.use(express.static('public'));
app.get(['/', '/index'], function (req, res) {
res.sendFile(__dirname + '/public/client.html');
});
io.on('connection', function(socket){
console.log('A user connected!');
});
http.listen(80, '127.0.0.1');
正如您所看到的,http对象用于创建socket.io实例。之后,我们可以监听连接事件以处理传入的Socket。
现在socket.io会做一些神奇的操作,它会自动将socket.io提供给我们的前端。这意味着在客户端创建Socket实例非常容易。首先,我们需要在某个地方包含脚本。
<html>
<head>
<meta charset="utf-8">
<title>Sockets example</title>
<script src="/socket.io/socket.io.js"></script>
<script src="client.js"></script>
</head>
<body>
<h1>Sockets example</h1>
</body>
</html>
然后,我们可以在client.js中使用Socket。
var socket = io();
哇,这太容易了!
您会注意到,如果您将该文件放在/public/client.html中并访问localhost,Node.js会在控制台中记录“A user connected!”!所以我们已经连接上了。
现在,我们之前在MEAN Web开发 #3:更多Node.js中讨论过EventEmitters。socket.io广泛使用它们。每个Socket都有一个特殊的“disconnect”事件。
io.on('connection', function(socket){
console.log('A user connected!');
socket.on('disconnect', function () {
console.log('A user disconnected...');
});
});
如果您尝试这样做,访问localhost,然后刷新页面,您将在控制台中看到“A user connectioned!”、“A user disconnected…”和“A user connected!”。所以这已经工作得很好了,对吧?
让我们添加另一个事件。您知道我喜欢音乐,而专辑对我来说一直是喜欢的测试对象。
io.on('connection', function(socket){
console.log('A user connected!');
socket.on('disconnect', function () {
console.log('A user disconnected...');
});
socket.on('add-album', function (album) {
console.log(album);
});
});
“等一下!”您可能会说,“一个Socket不可能有一个add-album事件!”嗯,现在还没有……让我们再看看我们的前端JavaScript。
var socket = io();
socket.emit('add-album', {
artist: 'Led Zeppelin',
title: 'Led Zeppelin III'
});
保存它,再次访问localhost,控制台肯定会打印出专辑。这太棒了!
但是我们需要更多。毕竟,我们想在客户端接收数据!当然,除非我们在页面上显示数据,否则我们看不到任何数据。
让我们先看看服务器。这真的很简单。准备好了吗?
io.emit('add-album', album);
很好,我们可以简单地使用io.emit,所有连接都将收到更新。因此,我们的客户端将一个“add-album”事件发送到服务器,服务器再将一个“add-album”事件发送回客户端。我们可以使用任何名称作为事件。
让我们看看前端。我添加了一些AngularJS,我在MEAN Web开发 #6:前端AngularJS中已经讨论过它。实际上,我从那篇文章中复制了这个例子,并将“book”改为了“album”。
<html>
<head>
<meta charset="utf-8">
<title>Sockets example</title>
<script src="angular.min.js"></script>
<script src="/socket.io/socket.io.js"></script>
<script src="client.js"></script>
</head>
<body ng-app="socketsApp">
<div ng-controller="socketsController">
<h1>Sockets example</h1>
<ul>
<li ng-repeat="album in albums">
{{ album.artist + ' - ' + album.title }}
</li>
</ul>
<input type="text" ng-model="newArtist" />
<input type="text" ng-model="newTitle" />
<button ng-click="addAlbum()">Add album</button>
</div>
</body>
</html>
这是JavaScript。
angular.module('socketsApp', [])
.controller('socketsController', function ($scope) {
var socket = io();
$scope.newArtist = null;
$scope.newTitle = null;
$scope.albums = [];
$scope.addAlbum = function () {
socket.emit('add-album', {
artist: $scope.newArtist,
title: $scope.newTitle
});
$scope.newArtist = null;
$scope.newTitle = null;
};
socket.on('add-album', function (album) {
$scope.$apply(function () {
$scope.albums.push(album);
});
});
});
请注意AngularJS的$apply函数。这是一个细节,但我需要它来在AngularJS上下文中执行代码,以便视图能够立即更新。
真正重要的是socket.on函数。我不需要解释它,您知道它的工作原理。它与您在服务器上使用的代码完全相同!
现在打开两个标签页,两个浏览器,两个窗口,两个任何东西,然后访问localhost。在一个窗口中输入艺术家和标题,然后提交。然后切换到另一个窗口,您将在另一个窗口中看到您刚刚添加的专辑!无需刷新,什么都不用做!如果您真的使用了两个窗口,请将它们并排放在一起,并实时查看效果。
现在您可能会认为这很棒,但是没有必要将刚刚发送对象的客户端发送回客户端。为此,您可以使用socket.broadcast.emit。这将向所有Socket发送消息,除了正在广播的那个。
socket.broadcast.emit('add-album', album);
前端现在看起来是这样的。
$scope.addAlbum = function () {
var album = {
artist: $scope.newArtist,
title: $scope.newTitle
};
socket.emit('add-album', album);
$scope.albums.push(album);
$scope.newArtist = null;
$scope.newTitle = null;
};
太棒了!
房间
假设您正在构建我们之前谈论的聊天应用程序(实际上我们在这里创建的也是)。现在您可能希望您的网站拥有多个聊天室。一个用于C#讨论的房间,一个用于JavaScript讨论的房间,或者人们可以创建自己的关于任何主题(可能是汽车、音乐、电影)的房间。也许您还想实现私有聊天(一对一,或仅限受邀者)。您该如何做到这一点?
我将保留之前的例子,开始一个新的例子。从我们的页面开始,我们应该能够创建房间、加入房间、发送消息和离开房间。所以让我们来看看Node.js服务器端。
io.on('connection', function(socket){
console.log('A user connected!');
socket.on('disconnect', function () {
console.log('A user disconnected...');
});
socket.on('add-room', function (room) {
console.log('Added:');
console.log(room);
socket.join(room.name);
socket.broadcast.emit('add-room', room);
});
socket.on('join-room', function (room) {
console.log('Joined:');
console.log(room);
socket.join(room);
});
socket.on('send-message', function (message) {
console.log('Send:');
console.log(message); socket.broadcast.to(message.roomName).emit('receive-message', message);
});
socket.on('leave-room', function (room) {
console.log('Leave:');
console.log(room);
socket.leave(room);
});
});
这代码量不小,但实际上没什么新东西!当有人添加房间时,我们只是将房间广播给所有其他客户端,这样每个人都可以看到新房间。此外,我们使用socket.join“加入”房间。这创建了一个可以立即通知的Socket组。当用户加入房间时,我们只需传入一个房间名称,然后再次调用socket.join。然后,当有人向房间发送消息时,我们只需调用socket.broadcast.to(roomName),现在只有加入该特定组的客户端才会收到通知。这很容易,对吧?当我们离开房间时,我们只需调用socket.leave,客户端将停止接收来自该特定房间的通知。
此示例的客户端脚本并不是很有趣。您可以在GitHub上获取完整的示例。是的,我承认,HTML可以再改进一下。JavaScript的工作方式与上一个示例相同。我们只需使用socket.emit将事件发送到服务器,并使用socket.on从服务器接收事件。我将把添加用户名、持久化聊天室、获取活动房间列表、实现私人房间等留给读者练习。
下周我们将结束MEAN Web编程系列!祝您编码愉快!
文章 MEAN Web开发 #8:Socket让您大开眼界! 最先出现在 Sander's bits。