在java中使用带有服务器的html5客户端

Posted

技术标签:

【中文标题】在java中使用带有服务器的html5客户端【英文标题】:using html5 client with a server in java 【发布时间】:2012-10-03 05:18:31 【问题描述】:

html5 客户端通过在 html5 websocket 客户端中提供客户端来减少程序员的工作量。学习如何在java中将这个html5 websocket客户端与服务器一起使用对许多程序员来说都是有益的。

我想创建一个 HTML5 客户端 与 java 服务器通信的示例,但我不知道如何做到这一点。任何人都可以点亮它吗?

参考:demo html5 client/server with c++

我在 http://java.dzone.com/articles/creating-websocket-chat 上找到了一个演示,但它不适合我..

【问题讨论】:

您的服务器应用程序会在 GlassFish 上运行吗? 抱歉,我以前从未做过编辑。 programmers 中有 2 个 a。之前它从未对指南中的小修改说过任何话,但是一旦我提交了修改,感谢信就会要求不要提交一封信的修改。 【参考方案1】:

我已经实现了一个简单 java 服务器端示例,我们可以看看。我首先创建一个 ServerSocket,它侦听端口 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();
    

如RFC standard for the websocket protocol 中所定义,当客户端通过 websocket 连接时,必须进行 握手。所以让我们看一下 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]);
        
    

根据 RFC - 第 1.2 节,客户端握手看起来像这样(这是 chrome 给我的,版本 22.0.1229.94 m)!

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

现在我们可以使用键映射在握手过程中创建相应的响应。引用 RFC:

为了证明握手被接收,服务器必须采取两次 信息片段并将它们组合起来形成响应。首先 一条信息来自 |Sec-WebSocket-Key|头域 在客户端握手中。对于这个头域,服务器必须取 值并将其与全局唯一标识符连接, 字符串形式的“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”,即 不太可能被不理解 WebSocket 协议。一个 SHA-1 哈希(160 位),base64 编码,这个 然后在服务器的握手中返回连接。

所以这就是我们必须做的!将 Sec-WebSocket-Key 与魔术字符串连接,使用 SHA-1 哈希函数对其进行哈希处理,然后对其进行 Base64 编码。这就是下一个丑陋的单线所做的。

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;

然后,我们只需在“Sec-WebSocket-Accept”字段中返回带有新创建的哈希的预期响应。

    //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;

我们现在已经成功地在客户端和服务器之间建立了 websocket 连接。所以现在怎么办?我们如何让他们互相交谈?我们可以从服务器向客户端发送消息开始。 注意!从这一点开始,我们不再使用 HTTP 与客户端对话。现在我们必须通信发送纯字节,并解释传入的字节。那么我们该怎么做呢?

来自服务器的消息必须采用称为“帧”的特定格式,如 RFC - 5.6 节所述。当从服务器发送消息时,RFC 声明第一个字节必须指定它是什么类型的帧。一个值为 0x81 的字节告诉客户端我们正在发送一条“单帧无屏蔽文本消息”,它基本上是 - 一条文本消息。后续字节必须表示消息的长度。紧随其后的是数据或有效负载。好吧,好吧……让我们实现它!

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();

所以要向客户端发送消息,我们只需调用 sendMessage("Hello, client!".getBytes())。

这不是太难了吗?接收来自客户端的消息怎么样?嗯,它有点复杂,但坚持下去!

从客户端发送的帧几乎的结构与从服务器发送的帧相同。第一个字节是消息类型,第二个字节是有效载荷长度。然后有一个区别:接下来的四个字节代表一个掩码。什么是掩码,为什么来自客户端的消息被屏蔽,而服务器消息却没有?从 RFC - 第 5.1 节,我们可以看到:

...客户端必须屏蔽它发送给服务器的所有帧...服务器不得屏蔽它发送给客户端的任何帧。

所以简单的答案是:我们必须这样做。那么为什么我们必须这样做,你可能会问? Didn't I tell you to read the RFC?

