您的位置:首页 > 编程语言 > Java开发

java同屏软件(优化手段:线程池 + 压缩)

2018-05-24 09:41 162 查看
一 同屏软件原理

客户端请求服务端,服务端截屏(如果需要录音,也可用第三方库调用录音设备,获取声音数据),转成字节数组,通过网络传输给客户端,客户端将原始的画面数据还原,展示在组件中(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();
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息