NodeJS: как я могу обновить представление с несколькими ответами от подписанного веб-сокета?

Я новичок в веб-сокетах.

Я пытаюсь реализовать веб-сокет в приложении MeanJS, чтобы подписаться на некоторые данные в реальном времени, которые предоставляет другой веб-сайт, когда данные возвращаются, я хотел бы обновить таблицу в своем представлении с результатами.

Я столкнулся с логической проблемой. Моя реализация не удалась, потому что она пытается отправить несколько ответов через одно TCP-соединение (я полагаю), она умирает с ошибкой:

throw new Error('Cant set headers after they are sent.');

Я понимаю, почему возникает эта ошибка, но, поскольку я новичок в WebSockets и довольно плохо знаком с NodeJS, я не уверен, как реорганизовать этот код таким образом, чтобы это имело смысл.

В моем клиентском контроллере:

    // Get Orderbook
    $scope.getOrderbook = function() {
        $http.get('exchanges/' + $stateParams.exchangeId + '/get_orderbook')
            .success(function(data) {
                console.log("ORDERBOOK DATA: " + data); // DEBUGGING
                $scope.orderBook = data;
            });
    };

В моем контроллере на стороне сервера:

    exports.getOrderbook = function(req, res, next) {
        var exchange = req.exchange;

        if(exchange.slug === 'somesite') {
            var ws = new WebSocket('wss://www.somesite.com/realtimews');

            ws.on('open', function open() {
                ws.send('{"op": "subscribe", "args": "orderBook"}');
            });

            ws.on('message', function(message) {
                var response = JSON.parse(message);

                if(response.table) {
                    res.jsonp(response.data);
                }
            });
        }
    };

Как лучше всего реорганизовать этот код, чтобы я мог подписаться на веб-сокет, получать от него сообщения и при получении любого сообщения обновлять представление с данными?


person Dave Rich    schedule 07.10.2015    source источник


Ответы (1)


При каждом новом входящем HTTP-запросе вы пытаетесь открыть новый веб-сокет на каком-либо сайте, а затем каждый раз, когда вы получаете сообщение, вы пытаетесь отправить ответ. Между тем, вы никогда не закрываете свой веб-сокет.

Конкретная ошибка, которую вы получаете, возникает, когда вы пытаетесь отправить второй HTTP-ответ на тот же http-запрос, когда первый ответ уже отправлен. Это произойдет в вашем коде, когда вы получите второе сообщение в веб-сокете.

Кроме того, вы открываете новый веб-сокет каждый раз, когда получаете этот запрос, который просто накапливает эти открытые веб-сокеты.

Если вам действительно нужно использовать веб-сокет для получения ваших данных, вам нужно закрыть веб-сокет, как только вы получите свое первое сообщение, или удалить эти обработчики событий, связанные с конкретным запросом.

Вот один из способов сделать это:

exports.getOrderbook = function(req, res, next) {
    var exchange = req.exchange;

    if(exchange.slug === 'somesite') {
        var ws = new WebSocket('wss://www.somesite.com/realtimews');

        ws.on('open', function open() {
            ws.send('{"op": "subscribe", "args": "orderBook"}');
        });

        ws.on('message', function(message) {
            var response = JSON.parse(message);

            if(response.table) {
                res.jsonp(response.data);
            } else {
                res.jsonp(/* some error response here */)
            }
            // close the websocket so we don't associate any more responses
            // with this particular http request
            ws.close();                
        });
    } else {
        // need to send some response here
    }
};
person jfriend00    schedule 07.10.2015
comment
Спасибо! Я знал, почему возникает ошибка, но я пропустил ws.close(). Если бы я реализовал это таким образом, с закрытием веб-сокета после возврата ответа, мне пришлось бы постоянно отправлять HTTP-запросы для вызова моей серверной функции для получения обновлений после первоначального ответа. Этот вид побеждает цель подписки на веб-сокет для возврата данных вместо выполнения длительного опроса, которого я пытался избежать. - person Dave Rich; 07.10.2015
comment
@DaveRich - вы можете создать веб-сокет и сохранить ссылку на сокет глобально, если хотите. Но для того, чтобы вернуть эту информацию клиенту, вам потребуется постоянное соединение с клиентом (например, веб-сокет между клиентом и сервером). Вы на самом деле не объяснили общий контекст и архитектуру проблемы, которую вы пытаетесь решить, поэтому я не уверен, что порекомендовать. Возможно, вам действительно следует сделать так, чтобы каждый клиент подключался через веб-сокет к серверу orderBook напрямую, чтобы он мог просто получать оттуда информацию в реальном времени? - person jfriend00; 07.10.2015
comment
@DaveRich - или вы можете иметь одно подключение через веб-сокет к источнику в реальном времени с вашего сервера, и каждый клиент подключается к вашему серверу через веб-сокет. Затем, в любое время, когда вы получаете информацию в реальном времени на своем сервере, вы можете немедленно пересылать соответствующие данные любым подключенным клиентам. - person jfriend00; 07.10.2015
comment
Спасибо, я попробую реализовать это с клиентского контроллера и посмотрю, как это работает. - person Dave Rich; 07.10.2015