I'm not going to describe the WebSockets protocol here - you can find zillions of tutorials online. In this short article I show how to build a simple chat application based on WebSockets.
The good news is that you don't have to worry about the nitty-gritty details of parsing WebSockets headers and packets, that's already implemented by open source libraries. On top of those you can write a server in Java, C#, Python, etc.
I use Tyrus, which makes it so easy to implement the server side of my chat application.
First I need to create a host for my endpoint:
Now I need to create the server itself - this ChatEndpoint type:
We have three more annotations:
- @OnOpen to mark the method for opening the connection from a client to this server; here session uniquely identifies the connecting client
- @OnClose to mark the callback triggered when the connection with a particular client closes
- @OnMessage for the method that does the most important job: whenever a message is received from a client, this method broadcasts the message to all clients (including the sender)
That's all on the server side.
So now the client is a simple web page with some jQuery embellishments:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<script type='text/javascript' src='jquery.js'></script>
<script type='text/javascript' src='jquery-ui-1.8.23.custom.min.js'></script>
<script type='text/javascript'>
var wsocket;
function connect() {
wsocket = new WebSocket("ws://localhost:8025/chat/test");
wsocket.onmessage = onMessage;
alert("Connect");
}
function onMessage(evt) {
$("#chatText").append("\n" + evt.data);
}
$(document).ready(
function() {
$("#connect").click(
function() {
wsocket = new WebSocket("ws://localhost:8025/chat/test");
wsocket.onmessage = onMessage;
}
);
$("#send").click(
function() {
wsocket.send($("#username").val() + ":" + $("#tosend").val());
$("#tosend").val("");
}
);
}
);
</script>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Chat</title>
</head>
<body>
Username: <input type="text" id="username"><br>
<input type="submit" value="Connect" id="connect">
<input type="submit" value="Disconnect"><br><br>
<textarea rows="30" cols="50" id="chatText">
</textarea><br>
<textarea rows="4" cols="50" id="tosend">
</textarea>
<br>
<input type="submit" value="Send" id="send">
</body>
</html>
Note how we open the connection with the server: new WebSocket("ws://localhost:8025/chat/test").
Sending messages from the client to the server is simple too: wsocket.send($("#username").val() + ":" + $("#tosend").val());
Incredibly easy, isn't it ?
Oh, by the way, this is the list of jars I need for the application to compile (you can use Maven to simplify the deployment a bit):
The good news is that you don't have to worry about the nitty-gritty details of parsing WebSockets headers and packets, that's already implemented by open source libraries. On top of those you can write a server in Java, C#, Python, etc.
I use Tyrus, which makes it so easy to implement the server side of my chat application.
First I need to create a host for my endpoint:
package server;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import org.glassfish.tyrus.server.Server;
public class WebSocketServer {
public static void main(String[] args) {
runServer();
}
public static void runServer() {
Server server = new Server("localhost", 8025, "/chat", ChatEndpoint.class);
try {
server.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
System.out.print("Please press a key to stop the server.");
reader.readLine();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
server.stop();
}
}
}
Look at the line where I create the Server object: I'll deploy ChatEndpoint to localhost:8025/chat.Now I need to create the server itself - this ChatEndpoint type:
package server; import java.io.IOException; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.logging.Level; import java.util.logging.Logger; import javax.websocket.CloseReason; import javax.websocket.OnClose; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.CloseReason.CloseCodes; import javax.websocket.server.ServerEndpoint; @ServerEndpoint(value = "/test") public class ChatEndpoint { private Logger logger = Logger.getLogger(this.getClass().getName()); // keep all open WebSocket sessions (from all users) private static QueueTyrus makes things easy for us. Look at the annotation @ServerEndpoint(value = "/test") - here I'm saying that my server is accessible under the relative path "/test" under the URL mentioned before, thus the whole URL becomes: localhost:8025/chat/test.queue = new ConcurrentLinkedQueue<>(); @OnOpen public void onOpen(Session session) { logger.info("Connect with session: " + session.getId()); queue.add(session); logger.log(Level.INFO, "Connected with " + session.getId()); } @OnMessage public void onMessage(String message, Session session) { logger.log(Level.INFO, "Mesage received " + message); try { // broadcast message to all open WebSocket sessions for (Session s : queue) { // include the original sender s.getBasicRemote().sendText(message); logger.log(Level.INFO, "Message sent: " + message); } } catch (IOException e) { logger.log(Level.INFO, e.toString()); } // we could have a return here, in which case the returned string // would be the one sent from server to client, // as if doing s.getBasicRemote().sendText(message) //return message + "TEST"; } @OnClose public void onClose(Session session, CloseReason closeReason) { logger.info(String.format("Session %s closed because of %s", session.getId(), closeReason)); queue.remove(session); logger.log(Level.INFO, "Connection closed with " + session.getId()); } }
We have three more annotations:
- @OnOpen to mark the method for opening the connection from a client to this server; here session uniquely identifies the connecting client
- @OnClose to mark the callback triggered when the connection with a particular client closes
- @OnMessage for the method that does the most important job: whenever a message is received from a client, this method broadcasts the message to all clients (including the sender)
That's all on the server side.
So now the client is a simple web page with some jQuery embellishments:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<script type='text/javascript' src='jquery.js'></script>
<script type='text/javascript' src='jquery-ui-1.8.23.custom.min.js'></script>
<script type='text/javascript'>
var wsocket;
function connect() {
wsocket = new WebSocket("ws://localhost:8025/chat/test");
wsocket.onmessage = onMessage;
alert("Connect");
}
function onMessage(evt) {
$("#chatText").append("\n" + evt.data);
}
$(document).ready(
function() {
$("#connect").click(
function() {
wsocket = new WebSocket("ws://localhost:8025/chat/test");
wsocket.onmessage = onMessage;
}
);
$("#send").click(
function() {
wsocket.send($("#username").val() + ":" + $("#tosend").val());
$("#tosend").val("");
}
);
}
);
</script>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Chat</title>
</head>
<body>
Username: <input type="text" id="username"><br>
<input type="submit" value="Connect" id="connect">
<input type="submit" value="Disconnect"><br><br>
<textarea rows="30" cols="50" id="chatText">
</textarea><br>
<textarea rows="4" cols="50" id="tosend">
</textarea>
<br>
<input type="submit" value="Send" id="send">
</body>
</html>
Note how we open the connection with the server: new WebSocket("ws://localhost:8025/chat/test").
Sending messages from the client to the server is simple too: wsocket.send($("#username").val() + ":" + $("#tosend").val());
Incredibly easy, isn't it ?
Oh, by the way, this is the list of jars I need for the application to compile (you can use Maven to simplify the deployment a bit):
No comments:
Post a Comment