java同屏软件(优化手段:线程池 + 压缩)
2018-05-24 09:41
162 查看
一 同屏软件原理
客户端请求服务端,服务端截屏(如果需要录音,也可用第三方库调用录音设备,获取声音数据),转成字节数组,通过网络传输给客户端,客户端将原始的画面数据还原,展示在组件中(awt或swing组件都可以)。
服务端和客户端之间的多次传输、就可以将画面在可控的时间间隔内一张张的展示出来。形成一种屏幕"连续"展示效果。
具体而言可以有如下几种实现方式。
1 长连接
即客户端请求了服务端之后,从此不断开(除非异常需要重新发送连接请求之外),服务端保存客户端的连接(list.add(socket))。不释放(除非客户端异常需要移除无用连接,添加新连接之外)。
2 短连接。客户端和服务端一站式请求,即一次请求一次响应。此后断开连接。
两种方式各有利弊。
本项目应用场景是在公司内部搭建局域网(路由器 + 交换机)开会时使用。所以不需要传输声音。用了几个月,效果还可以,既不会出现卡顿。内存也还在本人可接受范围内。其实压力最大的是服务端。几个人的话,在200M左右。十几个人连接。高峰期在600-700M。吃内存大户。挺吓人。
二 代码实现
本案例使用短连接实现。
客户端、服务端、屏幕工具类、压缩工具类。一共四个文件
1 客户端代码 com.sharescreen.Client.java
2 服务端代码com.sharescreen.Server.java
3 屏幕工具类 com.utils.ScreenUtils.java
4 压缩工具类 com.utils.ZipUtils.java
客户端请求服务端,服务端截屏(如果需要录音,也可用第三方库调用录音设备,获取声音数据),转成字节数组,通过网络传输给客户端,客户端将原始的画面数据还原,展示在组件中(awt或swing组件都可以)。
服务端和客户端之间的多次传输、就可以将画面在可控的时间间隔内一张张的展示出来。形成一种屏幕"连续"展示效果。
具体而言可以有如下几种实现方式。
1 长连接
即客户端请求了服务端之后,从此不断开(除非异常需要重新发送连接请求之外),服务端保存客户端的连接(list.add(socket))。不释放(除非客户端异常需要移除无用连接,添加新连接之外)。
2 短连接。客户端和服务端一站式请求,即一次请求一次响应。此后断开连接。
两种方式各有利弊。
利 | 弊 | |
长连接 | 不需要每次都发送连接请求。避免了三次握手的额外开销。 | ①服务端必须使用容器来保存客户端连接。这个容器可以是数组、List、Map等。增加内存消耗。 ②多客户端请求,意味着多线程。必须使用额外的代码保证多线程对连接的容器的安全访问。 ③考虑到客户端有可能频繁断开连接,服务端必须增加额外的代码(可以是单独的管理线程)来剔除那些已经断开了的、或者已经发生了异常了的客户端连接。 |
短连接 | 不会出现长连接的所有弊端。 | 没有了长链接的优势。意味着每次都要发送连接请求服务端。客户端开销也比较大。 |
二 代码实现
本案例使用短连接实现。
客户端、服务端、屏幕工具类、压缩工具类。一共四个文件
1 客户端代码 com.sharescreen.Client.java
package com.sharescreen; import java.awt.Image; import java.awt.Rectangle; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.InputStream; import java.net.Socket; import java.util.zip.ZipInputStream; import javax.imageio.ImageIO; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextField; import com.utils.ScreenUtils; public class Client { private static JLabel jl; private static String IP=null; private static int PORT=-1; private static JFrame jf; private static JPanel loginpanel; public static void main(String[] args) throws Exception{//先写再读 //先读再写 initFrame(); connect(); } public static void connect() throws InterruptedException{ while(PORT ==-1|| IP ==null){ Thread.sleep(10); } jf.remove(loginpanel);//移除登录框。 while(true){ Socket c = null; try { c = new Socket(IP,PORT); InputStream in = c.getInputStream(); ZipInputStream zin = new ZipInputStream(in); zin.getNextEntry(); Image img = ImageIO.read( zin ); jl.setIcon(new ImageIcon(img)); Thread.sleep(400);//客户端延时。可调。 } catch (Exception e) { e.printStackTrace(); } } } public static void initFrame(){ jf = new JFrame(); jf.setUndecorated(true); jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); jf.setBounds(new Rectangle(ScreenUtils.getScreensize())); JPanel jp = new JPanel(); jl = new JLabel(); jp.add(jl); jl.setBounds(new Rectangle(ScreenUtils.getScreensize())); jf.getContentPane().add(jp); loginpanel = new JPanel(); jf.add(loginpanel,"North"); loginpanel.add(new JLabel("IP")); JTextField ipinput = new JTextField(10); loginpanel.add(ipinput); loginpanel.add(new JLabel("端口")); JTextField portinput = new JTextField(10); loginpanel.add(portinput); JButton loginbtn = new JButton("登录"); loginpanel.add(loginbtn); jf.getContentPane().add(loginpanel,"North"); jf.setVisible(true); loginbtn.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { String ip = ipinput.getText(); String port = portinput.getText(); //未添加校验。 IP = ip; PORT = Integer.parseInt( port ); } }); } }
2 服务端代码com.sharescreen.Server.java
package com.sharescreen; import java.awt.image.BufferedImage; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import javax.imageio.ImageIO; import com.utils.ScreenUtils; /** * 优化手段 : 加入线程池+压缩 */ public class Server { public static void main(String[] args) throws Exception { ServerSocket s = new ServerSocket(8088); //创建一个带缓冲的线程池。 //Executors.newCachedThreadPool(); //通过比较发现,我的机子上。用固定线程池性能更好。 ExecutorService pools = Executors.newFixedThreadPool(10); while (true) { Socket c = s.accept();// 接收客户端连接。 System.out.println(c.getInetAddress().getHostAddress()); pools.submit(new MyTask(c));//将任务加入线程池。 c = null; } } private static class MyTask implements Runnable{ private Socket c; public MyTask(Socket c) { this.c = c; } @Override public void run() { try { OutputStream out = c.getOutputStream(); ZipOutputStream zout = new ZipOutputStream(out); zout.putNextEntry(new ZipEntry("test.jpg"));//指定入口。必须的。 zout.setLevel(5);//设置压缩等级。0-9 BufferedImage buf = ScreenUtils.getFullScreen(); ImageIO.write(buf, "jpg", zout); zout.closeEntry(); c.shutdownOutput(); //帮助垃圾回收期回收。 out = null; zout = null; c = null; buf = null; }catch (Exception e) { e.printStackTrace(); } } } }
3 屏幕工具类 com.utils.ScreenUtils.java
package com.utils; import java.awt.AWTException; import java.awt.Dimension; import java.awt.Rectangle; import java.awt.Robot; import java.awt.Toolkit; import java.awt.image.BufferedImage; public class ScreenUtils { private final static Dimension screensize=Toolkit.getDefaultToolkit().getScreenSize(); private static Robot robot = null; static{ try { robot = new Robot(); } catch (AWTException e) { e.printStackTrace(); } } public static BufferedImage getFullScreen(){ BufferedImage buf = robot.createScreenCapture(new Rectangle(screensize)); return buf; } public static Dimension getScreensize() { return screensize; } }
4 压缩工具类 com.utils.ZipUtils.java
package com.utils; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; public class ZipUtils { // 压缩 public static byte[] zip(byte[] bs) throws IOException { if (bs == null || bs.length == 0) { return null; } try( ByteArrayOutputStream out = new ByteArrayOutputStream(); GZIPOutputStream gzip = new GZIPOutputStream(out); ){ gzip.write(bs); gzip.close(); return out.toByteArray(); }catch (Exception e) { } return null; } // 解压缩 public static byte[] unzip(byte[] bs) throws IOException { if (bs == null || bs.length == 0) { return bs; } ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayInputStream in = new ByteArrayInputStream(bs); GZIPInputStream gunzip = new GZIPInputStream(in); byte[] readby = new byte[100*1024]; int readnum; while (true) { readnum = gunzip.read(readby); if(readnum==-1)break; out.write(readby, 0, readnum); } gunzip.close(); in.close(); return out.toByteArray(); } }
相关文章推荐
- Java文件压缩优化工具(ProGuard) 软件介绍 Soft content
- Java图片高优化压缩
- java zip压缩优化版 解决压缩后文件一直被占用无法删除
- Java使用线程池递归压缩文件夹下面的所有子文件
- 并查集实现-(秩优化+路径压缩+java)
- java swing版哈佛曼压缩软件
- 算法代码实现之Union-Find,Java实现,quick-find、quick-union、加权quick-union(附带路径压缩优化)
- Java 使用线程池递归压缩一个文件夹下的所有子文件
- java环境 + yuicompressor 实现代码压缩优化
- Java软件开发基础知识梳理之(3)------JDCB操作数据库性能优化策略
- Java性能优化学习之 巧用线程池ThreadPool
- Java 程序性能优化《第一章》Java性能调优概述 1.3基本调优策略和手段
- 【java】调用微信模板消息推送,线程池优化推送速度。
- Java咖啡馆---一个压缩归档实用软件
- java中的线程池技术及优化
- 屏幕录象java版软件,小心共享下.一时兴起写的哦.__压缩成zip包的:TestZip.java
- java笔记--使用线程池优化多线程编程
- Java咖啡馆(9)——一个压缩归档实用软件-Java基础-Java-编程开发
- JAVA 线程优化及线程池管理
- java.util.zip包无法正常解压rar软件压缩的rar文件