`
java_mzd
  • 浏览: 580650 次
  • 性别: Icon_minigender_1
  • 来自: 长沙
社区版块
存档分类
最新评论

用UDP改良远程监控系统浅析

阅读更多

昨天的简单的计算已经告诉了我们

用TCP/IP来发送服务器桌面信息

服务器端发出一张图片大概需要的时间是200MS

(所谓发出,即打包完成到发送结束----调用输出流的flush()方法截止)

那我们其实能做到的也就是一秒钟发送5张图片

把服务器端发送图片的时间间隔改成200MS后

发现服务器端和客户端的屏幕传输还算是比较正常

能够及时看到服务器端的操作

 

基本改良咱是实现了

可是,咱不能满足

好吧,服务器端看电影,

客户端连接看看情况怎么样

额,客户端连上后

发现看到的电影里的人动作是不连贯的

WHY?

 

大伙都做到

电影是24帧每秒

而我们现在用TCP/IP就算用多线程

发挥网络最大能量(我们寝室的网络)

一秒也就传了5帧

这肯定是没办法正常看电影的

 

 

好了

现在问题提出来了

这种情况下

我们应该怎么样继续改良呢?

 

大伙都知道

TCP/IP是面向连接、可靠的传输

用TCP/IP只要网络是通的

咱就可以放心,不怕数据传布过去

过去了不怕它收不到

可是出来混,总得有代价嘛

其中的3次握手,差错重传等等功能开销都是很大的

TCP/IP提供了可靠性以后

传输速度啥的自然就受到了很大的影响

 

而UDP是无连接、不可靠的传输

它不需要提供负责的连接机制,不需要理会差错控制

UDP只管作死的一个劲往外发

至于收不收的到,收到多少,则是跟哥无关的事

 

在对实时性要求很多的情况下

我们都只想能尽可能的多传输一些数据过去

就顾不上考虑那么多差错控制啥的了

(你这边在看直播,如果传输有差错了,总不能暂停一下,回过头来再回放,然后继续吧?)

这个时候,适当的差错是可以容忍的

实时是第一位的

 

 

好吧

既然一堆废话解析已经说的差不多了

 

现在

就正式开始通过UDP传输来改造俺们滴远程控制系统

 

其实在原有系统的基础上

基本上可以什么都不用变

改几行代码就差不多了

 

首先

其他的操控类信息还是要求要准确的

所以我们继续保留原有的TCP/IP连接中的SOCKET连接

这一部分都不动

原有系统协议,构架都不动

 

我们要实施UDP传输改良性能

无非就是把图片传输这个绝对巨头用UDP处理掉

所以

我们需要该的代码其实只是如下

 

 

在服务器端,用UDP发送取代用SOCKET发送

	// 原有的用Socket发送图片的语句
	// clientInfo.getThread().sendMessage(_screenImageMessage);
              //用UDP发送的语句
	java.net.InetAddress clientIP = clientInfo.getIPaddress();
	int port=10000;//设置UDP端口为10000
	byte[] data = _screenImageMessage.pack();
	//封装为DatagramPacket
	DatagramPacket datapacke = new DatagramPacket(data,data.length,clientIP,port);
	//用来发送的DatagramSocket对象
	DatagramSocket castSocket = new DatagramSocket(11000);
	castSocket.send(datapacke);
	Log.recordTime("结束发送截屏图像");

 

在客户端,用UDP接收,取代用SOCKET接收,至于什么接收图片监听啥的都不用改了

把用UDP接收图片独立开一个线程,连接成功后,自动启动该线程

代码如下

public class ReciveScreenImageUDP extends Thread{
	Client _client;

	public ReciveScreenImageUDP(Client client) {
		_client = client;
	}
	public void run(){
		reciveUDPMessage();
	}

	public void reciveUDPMessage() {
		while (true) {
			try {
				DatagramSocket reciveImageSocket = new DatagramSocket(10000);
				// 定义缓冲区大小
				byte[] buffer = new byte[250000];
				DatagramPacket recivedPacket = new DatagramPacket(buffer,
						buffer.length);
				reciveImageSocket.receive(recivedPacket);
				// 接收完字节数组,开始解封,处理
				MessageHead message = UnpackMessageTools
						.unpackUDPimageMessage(buffer);
				_client.UDPMessageHandle(message);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

 

 

好了

问题出来了

 

咱又得开始说废话,讲道理,玩数学了

首先,我想大家应该明白一点

UDP中传输数据都是以DatagramPacket为单位传的

也就是说,我们接收也是一个pachet一个packet的来接收

那么,接收之前,我们怎么知道每个packet里面数据量的大小呢?

恭喜你

答对了

我们就是不知道

所以

我们必须用到缓冲区数组buffer来接收packet

buffer设置小了,那么剩下的那部分数据你是收不到的

人家发送方是不对你负责的,谁叫你Y自己小气,舍不得把buffer设大一点呢

于是有人说

我们就把buffer设大一点吧

你还真是大方。。。

只有一小杯水,你开一辆卡车去运,有必要吗?

 

我们得通过算数来解决问题的

 

要算数

我们首先得知道我们到底需要多大的缓冲区

才能够完整的接收packet,同时还不能太浪费,注意,我说的是太浪费

 

恩,那我们还是先知道每次对方传来的数据有多大吧

 

好,那我们就测试,每次生成的图片大概有多大

需要测试这个,无非就是,截个屏,化为数组,看下大小

测试代码如下,比较简单,就不废话来解释了

/**
 * 用来测试每次截屏生成图片大小的类
 * @author mzd
 */
public class ImageSizeTest {
	/**
	 * @param args
	 * @throws AWTException
	 */
	public void getImageSize() throws AWTException {
		java.awt.Robot rb = new java.awt.Robot();
		Dimension d = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
		Rectangle rt = new Rectangle(0, 0, (int) d.getWidth(), (int) d
				.getHeight());
		byte[] data = null;
		for (int i = 0; i < 1000; i++) {
			BufferedImage image = rb.createScreenCapture(rt);
			data = bufferedImageTobytes(image);
			System.out.println(i + "--------图片--------->" + data.length);
		}
	}

	private byte[] bufferedImageTobytes(BufferedImage image) {
		BufferedImage bImage = new BufferedImage(image.getWidth(null), image
				.getHeight(null), BufferedImage.TYPE_INT_ARGB);
		Graphics bg = bImage.getGraphics();
		bg.drawImage(image, 0, 0, null);
		bg.dispose();
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		try {
			ImageIO.write(bImage, "jpeg", out);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return out.toByteArray();
	}

	public static void main(String args[]) throws Exception {
		ImageSizeTest test = new ImageSizeTest();
		test.getImageSize();
	}

}

 

 

我运行了1000次来观测,在这里就不可能贴出1000次的结果了

随机截取一部分吧

299--------图片--------->184001

300--------图片--------->183878

301--------图片--------->183825

302--------图片--------->183796

303--------图片--------->183710

304--------图片--------->183705

305--------图片--------->183705

306--------图片--------->183827

307--------图片--------->183827

308--------图片--------->183827

309--------图片--------->183921

310--------图片--------->184064

311--------图片--------->184040

312--------图片--------->183792

313--------图片--------->183809

314--------图片--------->183900

315--------图片--------->183867

316--------图片--------->183789

317--------图片--------->183871

318--------图片--------->183691

319--------图片--------->183685

320--------图片--------->183666

321--------图片--------->183666

322--------图片--------->183666

323--------图片--------->183666

324--------图片--------->183666

325--------图片--------->183666

326--------图片--------->183666

327--------图片--------->183666

328--------图片--------->183961

329--------图片--------->183961

330--------图片--------->183961

331--------图片--------->183961

332--------图片--------->183961

333--------图片--------->184059

很明显,我们发现每个图片的应该在200000左右

为了保险起见,

我们把缓冲区再设大点

250000

 

 

好了

废话完了

代码改完了

咱执行一次看看?

 

java.net.SocketException: The message is larger than the maximum supported by the underlying transport: Datagram send failed

    at java.net.PlainDatagramSocketImpl.send(Native Method)

    at java.net.DatagramSocket.send(DatagramSocket.java:612)

    at cn.javaeye.java_mzd.Monitor.Server.CastScreenImageUDP.castScreenImage(CastScreenImageUDP.java:49)

    at cn.javaeye.java_mzd.Monitor.Server.CastScreenImageUDP.run(CastScreenImageUDP.java:22)

    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)

    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)

    at java.lang.Thread.run(Thread.java:619)

 

 

我勒个去……………………

 

 

辛辛苦苦,看我废话半天

改了半天

结果不行?

 这也太恶心

太丢脸了

 

我没办法

别急

 

咱再慢慢分解

首先

咱看报的错误

The message is larger than the maximum supported by the underlying transport: Datagram send failed

 

大家英语都比我好

都看的明白

简单的说

就是传的文件比Datagram允许的大了

 

这是怎么回事?

人家几个G都能传

我这么小个图片竟然不能传?

TCP/IP都能传

UDP还不能传?

恭喜你答对了

TCP/IP能传

你用UDP直接用packet还就是传不了

(注意,我是说直接用packet

这是为什么呢?

还记得前面我废话过TCP/IP可以差错控制什么的嘛?

TCP的时候

协议会把大文件分块成许多小块

发表路由发送至目的地后

再通过标识来重组,对没有成功发送的提供差错控制

完成重传

UDP不提供此功能(当然,你可以直接去完善,那不是我今天讨论的内容)

 

好了

回到正题

为什么UDP会传不了呢?

我们知道

不管用UDP还是TCP最后得被封住成IP数据包

而一个IP数据报的总长度只是用16位来表示

16位,什么概念呢

也就是最多可以放64KB的数据

再回头看看我们截屏的图片大小

200KB

所以……………………

 

好了

休息会

下期分解

JAVA中图片有损压缩

 

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics