2017-08-10 16 views
0

APIを使ってサーバーを構築しました。これは、記録されていない呼び出しにはAxiosを使用し、記録された呼び出しにはSocket.ioを使用します。 それから私はそれに接続されたウェブサイトを持っています。それは完璧に機能します。 しかし私は、反応ネイティブでビルドされたアプリケーションには奇妙な動作があります。以前の接続を閉じることなく、すべての出力に対して接続を開きます。以下に示すように、私はconsole.logにサーバー上のwebsocket.engine.clientsCountを記録します。私が電話アプリケーションから発信するたびに、新しい接続が開き、その数が増えているサーバーが見つかります。Socket.ioがReact-Nativeで複数の接続を開く

enter image description here

サーバーで、Iユーザー以下のバージョン:ここで

"connect-mongo": "^1.3.2", 
"express": "^4.14.1", 
"express-session": "^1.12.1", 
"jwt-simple": "^0.5.1", 
"mongodb": "^2.2.30", 
"mongoose": "^4.11.5", 
"passport": "^0.3.2", 
"passport-jwt": "^2.2.1", 
"passport-local": "^1.0.0", 
"socket.io": "^1.7.3", 
"socketio-jwt": "^4.5.0" 

APIのコード。わかりやすくするためにいくつかのコードを削除しました。

const passport = require('passport'); 
const express = require('express'); 
const session = require('express-session'); 
const http = require('http'); 
const morgan = require('morgan'); 
const mongoose = require('mongoose'); 
const socketio = require('socket.io'); 
const bodyParser = require('body-parser'); 
const socketioJwt = require("socketio-jwt"); // da commentare 
const Users = require('../models/users'); 

const passportService = require('./services/passport'); 

const requireAuth = passport.authenticate('jwt', {session: false}); 
const requireLogin = passport.authenticate('local', {session: false}); 
const config = require('./config'); 

const app = express(); 
const socketRouter = require('./services/socketRouter'); 
const MongoStore = require('connect-mongo')(session); 

const mongoUri = process.env.MONGODB_URI || 'mongodb://localhost/blablabla'; 
mongoose.connect(mongoUri); 
... 
const server = http.Server(app); 
const websocket = socketio(server); 

// add authorization for jwt-passport when first connection -> https://github.com/auth0/socketio-jwt 
websocket.use(socketioJwt.authorize({ 
    secret: config.secret, 
    handshake: true 
})); 

const sessionMiddleware = session({ 
    store: new MongoStore({ // use MongoDb to store session (re-using previous connection) 
    mongooseConnection: mongoose.connection, 
    ttl: (1 * 60 * 60) 
    }), 
    secret: config.secretSession, 
    httpOnly: true, 
    resave: false, 
    saveUninitialized: false, 
    cookie: { maxAge: 86400000 } 
}); 
app.use(sessionMiddleware); 
... 
websocket.on('connection', (socket) => { 
    Users.findById(socket.decoded_token.sub, function(err, user) { 
     if (err) { console.log('the user wasn\'t find in database', err); } 
     if (user) { 
      socket.join(user._id); 
      console.log('Clients connected: ', websocket.engine.clientsCount); 

      // ------ PROTECTED EVENTS ------ // 
      ... 
      // ------------------------------ // 

     } 

     socket.on('disconnect',()=> { 
      socket.leave(user._id); 
      onsole.log('user disconnected'); 
     }); 
    }); 
}); 
... 

私はウェブサイトが正常に機能するため、ウェブサイトの初期化を行いません。モバイルアプリケーションで

、Iユーザー以下のバージョン:

"react-native": "^0.41.0", 
"react-native-keychain": "^1.1.0", 
"socket.io-client": "^1.7.3", 
"socketio-jwt": "^4.5.0" 

は、ここで反応し、ネイティブアプリケーションのinnitialisationです。

import * as Keychain from 'react-native-keychain'; 
import { BASIC_WS_URL } from '../api'; 

const io = require('socket.io-client/dist/socket.io'); 
const socketEvents = require('./events'); 

exports = module.exports = (store) => { 
    Keychain.getGenericPassword().then((credentials) => { 
    if (credentials && credentials !== false) { 
     const { password } = credentials; 
     const websocket = io(BASIC_WS_URL, { 
     jsonp: false, 
     transports: ['websocket'], // you need to explicitly tell it to use websockets 
     query: { 
      token: password 
     } 
     }); 

     websocket.connect(); 
     websocket.on('connect', (socket) => { 
     console.log('Connected'); 
     }); 

     websocket.on('reconnect', (socket) => { 
     console.log('Re-connected'); 
     }); 

     websocket.on('disconnect', (socket) => { 
     console.log('Disconnected'); 
     }); 
     // all the events to listen 
     socketEvents(websocket, store); 
    } 
    }); 
}; 

何が間違っているのですか?

+0

私が理解している限り、React-Nativeのソケットはグローバルであるという問題があります。私が放射するたびに、それは前のものを閉じることなく新しい接続を開きます。 私はソケットを初期化しようとしており、アクションクリエータでグローバルに送信できるようにしようとしています。私が 'コンテキスト'を使用すると、他のコンポーネントの初期化されたソケットを取得することができますが、それ以上は出ません(私が推測する循環オブジェクトのコンセプトがありません)。さらに、接続を認可するためのキーチェーントークンは非同期であり、ライフサイクルコンポーネントの機能には反映されません。 誰かに実用的な実装をされていますか? – Pibo

答えて

-1

ここに私は答えが来る。私は、私が探したいと思う答えを残そうとします。 React-nativeにSocket.ioを含める方法に関するチュートリアルの一種。あなたがより良い解決策を知っているなら、それを書き留めてください。 私が書いたように、問題はReact-Nativeのソケットがグローバルであり、このやり方では間違った実装がはっきりと分かります。 まず、ソケットを間違った場所に初期化しました。私が見つけた正しい場所は、ルータがあるApp.jsです。わかりやすくするためにいくつかのコードを削除します。

// important to access the context of React 
import PropTypes from 'prop-types'; 
// Library used to save encrypted token 
import * as Keychain from 'react-native-keychain'; 
// url to address 
import { BASIC_WS_URL } from '../../api';// library to encrypt and decrypt data 
const io = require('socket.io-client/dist/socket.io'); 

コンストラクタとcomponentDidMount以内にこの機能を準備します

state = {} 
    setStateAsync(state) { 
    return new Promise((resolve) => { 
     this.setState(state, resolve) 
    }); 
    } 

keichainは約束なので、componentDidMountでは動作しません。それを動作させるためには、次の手順を実行する必要があり、そのすべてのステップが実行される前のを待ちます。

async componentWillMount() { 
    const response = await Keychain.getGenericPassword(); 
    const websocket = await io(BASIC_WS_URL, { 
    jsonp: false, 
    // forceNew:true, 
    transports: ['websocket'], 
    query: { 
     token: response.password 
    } 
    }); 
    await websocket.connect(); 
    await websocket.on('connect', (socket) => { 
    console.log('Sono -> connesso!'); 
    }); 
    await websocket.on('reconnect', (socket) => { 
    console.log('Sono riconnesso!'); 
    }); 
    await websocket.on('disconnect', (socket) => { 
    console.log('Sono disconnesso!'); 
    }); 
    await websocket.on('error', (error) => { 
    console.log(error); 
    }); 
// a function imported containing all the events (passing store retrieved from context declare at the bottom) 
    await socketEvents(websocket, this.context.store); 

    // then save the socket in the state, because at this point the component will be already rendered and this.socket would be not effective 
    await this.setStateAsync({websocket: websocket}); 
} 

はその後console.logsを削除することを忘れないでください。彼らは検証のためだけにあります。右この後アンマウントするとき、思い出してくれる切断する:コンポーネントの下部にあるコンテキストを宣言し

getChildContext() { 
    return {websocket: this.state.websocket}; 
    } 

思い出してくれる:

componentWillUnmount() { 
    this.state.websocket.disconnect() 
    } 

