質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.90%

グローバルIPによる接続とローカルIPでの接続によるwebサーバでの挙動が異なる[ROS,javascript]

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 297

masabs

score 13

前提・実現したいこと

ROSのroswwwを使ったwebサーバで、サーバ上のHTMLファイル内にjavascriptでROSへの接続を記述しています。
ROSのサーバを持つ端末のLAN外にあるクライアントからこのHTMLにアクセスして、ROSの指令を出せるようにしたいです。

発生している問題・エラーメッセージ

まず、サーバ端末とクライアント端末を同一LAN内に配置してHTMLにアクセスしたところ、所望の動作が得られました。
----  サーバ端末上でROSのサーバ(roswww)とサーバとROSのブリッジ(rosbridge_server)を起動
----  クライアント端末からChromeでhttp://192.168.xxx.xxx:8085/WorkSpace/example.htmlに接続
----  ChromeにHTMLファイルの内容が表示され、javascriptによるROSへの接続が成功。
----  (接続できた際のメッセージ"Connect"がChrome上に表示される)

次に、クライアント端末をサーバ端末のLANから外し、サーバ端末のグローバルIPを指定してHTMLにアクセスしたところ、
HTMLへのアクセスはできましたが、HTMLファイル内のjavascriptによるROSへの接続ができませんでした。
----  サーバ端末のLANのインターネットルータのポートマッピングを設定
----   [WANアドレス:xxx.xxx.xxx.xxx,ポート 80 → LANアドレス:192.168.xxx.xxx,ポート8085] 
----  上記と同様に、サーバ端末上でroswwwとrosbridge_serverを起動
----  クライアント端末からChromeでhttp://xxx.xxx.xxx.xxx/WorkSpace/example.htmlに接続
----  ChromeにHTMLファイルの内容が表示されるが、javascriptによるROSへの接続が失敗。
----  (接続できなかった際のメッセージ"Close"がChrome上に表示される。)

LAN経由とグローバルネットワーク経由では、同じHTMLにアクセスしても何か動作に異なることがあるのでしょうか?
どの部分を調べていけばよいのか分からず困っています。

該当のソースコード

<html lang="ja">
<head>
<meta charset="UTF-8">
<title>ros sample</title>
<script src="http://static.robotwebtools.org/EventEmitter2/current/eventemitter2.min.js"></script>
<script src="http://static.robotwebtools.org/roslibjs/current/roslib.min.js"></script>
</head>
<body>
    <h3>rosjs example</h3>
    <p>status: <label id="state">Disconnect</label></p>

<script>
// rosbridgeと接続
const ros = new ROSLIB.Ros({
    url : ('ws://' + location.hostname + ':9090')
});
// 接続時のコールバック
ros.on('connection', function() {
    console.log('Connected to websocket server.');
    document.getElementById('state').innerHTML = "Connect";
});
// エラー時のコールバック
ros.on('error', function(error) {
    console.log('Error connecting to websocket server: ', error);
    document.getElementById('state').innerHTML = "Disconnect";
});
// 切断時のコールバック
ros.on('close', function() {
    console.log('Connection to websocket server closed.');
    document.getElementById('state').innerHTML = "Close";
});

</script>
</body>

試したこと

■9090番のポートマッピング →マッピング後も接続できない状況のまま変わらず。
ルータのポートマッピング設定

■クライアント側ブラウザのコンソールのエラーを確認。
WebSocketのエラー
roslib.min.js:1 
WebSocket connection to 'ws://XXX.XXX.XXX.XXX:9090/' failed: Error in connection establishment: net::ERR_CONNECTION_TIMED_OUT
d.connect @ roslib.min.js:1
d @ roslib.min.js:1
(anonymous) @ connect.html:14

■('ws://' + location.hostname + ':9090') → 'ws://192.168.100.110:9090/'(サーバのアドレスを指定)
同様のエラー
WebSocket connection to 'ws://192.168.100.110:9090/' failed: Error in connection establishment: net::ERR_CONNECTION_TIMED_OUT
d.connect @ roslib.min.js:1
d @ roslib.min.js:1
(anonymous) @ connect.html:14

