使用Jetty搭建Java Websocket Server,实现图像传输
jopen
10年前
使用Jetty,可以使用Java快速搭建一个Websocket Server。
快速创建Websocket Server
创建一个从WebSocketHandler继承的类WSHandler:
import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; import org.eclipse.jetty.websocket.api.annotations.WebSocket; import org.eclipse.jetty.websocket.server.WebSocketHandler; import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory; @WebSocket public class WSHandler extends WebSocketHandler { @OnWebSocketClose public void onClose(int statusCode, String reason) { } @OnWebSocketError public void onError(Throwable t) { } @OnWebSocketConnect public void onConnect(Session session) { } @OnWebSocketMessage public void onMessage(String message) { } @Override public void configure(WebSocketServletFactory factory) { // TODO Auto-generated method stub factory.register(WSHandler.class); } }
设置一个端口和Handler,启动Server:
public static void main(String[] args) throws Exception { Server server = new Server(2014); server.setHandler(new WSHandler()); server.setStopTimeout(0); server.start(); server.join(); }
JavaScript客户端
写一个简单的测试。
Index.htm:
<!DOCTYPE html> <html> <body> <script src="websocket.js"></script> </body> </html>
Websocket.js:
var ws = new WebSocket("ws://127.0.0.1:2014/"); ws.onopen = function() { alert("Opened"); ws.send("I'm client"); }; ws.onmessage = function (evt) { }; ws.onclose = function() { alert("Closed"); }; ws.onerror = function(err) { alert("Error: " + err); };
图像传输
功能:
-
从网页中主动获取图像
-
服务端推送图像到网页中
代码修改
从网页中获取服务端数据。
在index.htm中添加一个按钮元素和一个图片元素:
<!DOCTYPE html> <html> <body> <h1>WebSocket Image Display</h1> <input type="button" id="button" value="image" ><br> <img id="image"></<img> <script src="websocket.js"></script> </body> </html>
在Websocket.js中添加下面的代码:
ws.binaryType = "arraybuffer"; var button = document.getElementById("button"); button.onclick = function() { ws.send("image"); // send the fetch request }; ws.onmessage = function (evt) { // display the image var bytes = new Uint8Array(evt.data); var data = ""; var len = bytes.byteLength; for (var i = 0; i < len; ++i) { data += String.fromCharCode(bytes[i]); } var img = document.getElementById("image"); img.src = "data:image/png;base64,"+window.btoa(data); };
服务端收到消息之后,发送图片:
public void onMessage(String message) { System.out.println("Message: " + message); if (message.equals("image")) { System.out.println("session: " + mSession); if (mSession != null) { try { File f = new File("image\\github.jpg"); BufferedImage bi = ImageIO.read(f); ByteArrayOutputStream out = new ByteArrayOutputStream(); ImageIO.write(bi, "png", out); ByteBuffer byteBuffer = ByteBuffer.wrap(out.toByteArray()); mSession.getRemote().sendBytes(byteBuffer); out.close(); byteBuffer.clear(); } catch (IOException e) { e.printStackTrace(); } } } }
从服务端发送数据到客户端。
在服务端的UI上添加两个按钮,一个用来加载本地图片,一个用来发送图片:
package com.ui; import java.awt.BorderLayout; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.util.ArrayList; import javax.imageio.ImageIO; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.filechooser.FileNameExtensionFilter; import com.server.WSHandler; import com.server.WebSocketServer; public class UIMain extends JPanel implements ActionListener { private JButton mLoad, mSend; private JFileChooser mFileChooser; private JLabel mImage; private byte[] mData; private WebSocketServer mWebSocketServer; public UIMain() { super(new BorderLayout()); //Create a file chooser mFileChooser = new JFileChooser(); FileNameExtensionFilter filter = new FileNameExtensionFilter( ".png.jpg", "png","jpg"); mFileChooser.setFileFilter(filter); mLoad = new JButton("Load"); mLoad.addActionListener(this); mSend = new JButton("Send"); mSend.addActionListener(this); mSend.setEnabled(false); // button panel JPanel buttonPanel = new JPanel(); buttonPanel.add(mLoad); buttonPanel.add(mSend); add(buttonPanel, BorderLayout.PAGE_START); // image panel JPanel imageViewer = new JPanel(); mImage = new JLabel(); mImage.setSize(480, 640); imageViewer.add(mImage); add(imageViewer, BorderLayout.CENTER); // WebSocketServer mWebSocketServer = new WebSocketServer(); mWebSocketServer.start(); } @Override public void actionPerformed(ActionEvent e) { } private static void createAndShowGUI() { //Create and set up the window. JFrame frame = new JFrame("WebSocket Demo"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //Add content to the window. frame.add(new UIMain()); //Display the window. frame.pack(); frame.setVisible(true); frame.setResizable(false); frame.setSize(480, 700); double width = Toolkit.getDefaultToolkit().getScreenSize().getWidth(); double height = Toolkit.getDefaultToolkit().getScreenSize().getHeight(); int frameWidth = frame.getWidth(); int frameHeight = frame.getHeight(); frame.setLocation((int)(width - frameWidth) / 2, (int)(height - frameHeight) / 2); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { UIManager.put("swing.boldMetal", Boolean.FALSE); createAndShowGUI(); } }); } }
为了防止UI阻塞,Websocket server需要在线程中创建:
package com.server; import org.eclipse.jetty.server.Server; public class WebSocketServer extends Thread{ @Override public void run() { // TODO Auto-generated method stub super.run(); try { Server server = new Server(2014); server.setHandler(new WSHandler()); server.setStopTimeout(0); server.start(); server.join(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
在按钮事件中添加图片添加和发送功能:
public void sendImage(byte[] data) { if (mSession == null) return; try { ByteBuffer byteBuffer = ByteBuffer.wrap(data); mSession.getRemote().sendBytes(byteBuffer); byteBuffer.clear(); } catch (IOException e) { e.printStackTrace(); } } @Override public void actionPerformed(ActionEvent e) { if (e.getSource() == mLoad) { int returnVal = mFileChooser.showOpenDialog(UIMain.this); if (returnVal == JFileChooser.APPROVE_OPTION) { File file = mFileChooser.getSelectedFile(); // load image data to byte array try { BufferedImage bi = ImageIO.read(file); ByteArrayOutputStream out = new ByteArrayOutputStream(); ImageIO.write(bi, "png", out); mData = out.toByteArray(); out.close(); } catch (IOException exception) { exception.printStackTrace(); } mImage.setIcon(new ImageIcon(mData)); mSend.setEnabled(true); } } else if (e.getSource() == mSend) { ArrayList<WSHandler> sessions = WSHandler.getAllSessions(); for (WSHandler session : sessions) { session.sendImage(mData); } mSend.setEnabled(false); } }