这是我想要达到的 Szenario:
我想使用 vuzix m100(Android API 15)和 Google Chrome 浏览器/或者 Firefox 开发 2 路远程视频会议。为了达到这个连接,我使用 WebRTC 和一个带有 Websockets 的 Node.JS 服务器来启用两个客户端的查找。
我目前面临的问题是,如果我将 2 个 Chrome 浏览器客户端详细连接到一个 Android 设备(Sony Xperia z 3 compact)和一个桌面电脑,那么一切都可以完美运行。如果我尝试通过浏览器连接我的 Vuzix,则会收到没有主机路由的错误消息。
node.js 服务器:
var fs = require('fs');
var https = require('https');
var privateKey = fs.readFileSync('server.key', 'utf8');
var certificate = fs.readFileSync('server.crt', 'utf8');
var credentials = {key: privateKey, cert: certificate};
var express = require('express');
var app = express();
//require our websocket library
var WebSocketServer = require('ws').Server;
var httpsServer = https.createServer(credentials, app);
httpsServer.listen(9090);
//creating a websocket server at port 9090
// var wss = new WebSocketServer({port: 9090}, {});
var wss = new WebSocketServer({
server : httpsServer
})
//all connected to the server users
var users = {};
//when a user connects to our sever
wss.on('connection', function(connection) {
console.log("User connected");
//when server gets a message from a connected user
connection.on('message', function(message) {
var data;
//accepting only JSON messages
try {
data = JSON.parse(message);
} catch (e) {
console.log("Invalid JSON");
data = {};
}
//switching type of the user message
switch (data.type) {
//when a user tries to login
case "login":
console.log("User logged", data.name);
//if anyone is logged in with this username then refuse
if(users[data.name]) {
sendTo(connection, {
type: "login",
success: false
});
} else {
//save user connection on the server
users[data.name] = connection;
connection.name = data.name;
sendTo(connection, {
type: "login",
success: true
});
}
break;
case "offer":
//for ex. UserA wants to call UserB
console.log("Sending offer to: ", data.name);
//if UserB exists then send him offer details
var conn = users[data.name];
if(conn != null) {
//setting that UserA connected with UserB
connection.otherName = data.name;
sendTo(conn, {
type: "offer",
offer: data.offer,
name: connection.name
});
}
break;
case "answer":
console.log("Sending answer to: ", data.name);
//for ex. UserB answers UserA
var conn = users[data.name];
if(conn != null) {
connection.otherName = data.name;
sendTo(conn, {
type: "answer",
answer: data.answer
});
}
break;
case "candidate":
console.log("Sending candidate to:",data.name);
var conn = users[data.name];
if(conn != null) {
sendTo(conn, {
type: "candidate",
candidate: data.candidate
});
}
break;
case "leave":
console.log("Disconnecting from", data.name);
var conn = users[data.name];
conn.otherName = null;
//notify the other user so he can disconnect his peer connection
if(conn != null) {
sendTo(conn, {
type: "leave"
});
}
break;
default:
sendTo(connection, {
type: "error",
message: "Command not found: " + data.type
});
break;
}
});
//when user exits, for example closes a browser window
//this may help if we are still in "offer","answer" or "candidate" state
connection.on("close", function() {
if(connection.name) {
delete users[connection.name];
if(connection.otherName) {
console.log("Disconnecting from ", connection.otherName);
var conn = users[connection.otherName];
conn.otherName = null;
if(conn != null) {
sendTo(conn, {
type: "leave"
});
}
}
}
});
connection.send("Hello world");
});
function sendTo(connection, message) {
connection.send(JSON.stringify(message));
}
安卓应用:
public class ServerConnection {
private WebSocketFactory webSocketfactory;
private WebSocket ws;
private MediaStream mS;
private PeerConnectionFactory peerFactory;
private PeerConnection yourConn;
private MediaConstraints sdpConstraints;
private SessionObserver sessionObserver = new SessionObserver();
private PeerObserver peerObserver = new PeerObserver();
private String connectedUser;
public ServerConnection(MediaStream mS, PeerConnectionFactory peerFactory) {
this.webSocketfactory = new WebSocketFactory();
this.mS = mS;
this.peerFactory = peerFactory;
// Create a custom SSL context.
SSLContext context = null;
try {
context = NaiveSSLContext.getInstance("TLS");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
// Set the custom SSL context.
webSocketfactory.setSSLContext(context);
initWebsockets();
}
public void sendMessage(String message) {
if (connectedUser != null) {
try {
JSONObject json = new JSONObject(message);
json.put("name", connectedUser);
ws.sendText(json.toString());
} catch (JSONException e) {
e.printStackTrace();
}
} else {
Log.e("String s", message);
ws.sendText(message);
}
}
private void initWebsockets() {
try {
ws = webSocketfactory.createSocket("wss://192.168.179.36:9090");
ws.addListener(new SocketListener());
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
ws.connect();
} catch (WebSocketException e) {
e.printStackTrace();
initWebsockets();
}
}
});
t.start();
} catch (IOException e) {
e.printStackTrace();
}
}
class SocketListener extends WebSocketAdapter {
@Override
public void onConnected(WebSocket websocket, Map<String, List<String>> headers) throws Exception {
Log.e("connected", "connected");
JSONObject json = new JSONObject();
try {
// Für den Anfang statisch
json.put("type", "login");
json.put("name", "John");
} catch (JSONException e) {
e.printStackTrace();
}
sendMessage(json.toString());
}
@Override
public void onConnectError(WebSocket websocket, WebSocketException exception) throws Exception {
Log.e("onError", exception.getMessage());
}
@Override
public void onDisconnected(WebSocket websocket,
WebSocketFrame serverCloseFrame, WebSocketFrame clientCloseFrame,
boolean closedByServer) throws Exception {
}
@Override
public void onTextMessage(WebSocket websocket, String text) throws Exception {
Log.e("Got Message", text);
JSONObject json = new JSONObject(text);
if (json != null) {
switch (json.getString("type")) {
case "login":
Log.e("Condition", json.getString("success"));
handleLogin(json.getBoolean("success"));
break;
case "offer":
handleOffer(json.getString("offer"), json.getString("name"));
break;
case "answer":
handleAnswer(json.getString("answer"));
break;
case "candidate":
String candidate = json.getString("candidate");
handleCandidate(candidate);
break;
case "leave":
break;
default:
break;
}
}
}
@Override
public void onFrameSent(WebSocket websocket, WebSocketFrame frame) throws Exception {
Log.e("sent", frame.getPayloadText());
}
}
private void handleLogin(boolean success) {
if (!success) {
Log.e("handleLogin", "Try a different Username");
} else {
List<PeerConnection.IceServer> server = new ArrayList<>();
server.add(new PeerConnection.IceServer("stun:stun2.1.google.com:19302"));
server.add(new PeerConnection.IceServer("turn:192.158.29.39:3478?transport=udp", "28224511:1379330808", "JZEOEt2V3Qb0y27GRntt2u2PAYA="));
sdpConstraints = new MediaConstraints();
sdpConstraints.optional.add(new MediaConstraints.KeyValuePair("offerToReceiveAudio", "true"));
sdpConstraints.optional.add(new MediaConstraints.KeyValuePair("offerToReceiveVideo", "true"));
yourConn = peerFactory.createPeerConnection(server, sdpConstraints, peerObserver);
Log.e("Media Stream:", mS.toString());
yourConn.addStream(mS);
// wird später implementiert um anrufe zu starten
// yourConn.createOffer(new SessionObserver(), sdpConstraints);
}
}
private void handleOffer(String offer, String name) {
try {
JSONObject sdp = new JSONObject(offer);
connectedUser = name;
SessionDescription sessionDescription = new SessionDescription(SessionDescription.Type.OFFER, sdp.getString("sdp"));
yourConn.setRemoteDescription(sessionObserver, sessionDescription);
yourConn.createAnswer(sessionObserver, sdpConstraints);
} catch (JSONException e) {
e.printStackTrace();
}
}
private void handleAnswer(String answer) {
try {
Log.e("answer", answer);
JSONObject sdp = new JSONObject(answer);
SessionDescription sessionDescription = new SessionDescription(SessionDescription.Type.ANSWER, sdp.getString("sdp"));
yourConn.setRemoteDescription(sessionObserver, sessionDescription);
} catch (JSONException e) {
e.printStackTrace();
}
}
private void handleLeave() {
}
private void handleCandidate(String candidate) {
Log.e("handleCandidate", candidate);
//yourConn.addIceCandidate(new IceCandidate())
// try {
// JSONObject candidateJson = new JSONObject();
// candidateJson.put("type", "candidate");
// JSONObject candidateInfosJSON = new JSONObject(candidate);
// candidateJson.put("candidate", candidateInfosJSON);
//
// sendMessage(candidateJson.toString());
// } catch (JSONException e) {
// e.printStackTrace();
// }
}
class PeerObserver implements PeerConnection.Observer {
@Override
public void onSignalingChange(PeerConnection.SignalingState signalingState) {
Log.e("onSignalingChange", "");
}
@Override
public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) {
Log.e("onIceConnectionChange", "");
}
@Override
public void onIceConnectionReceivingChange(boolean b) {
Log.e("onIceConnectionChange", "");
}
@Override
public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) {
Log.e("onIceGatheringChange", "");
}
@Override
public void onIceCandidate(IceCandidate iceCandidate) {
// Log.e("onIceCandidate", iceCandidate.toString());
// JSONObject candidate = new JSONObject();
// try {
//
// candidate.put("type", "candidate");
// candidate.put("label", iceCandidate.sdpMLineIndex);
// candidate.put("id", iceCandidate.sdpMid);
// candidate.put("candidate", iceCandidate);
Log.e("OnIceCandidate", "here");
// } catch (JSONException e) {
// e.printStackTrace();
// }
}
@Override
public void onAddStream(MediaStream mediaStream) {
Log.e("onAddStream", "Stream added: " + mediaStream);
}
@Override
public void onRemoveStream(MediaStream mediaStream) {
Log.e("onRemoveStream", "Removed mediaStream: " + mediaStream);
}
@Override
public void onDataChannel(DataChannel dataChannel) {
Log.e("onDataChannel", "");
}
@Override
public void onRenegotiationNeeded() {
Log.e("onRenegotiationNeeded", "");
}
}
class SessionObserver implements SdpObserver {
@Override
public void onCreateSuccess(SessionDescription sessionDescription) {
Log.e("Session", "sending");
JSONObject session = new JSONObject();
JSONObject answer = new JSONObject();
try {
String answerORoffer = sessionDescription.type.toString().toLowerCase();
session.put("type", answerORoffer);
answer.put("type", answerORoffer);
answer.put("sdp", sessionDescription.description);
session.put(answerORoffer, answer);
Log.e("SESSION", session.toString());
//session.put(answerORoffer, sessionDescription.description);
sendMessage(session.toString());
} catch (JSONException e) {
e.printStackTrace();
}
}
@Override
public void onSetSuccess() {
Log.e("do call success", ".....");
}
@Override
public void onCreateFailure(String s) {
Log.e("do call failure", s);
}
@Override
public void onSetFailure(String s) {
Log.e("onSetFailure", s);
}
}
}
需要提到的重要一点是错误出现在 initWebSockets 函数中
我得到的错误:
09-22 15:41:47.056 7682-7779/com.mobile_function.webrtc_android W/System.err: com.neovisionaries.ws.client.WebSocketException: Failed to connect to '192.168.179.36:9090': failed to connect to /192.168.179.36 (port 9090): connect failed: EHOSTUNREACH (No route to host)
09-22 15:41:47.056 7682-7779/com.mobile_function.webrtc_android W/System.err: at com.neovisionaries.ws.client.SocketConnector.doConnect(SocketConnector.java:119)
09-22 15:41:47.056 7682-7779/com.mobile_function.webrtc_android W/System.err: at com.neovisionaries.ws.client.SocketConnector.connect(SocketConnector.java:81)
09-22 15:41:47.064 7682-7779/com.mobile_function.webrtc_android W/System.err: at com.neovisionaries.ws.client.WebSocket.connect(WebSocket.java:2022)
09-22 15:41:47.064 7682-7779/com.mobile_function.webrtc_android W/System.err: at com.mobile_function.webrtc_android.ServerConnection$1.run(ServerConnection.java:84)
09-22 15:41:47.064 7682-7779/com.mobile_function.webrtc_android W/System.err: at java.lang.Thread.run(Thread.java:856)
09-22 15:41:47.064 7682-7779/com.mobile_function.webrtc_android W/System.err: Caused by: java.net.ConnectException: failed to connect to /192.168.179.36 (port 9090): connect failed: EHOSTUNREACH (No route to host)
09-22 15:41:47.071 7682-7779/com.mobile_function.webrtc_android W/System.err: at libcore.io.IoBridge.connect(IoBridge.java:114)
09-22 15:41:47.071 7682-7779/com.mobile_function.webrtc_android W/System.err: at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:192)
09-22 15:41:47.071 7682-7779/com.mobile_function.webrtc_android W/System.err: at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:459)
09-22 15:41:47.071 7682-7779/com.mobile_function.webrtc_android W/System.err: at java.net.Socket.connect(Socket.java:842)
09-22 15:41:47.071 7682-7779/com.mobile_function.webrtc_android W/System.err: at com.neovisionaries.ws.client.SocketConnector.doConnect(SocketConnector.java:110)
09-22 15:41:47.071 7682-7779/com.mobile_function.webrtc_android W/System.err: ... 4 more
09-22 15:41:47.071 7682-7779/com.mobile_function.webrtc_android W/System.err: Caused by: libcore.io.ErrnoException: connect failed: EHOSTUNREACH (No route to host)
09-22 15:41:47.079 7682-7779/com.mobile_function.webrtc_android W/System.err: at libcore.io.Posix.connect(Native Method)
09-22 15:41:47.079 7682-7779/com.mobile_function.webrtc_android W/System.err: at libcore.io.BlockGuardOs.connect(BlockGuardOs.java:85)
09-22 15:41:47.079 7682-7779/com.mobile_function.webrtc_android W/System.err: at libcore.io.IoBridge.connectErrno(IoBridge.java:127)
09-22 15:41:47.079 7682-7779/com.mobile_function.webrtc_android W/System.err: at libcore.io.IoBridge.connect(IoBridge.java:112)
09-22 15:41:47.079 7682-7779/com.mobile_function.webrtc_android W/System.err: ... 8 more
我已经搜索了有关这些错误代码的某些主题。我的 WiFi 连接已启用,我可以使用我的 Vuzix 玻璃通过 mozilla firefox 访问不同的网页,但我无法访问我的服务器。
这两个客户端肯定在同一个子网中: Android:192.168.179.94 WebApp 的 Html 服务器:192.168.179.36:8080 节点套接字服务器:192.168.179.36:9090 网关:192.168.179.1
我希望任何人都可以给我一些建议,我该如何解决这个问题
亲切的问候
JMD