65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.95/5 (5投票s)

2015 年 9 月 13 日

CPOL

8分钟阅读

viewsIcon

14001

MEAN Web开发系列文章的第八篇。

本周我计划简短进行。我们将探讨Socket以及如何使用Node.js来利用它们。幸运的是,这对于Node.js而言是一项相对容易的任务,它以易用性而闻名。Socket使您能够构建实时网站,而使用HTTP做到这一点充其量是困难的。
这也是本系列中我们将要了解新技术的最后一篇文章。如果您一直读到现在,那么您已经走上了成为一名MEAN Web开发者的道路!下周我们将进行总结,之后,谁知道呢 :)

  1. MEAN Web 开发 #1:MEAN 是什么以及为什么使用它
  2. MEAN Web 开发 #2:Node.js 在后端
  3. MEAN Web 开发 #3:更多 Node.js
  4. MEAN Web 开发 #4:Node.js Express 全面起航!
  5. MEAN Web 开发 #5:Jade 和 Express
  6. MEAN Web 开发 #6:AngularJS 在前端
  7. MEAN Web 开发 #7:MongoDB 和 Mongoose
  8. MEAN Web 开发 #8:Sockets 将让你大吃一惊!
  9. 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

© . All rights reserved.