博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java同屏软件(优化手段:线程池 + 压缩)
阅读量:5919 次
发布时间:2019-06-19

本文共 6288 字,大约阅读时间需要 20 分钟。

hot3.png

一  同屏软件原理

客户端请求服务端,服务端截屏(如果需要录音,也可用第三方库调用录音设备,获取声音数据),转成字节数组,通过网络传输给客户端,客户端将原始的画面数据还原,展示在组件中(awt或swing组件都可以)。

服务端和客户端之间的多次传输、就可以将画面在可控的时间间隔内一张张的展示出来。形成一种屏幕"连续"展示效果。

具体而言可以有如下几种实现方式。

1  长连接

即客户端请求了服务端之后,从此不断开(除非异常需要重新发送连接请求之外),服务端保存客户端的连接(list.add(socket))。不释放(除非客户端异常需要移除无用连接,添加新连接之外)。

2  短连接。客户端和服务端一站式请求,即一次请求一次响应。此后断开连接。

两种方式各有利弊。

 
长连接

不需要每次都发送连接请求。避免了三次握手的额外开销。

①服务端必须使用容器来保存客户端连接。这个容器可以是数组、List、Map等。增加内存消耗。

②多客户端请求,意味着多线程。必须使用额外的代码保证多线程对连接的容器的安全访问。

③考虑到客户端有可能频繁断开连接,服务端必须增加额外的代码(可以是单独的管理线程)来剔除那些已经断开了的、或者已经发生了异常了的客户端连接。

短连接 不会出现长连接的所有弊端。 没有了长链接的优势。意味着每次都要发送连接请求服务端。客户端开销也比较大。

本项目应用场景是在公司内部搭建局域网(路由器 + 交换机)开会时使用。所以不需要传输声音。用了几个月,效果还可以,既不会出现卡顿。内存也还在本人可接受范围内。其实压力最大的是服务端。几个人的话,在200M左右。十几个人连接。高峰期在600-700M。吃内存大户。挺吓人。

二 代码实现

本案例使用短连接实现。

客户端、服务端、屏幕工具类、压缩工具类。一共四个文件

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

 

转载于:https://my.oschina.net/lightled/blog/1817779

你可能感兴趣的文章
【STL容器学习】-关联容器与map的用法
查看>>
每一个程序猿必知之SEO
查看>>
Linux内核同步机制之(四):spin lock【转】
查看>>
决策树
查看>>
第三百一十七节,Django框架,缓存
查看>>
【微软2014实习生及秋令营技术类职位在线測试】题目1 : String reorder
查看>>
android中实现毛笔效果(View 中画图)
查看>>
有哪些 Bootstrap 的学习案例?
查看>>
.Net Excel操作之NPOI(二)常用操作封装
查看>>
Yii学习笔记之二(使用gii生成一个简单的样例)
查看>>
jquery deferred
查看>>
js单页hash路由原理与应用实战
查看>>
android形状drawable
查看>>
Linux - quota的举例说明
查看>>
SpringMVC处理方法的数据绑定
查看>>
Array负载均衡控制器(vAPV)
查看>>
HTTP与HTTPS的区别
查看>>
Tensorboard
查看>>
[Python]Python 函数调用小例子
查看>>
翻译:MariaDB wait/nowait
查看>>