補足情報(FW/ツールのバージョンなど)

サーバ側端末(ROSの搭載端末): Jetson nano (Ubuntu 18.04)

クライアント側端末:Windows10

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

+1

url : ('ws://' + location.hostname + ':9090')

で、9090番ポートにwebsocket接続に行っているようなので、9090番ポートも
WAN側に見せれば動くと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/04 04:18

    ご回答ありがとうございます。そのときのWAN側のポートはどのように指定すればよいのでしょうか?websocketのデフォルト80番だと他のマッピングと競合してしまうのですが、、、
    知識が浅く申し訳ありません。自身で調べてはみましたが、やり方が分かりませんでした。再度ご教授いただけると大変ありがたいです。

    キャンセル

  • 2020/07/04 23:35

    WAN側のアドレスの 9090番をそのままLAN側のIPアドレスの9090番ポートにマップすればよいかと思います。
    都合、 (ルーターに)
    WAN側のアドレス:80 → LAN側のアドレス:8085
    WAN側のアドレス:9090 → LAN側のアドレス:9090
    の2つのマッピングを作ることになります。

    キャンセル

  • 2020/07/05 00:34

    ありがとうございます。教えていただいた内容を試しましたが、まだ問題があるようで、繋がりませんでした。クライアント側ブラウザのコンソールを確認しましたが、やはり指摘いただいてる部分のWebsocket接続でエラーが出ています。
    もし他に確かめるべきことやデバッグの方法など思いつくことがございましたら、お手数ですが再度ご教授いただけると大変ありがたいです。
    試したことを追記しました。

    キャンセル

  • 2020/07/07 11:06

    解決できました。ありがとうございました。

    キャンセル

check解決した方法

0

サーバ側にリバースプロキシを導入することで解決しました。

WebサーバのHTMLにクライアント側からアクセスしてwebsocketを発行させる際、HTMLにアクセスするのと同じWAN側80番による接続となるため、ルータのポートマッピング機能でLAN側9090番にwebsocketを通せないことが問題でした。(httpアクセス用のWAN80番→LAN8085番のマッピングと重複してしまう)

websocket発行の際に80番と別のWAN側ポートを指定できればそのポートをマッピングすれば良いと思うのですが、このあたり十分理解できていないのですがクライアント側ブラウザのChromeからではとりあえず無理そうでした。

そこで、ルータのマッピングによる方法ではなく、サーバ側にNginxによるリバースプロキシを設定し、
WAN側80番のhttpをLAN側8085番、WAN側80番のwebsocketをLAN側9090番に転送するようにし、無事接続できるようになりました。

つまり、自分の元の疑問である「グローバルIPによる接続とローカルIPでの接続によるwebサーバでの挙動が異なる」については、グローバルIPで接続する場合、httpもwebsocketもWAN側80番のポートを通る(80番以外だと通らない。これは自分の環境のみの制約?webブラウザの制約?)。ローカルで自由にポートアクセスできるときと比べて、ルータ通過後にリバースプロキシで別のポートに振り分けるなどの必要が出てくる、ということでした。

以下のページを参考にさせていただきました。
nginx(1.4.3)のproxyでWebとWebSocketのポート共有。SSL対応

    upstream any_web {
        ip_hash;
        server ローカルIP:8085 weight=5;
    }

    upstream any_websocket {
        ip_hash;
        server ローカルIP:9090;
    }

    server {
        listen       80;

        location /ws/ {
            proxy_pass http://any_websocket;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_set_header Host $host;
        }
        location / {
            proxy_pass http://any_web;
            proxy_redirect default;
        }
    }

また、今回のwebsocketの接続先はROSのノードになっていて、実はそちら側の制約で上記のコードでもまだ不十分でした。上記をSSLを使ったhttpsとwssでの接続に書き換えて、やっと最終的な接続ができるようになりました。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.90%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る