继续前进,在帧中的四个字节掩码之后,被掩码的有效载荷紧随其后。还有一件事,客户端必须将帧中最左边的第 9 位设置为 1,以告诉服务器消息已被屏蔽(查看 RFC 中整洁的 ASCII-art 帧 - 第 5.2 节)。最左边的第 9 位对应于我们第二个字节中的最左边的位,但是嘿,那是我们的有效载荷长度字节!这意味着来自我们客户端的所有消息的有效负载长度字节等于 0b10000000 = 0x80 + 实际有效负载长度。因此,要找出真正的有效载荷长度,我们必须从有效载荷长度字节(帧中的第二个字节)中减去 0x80、128 或 0b10000000(或您可能喜欢的任何其他数字系统)。

哇,好吧..听起来很复杂...对于“TLDR”-伙计们,总结:从第二个字节中减去 0x80 以获得有效负载长度...

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!

现在我们已经阅读了整个消息,是时候揭开它的掩码了,这样我们就可以对有效负载有所了解了。为了取消屏蔽它,我创建了一个方法,该方法将屏蔽和有效负载作为参数,并返回解码后的有效负载。所以调用完成了:

    buf = unMask(Arrays.copyOfRange(buf, 0, 4), Arrays.copyOfRange(buf, 4, buf.length));
    String message = new String(buf);
    return message;
    

现在的 unMask 方法相当可爱和小巧

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;

getSizeOfPayload 也是如此:

private int getSizeOfPayload(byte b) 
    //Must subtract 0x80 from (unsigned) masked frames
    return ((b & 0xFF) - 0x80);

就是这样!您现在应该能够使用纯套接字进行双向通信。为了完整起见,我将添加完整的 Java 类。它能够使用 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());
    
    

还有一个简单的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>

希望这会澄清一些事情,并且我对此有所了解:)

【讨论】:

哇。这就是我要说的......必须有一个更简单的方法。我刚刚实现了你的方法,但是嘘......考虑到今天的图书馆让事情变得如此简单 @HopeRunsDeep,你正在使用什么库,这让事情变得简单? @Maytham, eclipse.org/jetty/documentation/current/… 那个是jetty websocket库,非常强大且易于理解。我现在使用这是我的生产软件【参考方案2】:

使用来自客户端的 jQuery ajax 请求,并在服务器端使用休息服务。 这里是关于使用 Rest Service 创建 war 模块

article 1 (Rest Service)

这里是关于 jQuery ajax

article 2 (jQuery Ajax)

要编写Java socket server,你只需要创建主程序

  try
  
     final ServerSocket ss = new ServerSocket(8001);

     while (true)
     
        final Socket s = ss.accept();
        // @todo s.getInputStream();
     
  
  catch (final IOException ex)
  
     //
  

它是服务器部分的主要级联

【讨论】:

我想要使用 websocket 演示 html 5 客户端。请参阅 html 5 客户端的代码示例,我只是想在 java 中创建一个与该客户端通信的服务器 @llya 这是我的错误,我只是认为 html 5 客户端就足够了,现在可以看到有问题的编辑。 @llya 你知道如何在 html5 客户端和 java 服务器之间进行通信吗? html5 提供了一个客户端,所以我只希望如果我能够与这两个通信,它将减少为客户端编写代码,所以无论如何我都在尝试这样做,其他人可能知道这一点。顺便谢谢。【参考方案3】:

尝试阅读此博客。它涵盖了如何使用 spring 框架来完成你的工作。如果尚未添加,应尽快添加全面支持。

http://keaplogik.blogspot.com.au/2012/05/atmosphere-websockets-comet-with-spring.html?m=1

我还建议查看春季发行说明。

【讨论】:

【参考方案4】:

您正在运行 GlassFish。默认情况下不启用 Web 套接字。要启用它们,您必须在您的域上执行以下单行命令:

asadmin set configs.config.server-config.network-config.protocols.protocol.http-listener-1.http.websockets-support-enabled=true

HttpServlet.init(...) 方法由 servlet 容器调用,以向 servlet 指示 servlet 正在投入使用。* 所以,您的日志消息并不代表事实。

【讨论】:

