HTML5 client reduce prograamers effort by providing client in html5 websocket client. It will be beneficial to many programmers to learn how to use this html5 websocket client with server in java.
i want to create an Example of HTML5 client communicating with a java server, but i am not able to find out the way how to do it. can anyone throw a light on it ?
Reference : demo html5 client/server with c++
I have found a demo on http://java.dzone.com/articles/creating-websocket-chat but its not working for me..
I've implemented a simple java server side example which we can take a look at. I'm starting off by creating a ServerSocket which listens for a connection on port 2005
public class WebsocketServer {
public static final int MASK_SIZE = 4;
public static final int SINGLE_FRAME_UNMASKED = 0x81;
private ServerSocket serverSocket;
private Socket socket;
public WebsocketServer() throws IOException {
serverSocket = new ServerSocket(2005);
connect();
}
private void connect() throws IOException {
System.out.println("Listening");
socket = serverSocket.accept();
System.out.println("Got connection");
if(handshake()) {
listenerThread();
}
}
As defined in the RFC standard for the websocket protocol, when a client connects through a websocket, a handshake must be done. So let's take a look at the handshake() method, it's pretty ugly so will walk stepwise through it: The first part reads the client handshake.
private boolean handshake() throws IOException {
PrintWriter out = new PrintWriter(socket.getOutputStream());
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//This hashmap will be used to store the information given to the server in the handshake
HashMap<String, String> keys = new HashMap<>();
String str;
//Reading client handshake, handshake ends with CRLF which is again specified in the RFC, so we keep on reading until we hit ""...
while (!(str = in.readLine()).equals("")) {
//Split the string and store it in our hashmap
String[] s = str.split(": ");
System.out.println(str);
if (s.length == 2) {
keys.put(s[0], s[1]);
}
}
The client handshake looks something like this (this is what chrome gave me, version 22.0.1229.94 m), according to the RFC - section 1.2!
GET / HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: localhost:2005
Origin: null
Sec-WebSocket-Key: PyvrecP0EoFwVnHwC72ecA==
Sec-WebSocket-Version: 13
Sec-WebSocket-Extensions: x-webkit-deflate-frame
Now we can use the keys-map to create a corresponding response in the handshake process. Quoting from the RFC:
To prove that the handshake was received, the server has to take two pieces of information and combine them to form a response. The first piece of information comes from the |Sec-WebSocket-Key| header field in the client handshake. For this header field, the server has to take the value and concatenate this with the Globally Unique Identifier, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" in string form, which is unlikely to be used by network endpoints that do not understand the WebSocket Protocol. A SHA-1 hash (160 bits) , base64-encoded, of this concatenation is then returned in the server's handshake.
So that's what we have to do! concatenate Sec-WebSocket-Key with a magic string, hash it with the SHA-1 hash function, and Base64-encode it. This is what the next ugly one-liner does.
String hash;
try {
hash = new BASE64Encoder().encode(MessageDigest.getInstance("SHA-1").digest((keys.get("Sec-WebSocket-Key") + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes()));
} catch (NoSuchAlgorithmException ex) {
ex.printStackTrace();
return false;
}
Then we just return the expected response with the newly hash created into the "Sec-WebSocket-Accept" field.
//Write handshake response
out.write("HTTP/1.1 101 Switching Protocols\r\n"
+ "Upgrade: websocket\r\n"
+ "Connection: Upgrade\r\n"
+ "Sec-WebSocket-Accept: " + hash + "\r\n"
+ "\r\n");
out.flush();
return true;
}
We have now established a successfully websocket connection between the client and the server. So, what now? How do we make them talk to each other? We can start by sending a message from server to client. NB! We do from this point on, not talk to the client with HTTP anymore. Now we must communicate sending pure bytes, and interpret incoming bytes. So how do we do this?
A message from the server have to be in a certain format called "frames", as spesified in the RFC - section 5.6. When sending a message from the server, the RFC states that the first byte must specify what kind of frame it is. A byte with value 0x81 tells the client that we are sending a "single-frame unmasked text message", which basically is - a text message. The suqsequent byte must represent the length of the message. Following this is the data, or payload. Well, okay... let's implement that!
public void sendMessage(byte[] msg) throws IOException {
System.out.println("Sending to client");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
BufferedOutputStream os = new BufferedOutputStream(socket.getOutputStream());
//first byte is kind of frame
baos.write(SINGLE_FRAME_UNMASKED);
//Next byte is length of payload
baos.write(msg.length);
//Then goes the message
baos.write(msg);
baos.flush();
baos.close();
//This function only prints the byte representation of the frame in hex to console
convertAndPrint(baos.toByteArray());
//Send the frame to the client
os.write(baos.toByteArray(), 0, baos.size());
os.flush();
}
So to send a message to the client, we simply call sendMessage("Hello, client!".getBytes()).
That wasn't too hard? What about recieving messages from the client? Well, it's a little more complicated, but hang in there!
The frame sendt from the client is almost structured the same way as the frame sendt from the server. First byte is type of message, and second byte is payload length. Then there is a difference: the next four bytes represents a mask. What's a mask, and why is messages from the client masked, but the servers messages not? From the RFC - section 5.1, we can see that:
...a client MUST mask all frames that it sends to the server... A server MUST NOT mask any frames that it sends to the client.
So the easy answer is: we just HAVE to. Well why do we have to, you may ask? Didn't I tell you to read the RFC?
Moving on, after the four byte mask in the frame, the masked payload is following on. And one more thing, the client must set the 9th leftmost bit in the frame to 1, to tell the server that the message is masked (Check out the neat ASCII-art frame in the RFC - section 5.2). The 9th leftmost bit corresponds to our leftmost bit in our second byte, but hey, that's our payload length byte! This means that all messages from our client will have a payload length byte equal 0b10000000 = 0x80 + the actual payload length. So to find out the real payload length, we must subtract 0x80, or 128, or 0b10000000 (or any other number system you may prefer) from the payload length byte, our second byte in the frame.
Wow, okay.. that sounds complicated... For you "TLDR"-guys, summary: subtract 0x80 from the second byte to get the payload length...
public String reiceveMessage() throws IOException {
//Read the first two bytes of the message, the frame type byte - and the payload length byte
byte[] buf = readBytes(2);
System.out.println("Headers:");
//Print them in nice hex to console
convertAndPrint(buf);
//And it with 00001111 to get four lower bits only, which is the opcode
int opcode = buf[0] & 0x0F;
//Opcode 8 is close connection
if (opcode == 8) {
//Client want to close connection!
System.out.println("Client closed!");
socket.close();
System.exit(0);
return null;
}
//Else I just assume it's a single framed text message (opcode 1)
else {
final int payloadSize = getSizeOfPayload(buf[1]);
System.out.println("Payloadsize: " + payloadSize);
//Read the mask, which is 4 bytes, and than the payload
buf = readBytes(MASK_SIZE + payloadSize);
System.out.println("Payload:");
convertAndPrint(buf);
//method continues below!
Now that we have read the whole message, it's time to unmask it so we can make some sense of the payload. To unmask it I've made a method which takes the mask, and the payload as arguments, and return the decoded payload. So the call is done with:
buf = unMask(Arrays.copyOfRange(buf, 0, 4), Arrays.copyOfRange(buf, 4, buf.length));
String message = new String(buf);
return message;
}
}
Now the unMask method is rather sweet and tiny
private byte[] unMask(byte[] mask, byte[] data) {
for (int i = 0; i < data.length; i++) {
data[i] = (byte) (data[i] ^ mask[i % mask.length]);
}
return data;
}
Same goes for getSizeOfPayload:
private int getSizeOfPayload(byte b) {
//Must subtract 0x80 from (unsigned) masked frames
return ((b & 0xFF) - 0x80);
}
That's all! You should now be able to communicate in both directions using pure sockets. I will add the complete Java-class for the sake of completeness. It is capable of recieveing and sending messages with a client using websockets.
package javaapplication5;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.HashMap;
import sun.misc.BASE64Encoder;
/**
*
* @author
* Anders
*/
public class WebsocketServer {
public static final int MASK_SIZE = 4;
public static final int SINGLE_FRAME_UNMASKED = 0x81;
private ServerSocket serverSocket;
private Socket socket;
public WebsocketServer() throws IOException {
serverSocket = new ServerSocket(2005);
connect();
}
private void connect() throws IOException {
System.out.println("Listening");
socket = serverSocket.accept();
System.out.println("Got connection");
if(handshake()) {
listenerThread();
}
}
private boolean handshake() throws IOException {
PrintWriter out = new PrintWriter(socket.getOutputStream());
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
HashMap<String, String> keys = new HashMap<>();
String str;
//Reading client handshake
while (!(str = in.readLine()).equals("")) {
String[] s = str.split(": ");
System.out.println();
System.out.println(str);
if (s.length == 2) {
keys.put(s[0], s[1]);
}
}
//Do what you want with the keys here, we will just use "Sec-WebSocket-Key"
String hash;
try {
hash = new BASE64Encoder().encode(MessageDigest.getInstance("SHA-1").digest((keys.get("Sec-WebSocket-Key") + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes()));
} catch (NoSuchAlgorithmException ex) {
ex.printStackTrace();
return false;
}
//Write handshake response
out.write("HTTP/1.1 101 Switching Protocols\r\n"
+ "Upgrade: websocket\r\n"
+ "Connection: Upgrade\r\n"
+ "Sec-WebSocket-Accept: " + hash + "\r\n"
+ "\r\n");
out.flush();
return true;
}
private byte[] readBytes(int numOfBytes) throws IOException {
byte[] b = new byte[numOfBytes];
socket.getInputStream().read(b);
return b;
}
public void sendMessage(byte[] msg) throws IOException {
System.out.println("Sending to client");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
BufferedOutputStream os = new BufferedOutputStream(socket.getOutputStream());
baos.write(SINGLE_FRAME_UNMASKED);
baos.write(msg.length);
baos.write(msg);
baos.flush();
baos.close();
convertAndPrint(baos.toByteArray());
os.write(baos.toByteArray(), 0, baos.size());
os.flush();
}
public void listenerThread() {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
while (true) {
System.out.println("Recieved from client: " + reiceveMessage());
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
});
t.start();
}
public String reiceveMessage() throws IOException {
byte[] buf = readBytes(2);
System.out.println("Headers:");
convertAndPrint(buf);
int opcode = buf[0] & 0x0F;
if (opcode == 8) {
//Client want to close connection!
System.out.println("Client closed!");
socket.close();
System.exit(0);
return null;
} else {
final int payloadSize = getSizeOfPayload(buf[1]);
System.out.println("Payloadsize: " + payloadSize);
buf = readBytes(MASK_SIZE + payloadSize);
System.out.println("Payload:");
convertAndPrint(buf);
buf = unMask(Arrays.copyOfRange(buf, 0, 4), Arrays.copyOfRange(buf, 4, buf.length));
String message = new String(buf);
return message;
}
}
private int getSizeOfPayload(byte b) {
//Must subtract 0x80 from masked frames
return ((b & 0xFF) - 0x80);
}
private byte[] unMask(byte[] mask, byte[] data) {
for (int i = 0; i < data.length; i++) {
data[i] = (byte) (data[i] ^ mask[i % mask.length]);
}
return data;
}
private void convertAndPrint(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02X ", b));
}
System.out.println(sb.toString());
}
public static void main(String[] args) throws IOException, InterruptedException, NoSuchAlgorithmException {
WebsocketServer j = new WebsocketServer();
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
while (true) {
System.out.println("Write something to the client!");
j.sendMessage(br.readLine().getBytes());
}
}
}
And a simple client in html:
<!DOCTYPE HTML>
<html>
<body>
<button type="button" onclick="connect();">Connect</button>
<button type="button" onclick="connection.close()">Close</button>
<form>
<input type="text" id="msg" />
<button type="button" onclick="sayHello();">Say Hello!</button>
<script>
var connection;
function connect() {
console.log("connection");
connection = new WebSocket("ws://localhost:2005/");
// Log errors
connection.onerror = function (error) {
console.log('WebSocket Error ');
console.log(error);
};
// Log messages from the server
connection.onmessage = function (e) {
console.log('Server: ' + e.data);
alert("Server said: " + e.data);
};
connection.onopen = function (e) {
console.log("Connection open...");
}
connection.onclose = function (e) {
console.log("Connection closed...");
}
}
function sayHello() {
connection.send(document.getElementById("msg").value);
}
function close() {
console.log("Closing...");
connection.close();
}
</script>
</body>
</html>
Hope that this will clear something up, and that I threw some light on it :)
Use jQuery ajax requests from client side, and rest services on server side.
Here about creating of war module with Rest Service
article 1 (Rest Service)
here abour jQuery ajax
article 2 (jQuery Ajax)
To write Java socket server, all that you need is create main program with
try
{
final ServerSocket ss = new ServerSocket(8001);
while (true)
{
final Socket s = ss.accept();
// @todo s.getInputStream();
}
}
catch (final IOException ex)
{
//
}
it's main cascade of server part
Try reading this blog. It covers how to achieve your work using spring framework. Full support should be added soon, if not already added.
http://keaplogik.blogspot.com.au/2012/05/atmosphere-websockets-comet-with-spring.html?m=1
I'd also suggest checking the spring release notes.
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