ホームページ  >  記事  >  ウェブフロントエンド  >  Node.jsでのsocket.io学習チュートリアルの紹介(3)

Node.jsでのsocket.io学習チュートリアルの紹介(3)

零下一度
零下一度オリジナル
2017-05-03 10:02:191149ブラウズ

この記事では、socket.io の基本的なチュートリアルとアプリケーションをさらに詳しく紹介します。必要な方は、この記事を参照してください。参考までに、以下を見てみましょう。

はじめに

socket.io はイベントに基づいたリアルタイムの双方向通信を提供します。この記事では、socket.io について詳しく紹介します。

静的ファイル

socket.ioは、デフォルトでsocket.io-clientパッケージを通じてsocket.io.min.jsとsocket.io.js.mapのダウンロードを提供します

インスタンスapp.jsを実行します

let app = require('http').createServer() 
let io = require('socket.io')(app)

app.listen(3000);

http://localhost:3000/socket.io/socket.io.js にアクセスすると、ブラウザで圧縮されたソース コードをロードできます。マップを使用してロードします

この動作を変更できます

socket.io.js のダウンロードを無効にします

方法 1: インスタンス化するときにコントロール パラメーターserveClient 値 false を渡します

let io = require('socket.io')(app, { 
 serveClient: false
})

方法 2: function serverClient

let app = require('http').createServer() 
let io = require('socket.io')() 
io.serveClient(false) 
io.listen(app) // 或者io.attach(app)

関数を呼び出す前にサービスが http.Server にバインドされている場合、このメソッドは無効にした後に再度アクセスすると、{"code":0, というプロンプトが表示されます。 "message":"不明なトランスポート"}http.Server,该方法将不起作用

禁用后再次访问将提示{"code":0,"message":"Transport unknown"}

修改静态文件路径

socket.io.js路径可以改变,其默认路径为/socket.io。

实例化时传参

let io = require('socket.io')(app, { 
 path: '/io'
})

调用函数path

let app = require('http').createServer() 
let io = require('socket.io')() 
io.path('/io') 
io.listen(app)

如果在调用函数前服务已绑定http.Server,该方法将不起作用

安全策略

socket.io提供了两种安全策略

allowRequest

函数allowRequest有两个参数,第一个参数为收到的握手包(http.request)对象,作为判断依据, success), err是错误对象,success为boolean, false表示阻止建立连接

前端请求带上token

let socket = io('http://localhost:3000?token=abc') 
socket.on('connect', () => { 
 console.log('connect')
})
socket.on('connect_error', err => { 
 socket.disconnect()
 console.log('connect_error', err)
})

后端allowRequest根据token判断是否继续

let app = require('http').createServer() 
let io = require('socket.io')(app, { 
 allowRequest: (req, cb) => {
 if (req._query && req._query.token === 'abc') return cb(null, true)
 cb(null, false)
 }
});

origins

可以对源进行限制

1、实例化时限制源

let app = require('http').createServer() 
let io = require('socket.io')(app, { 
 origins: 'http://localhost:3000'
})

2、origins函数设置源

origins函数有两种形式

origins(string) : 设置运行的源

origins(string, fn(err, success)) : 通过函数判断源是否允许

io.origins('http://localhost:*')

io.origins((origin, cb) => { 
 if (origin === 'http://localhost:3000/') return cb(null, true)
 cb(null, false)
})

名称空间

名称空间用来对服务端/客户端的连接隔离,有些地方,也称呼名称空间(namespace)为通道(channel)。下面举例对其意义进行说明

我们需要实现一个协同应用,这个应用有两个功能:

  • 协同编辑: 多个用户可以同时编辑一个文档

  • 消息: 用户间可以发送消息

用socket.io实现这个应用,有如下几种形式

1、完全独立: 协同编辑有一个独立服务edit.socket.test ,消息系统一个独立服务message.socket.test

let editSocket = io('edit.socket.test') 
let messageSocket = io('message.socket.test')

2、名称空间: 只运行一个独立服务,通过名称空间进行隔离

let app = require('http').createServer() 
let io = require('socket.io')(app) 
let editServer = io.of('/edit') 
let messsageServer = io.of('/message') 
editServer.on('connection', socket => { 
 //编辑相关
})
messsageServer.on('connection', socket => { 
 /消息相关
})
let editSocket = io('socket.test/edit') 
let messageSocket = io('socket.test/message')

3、事件名约定: 通过为事件名添加进行隔离

let app = require('http').createServer() 
let io = require('socket.io')(app)