そして右この後

は、コンテキストでソケットを保存

... 
    // important to access the context of React 
    import PropTypes from 'prop-types'; 
    // Library used to save encrypted token 
    import * as Keychain from 'react-native-keychain'; 
    // url to address 
    import { BASIC_WS_URL } from '../../api';// library to encrypt and decrypt data 
    const io = require('socket.io-client/dist/socket.io'); 
... 


class App extends Component { 
    constructor() { 
    super(); 
    ... 
    } 
    state = {} 
    setStateAsync(state) { 
    return new Promise((resolve) => { 
     this.setState(state, resolve) 
    }); 
    } 
    // set the function as asynchronous 
    async componentWillMount() { 
    //retrieve the token to authorize the calls 
    const response = await Keychain.getGenericPassword(); 
    // initialize the socket connection with the passwordToken (wait for it) 
    const websocket = await io(BASIC_WS_URL, { 
     jsonp: false, 
     // forceNew:true, 
     transports: ['websocket'], // you need to explicitly tell it to use websockets 
     query: { 
     token: response.password 
     } 
    }); 
    // connect to socket (ask for waiting for the previous initialization) 
    await websocket.connect(); 
    await websocket.on('connect', (socket) => { 
     console.log('Sono -> connesso!'); 
    }); 
    await websocket.on('reconnect', (socket) => { 
     console.log('Sono riconnesso!'); 
    }); 
    await websocket.on('disconnect', (socket) => { 
     console.log('Sono disconnesso!'); 
    }); 
    await websocket.on('error', (error) => { 
     console.log(error); 
    }); 
// a function imported containing all the events 
    await socketEvents(websocket, this.context.store); 
    await this.setStateAsync({websocket: websocket}); 
    } 
    componentWillUnmount() { 
    this.state.websocket.disconnect() 
    } 
    getChildContext() { 
    return {websocket: this.state.websocket}; 
    } 
    render() { 
    return (
    ... // here goes the router 
    ); 
    } 
} 
App.childContextTypes = { 
    websocket: PropTypes.object 
} 
// access context.type to get the store to pass to socket.io initialization 
App.contextTypes = { 
    store: PropTypes.object 
} 
export default App; 
01:
App.childContextTypes = { 
    websocket: PropTypes.object 
} 
// access context.type to get the store to pass to socket.io initialization 
App.contextTypes = { 
    store: PropTypes.object 
} 

ので、最終結果はこれです

次に、どのページ/コンテナでも、このようにすることができます。

Main.contextTypes = { 
    websocket: PropTypes.object 
} 

そして、あなたがアクションをディスパッチするとき、あなたは発光する、次にできるようになります: - >コンポーネントの下にコンテキストを宣言アクションの作成者で

this.props.dispatch(loadNotif(this.context.websocket)); 

、次のような放出されますこの:

exports.loadNotif = (websocket) => { 
    return function (dispatch) { 
    // send request to server 
    websocket.emit('action', {par: 'blablabla'}); 
    }; 
}; 

私はそれが誰かを助けることを願っています。

0

あなたのsocket.ioインスタンスを反応するネイティブコンポーネントの中に置かないでください。代わりに、それらをインデックスファイルの中に入れてください。したがって、反応ライフサイクルイベント中には起動されません

+0

これは私の最初の実装でした。しかし、私はアクションクリエイターから放出するためのアプリケーションを通して初期化されたソケットを共有する方法を見つけませんでした。私が見つけた唯一の方法はコンテキストであり、そのためにはコンポーネントのソケットを初期化する必要があります(私が知っている限り)。私たちがWeb上で見つけた多くの実装は、私が解決しなければならなかったバグを引き起こします。ちょうど彼らは問題をサーバーに知らせることはできませんでした - > console.log( 'Clients connected:'、websocket.engine.clientsCount) – Pibo

+0

ですが、インデックスファイルからソケットオブジェクトをエクスポートできます。 – Developer

1

socket.on()の代わりにsocket.once()を試してください。毎回接続を作成しますが、イベントは伝播しません。

関連する問題