In order to connect peer to peer for communication using the WebRTC API
, you need to implement signaling. Signaling needs a two way communication channel to work. WebSockets
are a two way communication channel. I would like to use WebSockets
to implement signaling, however, I have no idea how to approach this. Everything I've found online just discusses the different two way channels you can use rather than providing an example on how to use them.
I'm using JavaScript
for the client side and Java
for the server side. So, how would I implement signaling using WebSockets
?
Here's a way I figured out how to do this. I haven't tested the code yet, so, I'm not sure if it works. Also, I'm skeptical about whether this will be efficient. If you have a better solution, see a problem in my code, or any advice feel free to elaborate. The idea behind this is to have the WebSocket Server Endpoint filter through messages and send them to the appropriate user.
First, I am sending messages to clients using a predetermined user id. The WebSocket Server Endpoint is only aware of Session objects which contain session id's (which cannot be changed; no set method). In order to link the session id and user id, I will need a database table (much more reliable than keeping this in memory).
Then, the first message sent to the WebSocket Server Endpoint from the client will contain that user's id. This will be mapped to the database along with the session id and the client will be considered "connected". Of course, this happens after actually connecting to the WebSocket.
Now, once "connected", we can send (signaling) messages to another user, as long as, we know their user id (which I assume we do). The WebSocket Server Endpoint checks the recipient user id against the database to find their session id. Once found, the message can be sent.
Finally, on the client side, we can decipher the message and send the appropriate one back (using the same process above).
So, as stated before, we will need a database table for the clients session and user id's. Since I'm using Java for all the back-end code, we will use ORM (object relational mapping) by creating an Entity class to hold the attributes. Something like this:
@Entity
public class WebSocketUser {
@Id@GeneratedValue
private long id;
private long userId;
private long sessionId;
//constructors, getters and setters
}
Now, we can make the Server Endpoint class:
@ServerEndpoint("/SignalingServerEndpoint")
public class SignalingServerEndpoint {
//these class variables will be useful for
//access to the database and such
private EntityManagerFactory emf;
private EntityManager em;
private EntityTransaction tx;
private TypedQuery<WebsocketUser> query;
private WebsocketUser wsu;
Since, we are not in an EJB, we must control the Entity Manager like in an application managed environment. Add the onOpen and onClose methods to the Websocket:
@OnOpen
public void open(Session session, Endpoint config){
emf = Persistence.createEntityManagerFactory(""); //TODO add persistence unit reference here
em = emf.createEntityManager();
tx = em.getTransaction();
}
@OnClose
public void close(Session session, CloseReason reason){
//if session is closing and information still exists in the database; remove it
if (!wsu.equals(null)){
tx.begin();
em.remove(wsu);
tx.commit();
}
em.close();
emf.close();
}
Next, in the onMessage method in the WebSocket Server Endpoint, we filter the messages. I chose to send messages in the JSON format. This allows you to easily decipher the information (I used the org.json library). The onMessage method:
@OnMessage
public void message(Session session, String msg){
try {
JSONObject obj = new JSONObject(msg);
if (!obj.has("toUserId")){
//adding user to the database, so they can access their session ID with their user ID
WebsocketUser wsu = new WebsocketUser(obj.getLong("userId"), session.getId());
tx.begin();
em.persist(wsu);
tx.commit();
}else if (obj.has("toUserId")){
//message to be sent to the user with the specified user ID
//get session ID from database
query = em.createQuery("SELECT u FROM WebsocketUser u WHERE u.userId = " + obj.getLong("toUserId"), WebsocketUser.class);
wsu = (WebsocketUser) query.getSingleResult();
Set<Session> sessions = session.getOpenSessions();
for (Iterator<Session> i = sessions.iterator(); i.hasNext();){
Session s = i.next();
if (s.getId().equals(wsu.getSessionId())){
s.getAsyncRemote().sendText(obj.getString("message"));
break;
}
}
}
} catch (JSONException e) {
e.printStackTrace();
}
}
Finally, all we have left is the client side (javascript). Create the WebSocket variable:
var signalingSocket = new WebSocket("/SignalingServerEndpoint"); //TODO need the full and correct path to the WebSocket
Now, for the method that will send the message to "connect" to the WebSocket Server Endpoint:
function connect(){
var msg = {
"userId": userId
};
signalingSocket.send(JSON.stringify(msg));
And, finally, all we have is the onMessage method on the client side (which will decipher the message and possibly send information to the other client) and all the actual signaling code (ICE servers, constraints, etc.). I won't go into all the signaling work but there is a good tutorial here. I hope this helps anyone else faced with a similar problem. As I said, I haven't tested the code so I'm not positive if it will work. Also, I am very skeptical whether this will even be efficient. But it is at least a start.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With