質問: ### 前提・実現したいこと 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にアクセスしても何か動作に異なることがあるのでしょうか? どの部分を調べていけばよいのか分からず困っています。 ### 該当のソースコード ``` ros sample rosjs example status: Disconnect ``` ### 試したこと ■9090番のポートマッピング →マッピング後も接続できない状況のまま変わらず。 ![ルータのポートマッピング設定](https://teratail-v2.storage.googleapis.com/uploads/contributed_images/4fc147dbfef4fb1057367cf5a0b724c3.jpeg) ■クライアント側ブラウザのコンソールのエラーを確認。 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
回答: ### サーバ側にリバースプロキシを導入することで解決しました。 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での接続に書き換えて、やっと最終的な接続ができるようになりました。