io.on('connection', socket => { 
 //编辑相关
 io.emit('edit:test')
 io.on('edit:test', data => {

 })
 //消息相关
 io.emit('message:test')
 io.on('message:test', data => {

 })
}

通过事件名约定程序的侵入性太大,不利于拆分和重组,不推荐。 而完全独立的模式需要使用两个socket连接,即浪费浏览器允许的并发连接数,又更多消耗服务器资源。使用名称空间即能实现很好的隔离,又不会对资源造成浪费。

默认名称空间

socket.io实例化时自动绑定路径为/的名称空间

let app = require('http').createServer() 
let io = require('socket.io')(app)

io.sockets // io.of('/').sockets 
io.emit // 代理io.of('/').emit, 类似函数有'to', 'in', 'use', 'send', 'write', 'clients', 'compress'

中间件

socket.io的名空间通过use注册中间件,中间件在客户端与服务端建立连接成功后,connet事件派发前调用一次。

利用中间件数据校验

io.use((socket, next) => { 
 if (socket.request.headers.cookie) return next()
 next(new Error('Authentication error'))
})

利用中间件提取或转换数据 io.use((socket, next) => { <br>getInfo(socket.request.query.id, (err, data) => { if (err) return next(err) socket.custom = data next() }) })

静的ファイルのパスを変更します

socket.io.js のパスは変更できます。デフォルトのパスは /socket.io です。
  • インスタンス化中にパラメータを渡す

    socket.join(&#39;room name&#39;) //进入 
    socket.leave(&#39;room name&#39;) //退出

    関数のパスを呼び出す
  • io.to(&#39;some room&#39;).emit(&#39;some event&#39;) // io.to与io.in同义,向某个聊天室的所有成员发送消息
  • 関数を呼び出す前にサービスが http.Server にバインドされている場合、このメソッドは機能しません


  • セキュリティ ポリシー


socket.ioは2つのセキュリティ戦略を提供します

allowRequest
  • 関数allowRequestには2つのパラメータがあり、最初のパラメータは受信したハンドシェイクパケット(http.request)オブジェクトを基礎としています。判定用、success)、err はエラー オブジェクト、success はブール値、false は接続の確立を妨げることを意味します🎜🎜フロントエンド リクエストはトークンをもたらします🎜
    socket.on(&#39;say to someone&#39;, (id, msg) => { 
     socket.broadcast.to(id).emit(&#39;my message&#39;, msg)
    })
    🎜バックエンドのallowRequestはトークンに基づいて続行するかどうかを決定します🎜
    io.on(&#39;connection&#39;, socket => { 
     socket.emit(&#39;an event&#39;, { some: &#39;data&#39; }) //普通消息
    
     socket.emit(&#39;ferret&#39;, &#39;tobi&#39;, function (data) { //应答消息
     console.log(data); // data will be &#39;woot&#39;
     })
    })
    🎜🎜origins🎜🎜🎜🎜はい ソースを制限します🎜🎜1. インスタンス化するときにソースを制限します🎜🎜🎜origins 関数には 2 つの形式があります🎜🎜🎜origins( string): 実行のソースを設定します 🎜🎜🎜origins(string, fn(err, success)): ソースが関数 🎜
    socket.on(&#39;ferret&#39;, (name, fn) => { 
     fn(&#39;woot&#39;)
    })
    🎜🎜🎜Namespace を通じて許可されているかどうかを決定します🎜🎜🎜🎜🎜名前空間はサーバー/クライアント接続の分離に使用され、場所によっては名前空間 (ネームスペース) チャネル (チャネル) とも呼ばれます。次の例でその重要性を説明します🎜🎜このアプリケーションには 2 つの機能があります:🎜🎜🎜🎜共同編集: 複数のユーザーが同時にドキュメントを編集できます🎜🎜🎜🎜メッセージ: ユーザー間でメッセージを送信できます。相互に🎜 🎜🎜🎜🎜 このアプリケーションを実装するには、socket.io を使用します。このアプリケーションには次の形式があります 🎜🎜1. 完全に独立: 共同編集用の独立したサービス edit.socket.test があります。メッセージング システムの独立したサービス message.socket.test🎜
    // 客户端发送消息
    socket.emit(&#39;hello&#39;, &#39;can you hear me?&#39;, 1, 2, &#39;abc&#39;);
    
    // 向所有连接的客户端(除了自己)发送消息
    socket.broadcast.emit(&#39;broadcast&#39;, &#39;hello friends!&#39;);
    
    // 向game聊天室发送消息,自己不算
    socket.to(&#39;game&#39;).emit(&#39;nice game&#39;, "let&#39;s play a game");
    
    // 同时向game1和game2聊天室发送消息,自己不算
    socket.to(&#39;game1&#39;).to(&#39;game2&#39;).emit(&#39;nice game&#39;, "let&#39;s play a game (too)");
    
    // 向game聊天室的所有人发送消息
    io.in(&#39;game&#39;).emit(&#39;big-announcement&#39;, &#39;the game will start soon&#39;);
    
    // 发送消息到<socketid>客户端
    socket.to(<socketid>).emit(&#39;hey&#39;, &#39;I just met you&#39;);
    // 发送应答消息
    socket.emit(&#39;question&#39;, &#39;do you think so?&#39;, function (answer) {});
    🎜2. 名前空間: 独立したサービスを 1 つだけ実行し、名前空間を通じて分離します🎜rrreeerrreee🎜3. イベント名の規則: イベント名を追加して分離します🎜 rrreee🎜イベントを通じて 名義変更手続きは煩雑であり、分割・再編に資しないため、お勧めできません。 完全に独立したモードでは 2 つのソケット接続を使用する必要があるため、ブラウザーで許可されている同時接続の数が無駄になり、より多くのサーバー リソースが消費されます。名前空間を使用すると、リソースを無駄にすることなく適切な分離を実現できます。 🎜🎜🎜🎜デフォルトの名前空間🎜🎜🎜🎜🎜socket.ioは、インスタンス化するときに名前空間をパス/で自動的にバインドします🎜rrreee🎜🎜🎜ミドルウェア🎜🎜🎜🎜🎜socket.ioの名前空間は、 use を通じてミドルウェアを登録しますware はその後 1 回呼び出されますクライアントとサーバー間の接続が正常に確立され、connet イベントが送出される前。 🎜🎜データ検証にミドルウェアを使用する🎜rrreee🎜ミドルウェアを使用してデータを抽出または変換する io.use((socket, next) => { 🎜getInfo(socket.request.query.id, (err, data) = > { if (err) return next(err)ソケット.custom = data next() }) })🎜🎜🎜allowRequestとの比較🎜🎜🎜🎜allowRequestはいくつかの検証と抽出を実行できますが、なぜそれでも行うのですかミドルウェアが必要ですか?🎜🎜🎜🎜リクエストによって渡される http.request インスタンスを許可し、ミドルウェアはデータ ソケット インスタンスに出入りします。ソケット インスタンスにはリクエスト インスタンスが含まれており、詳細情報が含まれています。🎜🎜🎜🎜ミドルウェアは複数のデータを直接サポートします。非同期プロセスの埋め込みを設定し、allowRequest を自分で実装する必要があります🎜🎜🎜🎜🎜🎜接続イベントと比較して、数値検証や抽出にも使用できます。ミドルウェアが必要ですか?🎜🎜🎜🎜ミドルウェアは直接複数の非同期プロセスのネストをサポートしており、allowRequest は自分で実装する必要があります🎜
  • 中间件成功后到connection事件发送成功前,socket.io还做了一些工作,比如把socket实例添加到connected对象中,加入聊天室等。如果因为权限中断连接,在中间件中处理更省资源.

聊天室

聊天室是对当前连接的socket集合根据特定规则进行归组,方便群发消息。可以类比QQ群的概率.

socket.join(&#39;room name&#39;) //进入 
socket.leave(&#39;room name&#39;) //退出
io.to(&#39;some room&#39;).emit(&#39;some event&#39;) // io.to与io.in同义,向某个聊天室的所有成员发送消息

默认聊天室

每个socket在连接成功后会自动创建一个默认个聊天室,这个聊天室的名字是当前socket的id,可以通过默认聊天室实现向特定用户发送消息

socket.on(&#39;say to someone&#39;, (id, msg) => { 
 socket.broadcast.to(id).emit(&#39;my message&#39;, msg)
})

消息发送

应答消息

普通消息不需要回应,而应答消息提供了应答机制

io.on(&#39;connection&#39;, socket => { 
 socket.emit(&#39;an event&#39;, { some: &#39;data&#39; }) //普通消息

 socket.emit(&#39;ferret&#39;, &#39;tobi&#39;, function (data) { //应答消息
 console.log(data); // data will be &#39;woot&#39;
 })
})


socket.on(&#39;ferret&#39;, (name, fn) => { 
 fn(&#39;woot&#39;)
})

压缩

socket.compress(true)启用压缩,调用后当前连接的所有数据在传递给客户端前都会进行压缩

volatile标志

socket.io在正常情况下对发送的消息进行追踪,确保消息发送成功,而设置volatile后发送消息,socket.io不会对消息追踪,消息可能丢失

分类

// 客户端发送消息
socket.emit(&#39;hello&#39;, &#39;can you hear me?&#39;, 1, 2, &#39;abc&#39;);

// 向所有连接的客户端(除了自己)发送消息
socket.broadcast.emit(&#39;broadcast&#39;, &#39;hello friends!&#39;);

// 向game聊天室发送消息,自己不算
socket.to(&#39;game&#39;).emit(&#39;nice game&#39;, "let&#39;s play a game");

// 同时向game1和game2聊天室发送消息,自己不算
socket.to(&#39;game1&#39;).to(&#39;game2&#39;).emit(&#39;nice game&#39;, "let&#39;s play a game (too)");

// 向game聊天室的所有人发送消息
io.in(&#39;game&#39;).emit(&#39;big-announcement&#39;, &#39;the game will start soon&#39;);

// 发送消息到<socketid>客户端
socket.to(<socketid>).emit(&#39;hey&#39;, &#39;I just met you&#39;);
// 发送应答消息
socket.emit(&#39;question&#39;, &#39;do you think so?&#39;, function (answer) {});

以上がNode.jsでのsocket.io学習チュートリアルの紹介(3)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。