服务器日志有变化吗? 不,服务器日志与以前的日志相同,我可以看到通过获取 sout 调用的 servlet,但没有其他任何东西......【参考方案5】:

您也可以使用现有的框架来实现这一点,例如:jWebsocket

【讨论】:

【参考方案6】:

这与上面的代码相同,只是它允许您从客户端接收超过 126 字节的消息。很多web socket源码还没搞清楚分片。

 // Modified code from Anders, - Christopher Price
package GoodExample;

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;

public class JfragWS 

public static final int MASK_SIZE = 4;
public static final int SINGLE_FRAME_UNMASKED = 0x81;
private ServerSocket serverSocket;
private Socket socket;

public JfragWS() throws IOException 
serverSocket = new ServerSocket(1337);
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"
     + "Origin: http://face2fame.com\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());
        System.out.println("Enter data to send");
        
     catch (IOException ex) 
        ex.printStackTrace();
    
    
);
t.start();


public String reiceveMessage() throws IOException 
String EasyBytes = null;
byte[] buf = readBytes(2); // our initial header

convertAndPrint(buf);
//System.exit(0);
EasyBytes = (String.format("%02X ", buf[1]));
int payloadadder = 0;
if (EasyBytes.contains("FE")) // Indicates extended message
    byte[] buf2 = readBytes(1);
    int a = (buf2[0] & 0xff) + 1; // if byte is zero there is one extra fragment so add 1!
    System.out.println("Number of extra bytes" + a);
    payloadadder = 2; // account for original header size
    byte[] adder = null;
    //String MagnificentString = "";
    for (int x = 0; x < a; x++)
        if(x==0)
        adder = readBytes(1);
        //MagnificentString += String.format("%02X ", adder[0]);
        payloadadder += ((adder[0] & 0xFF) - 0x80);
        if(x==1)
        payloadadder =  (buf[1] & 0xFF) + (adder[0] & 0xFF);

        
        if(x>1)
            payloadadder = (Integer.parseInt((String.format("%02X", buf2[0]) + String.format("%02X", adder[0])), 16));
            //System.out.println(String.format("%02X", buf2[0]) + String.format("%02X", adder[0]));
            


    
    System.out.println("Overflow in byte/s " + payloadadder);
    //System.out.println("Our Hex String " + MagnificentString);
    //System.exit(0);

//convertAndPrint(buf);
//dont use this byte[] buf2 = readBytes(4);

System.out.println("Headers:");

//convertAndPrint(buf2);// Check out the byte sizes
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 
    int payloadSize = 0;
    if (payloadadder <= 0)
 payloadSize = getSizeOfPayload(buf[1]);
    else 
        payloadSize = getSizeOfPayload(buf[1]) + payloadadder;
    
//  if (extendedsize>=126)   
    //payloadSize = extendedsize;
    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

int a = b & 0xff;
//System.out.println("PAYLOAD SIZE INT" + a);
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 boolean convertAndPrintHeader(byte[] bytes) 
   StringBuilder sb = new StringBuilder();
   String CaryOverDetection = new String();
   // We must test byte 2 specifically for this. In the next step we add length bytes perhaps?
   //for(int i = 0; i < bytes.length; i++) 
       //
    for (byte b : bytes) 
        CaryOverDetection = (String.format("%02X ", b));
        if (CaryOverDetection.contains("FE"))

            return false;
        
        sb.append(String.format("%02X ", b));
    
    System.out.println(sb.toString());
    return true;

    

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 
JfragWS j = new JfragWS();
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
while (true) 
   System.out.println("Write something to the client!");
   j.sendMessage(br.readLine().getBytes());



【讨论】:

以上是关于在java中使用带有服务器的html5客户端的主要内容,如果未能解决你的问题,请参考以下文章

使用 ScriptManager (Rhino) 从 Java 中使用 Javascript HTML5 类型数组,如何?

支持带有 HTML5 视频标签的桌面和移动客户端

ASP MVC 5 在不同域上运行 HTML5 客户端和 Web API 服务器

带有 Android 客户端的 Java 游戏服务器

是否有 HTML5 WebSockets 的 Java 小程序实现?

带有 spnego 的 Java websocket 客户端