通过前面几篇文章的分析小结
我们已经知道知识纯粹的将图片按质量压缩
使图片文件小于64KB以适应UDP发送是不可行的
当图片质量小于临界点后
质量的降低对图片大小的影响是很小的
要想在保证图片清晰度的情况下用UDP来发送图片
前文已经分析
我们把图片数据分包,用UDP广播分包后的消息
UDP的不可靠性,以及根据概率学知识,大家都知道分的包越多,越危险,我们需要尽量少分包
在接收方,用一个缓冲区来接收数据包。根本数据包内容中的标识来组装包,根据实时性要求,当接收消息超过3秒还未收到兄弟包,在丢弃。
来了
先来看今天实现的思路和代码
先看依据UDP传输需求的数据对象
package cn.javaeye.java_mzd.Monitor.Tools.message;
import cn.javaeye.java_mzd.Monitor.Tools.Constance;
/**
* 用来在UDP中传送的数据对象
* @author Administrator
*
*/
public class UDPImageMessage extends MessageHead {
private int imageCounts;// 标识总的第几个图片
private int totalFlags; // 当前图片由几个包组成
private int flag;// 标识当前数据是第几个包
private byte[] data;// 数据内容
public byte[] pack() throws Exception {
totallen = 13 + 4 + 4 + 4 + data.length;
type = Constance.MESSAGE_TYPE_UDPIMAGEMESSAGE;
// 状态等通过setter方法设置
// 总长
dataOutputStream.writeInt(totallen);
// 类型
dataOutputStream.writeByte(type);
// senderID
dataOutputStream.writeInt(senderID);
// reciverID
dataOutputStream.writeInt(reciverID);
dataOutputStream.writeInt(imageCounts);
dataOutputStream.writeInt(totalFlags);
dataOutputStream.writeInt(flag);
dataOutputStream.write(data);
dataOutputStream.flush();
data = byteArrayOutputStream.toByteArray();
return data;
}
}
再来看服务器端发送UDP数据包的代码
package cn.javaeye.java_mzd.Monitor.Server.New;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import cn.javaeye.java_mzd.Monitor.Server.Tradition.ServerTools;
import cn.javaeye.java_mzd.Monitor.Tools.Log;
import cn.javaeye.java_mzd.Monitor.Tools.message.UDPImageMessage;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGEncodeParam;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
public class CastServerScreen implements Runnable {
int imageCounts;// 用来标识总的发送图片数序列
public CastServerScreen(int imageCounts){
this.imageCounts=imageCounts;
}
// 节约内存,把需要不断生成的下列对象设为属性
UDPImageMessage message = null;//分包时的UDPImageMessage对象
UDPImageMessage[] messages = null;//分包得到的UDPImageMessage数组对象
BufferedImage bufferedImage = null;//截屏得到的BufferedImage对象
DatagramPacket _datapacke = null; //用来发送的数据包对象
byte[] data=null;//用于打包图片的数组对象
@Override
public void run() {
castScreenImage();
}
/**
* 不断在设定好的组播地址中发送广播
* @param screenImageMessage
*/
public void castScreenImage() {
bufferedImage=ServerTools.shotScreen();//截屏
messages=pack(bufferedImageTobytes(bufferedImage, 0.5f));//将截屏图像按0.5的质量压缩,并且分包
Log.recordTime(imageCounts+"----开始发送--");
try {
java.net.InetAddress castIP = InetAddress.getByName("225.0.0.1");// 设置组播IP
int port = 9999;// 设置组端口
// 开始发送分包后的UDPImageMessage数组
for(int i=0;i<messages.length;i++){
data=messages[i].pack();
_datapacke = new DatagramPacket(data, data.length, castIP, port);
MulticastSocket castSocket = new MulticastSocket();
castSocket.send(_datapacke);
}
} catch (Exception e) {
e.printStackTrace();
}
Log.recordTime(imageCounts+"----完成发送--");
}
/**
* 将字节数组分包成适合UDP发送的UDPImageMessage数组
* @param data 需要打包的byte[]数组
* @return 打包后得到的UDPImageMessage数组
*/
private UDPImageMessage[] pack(byte[] data) {
Log.recordTime(imageCounts+"----开始分包--");
int t = data.length;
int buffer = 65000;// 窗口大小,
byte[] packdata = null;
int totalFlags = (t / buffer) + 1;// 分块数组大小
UDPImageMessage[] messages = new UDPImageMessage[totalFlags];
int flag = 0;
while (t > 0) {
if (t > buffer) {
packdata = new byte[buffer];
for (int i = 0; i < packdata.length; i++) {
packdata[i] = data[i + flag * buffer];
}
message = new UDPImageMessage();
message.setImageCounts(imageCounts);
message.setTotalFlags(totalFlags);
message.setFlag(flag);
message.setData(packdata);
messages[flag] = message;
t = t - buffer;
flag++;
}
if (t < buffer) {
packdata = new byte[t];
for (int i = 0; i < packdata.length; i++) {
packdata[i] = data[i + flag * buffer];
}
message = new UDPImageMessage();
message.setImageCounts(imageCounts);
message.setTotalFlags(totalFlags);
message.setFlag(flag);
message.setData(packdata);
messages[flag] = message;
t = 0;
flag++;
}
}
Log.recordTime(imageCounts+"----完成分包--");
return messages;
}
/**
* 通过 com.sun.image.codec.jpeg.JPEGCodec提供的编码器来实现图像压缩
* @param image 截屏得到的 BufferedImage
* @param quality 压缩质量
* @return 压缩后得到字节数组
*/
private byte[] bufferedImageTobytes(BufferedImage image, float quality) {
Log.recordTime(imageCounts+"----开始压缩--");
// 如果图片空,返回空
if (image == null) {
return null;
}
// 开始开始,写入byte[]
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); // 取得内存输出流
// 设置压缩参数
JPEGEncodeParam param = JPEGCodec.getDefaultJPEGEncodeParam(image);
param.setQuality(quality, false);
// 设置编码器
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(
byteArrayOutputStream, param);
try {
encoder.encode(image);
} catch (Exception ef) {
ef.printStackTrace();
}
Log.recordTime(imageCounts+"----结束压缩--");
return byteArrayOutputStream.toByteArray();
}
}
通过以上代码,实现了将一个图片按质量压缩,并且将图片信息的字节数组分包,以便用UDP发送
现在来测试下看看效果
0----开始压缩--16点57分11秒536 0----结束压缩--16点57分11秒588 0----开始分包--16点57分11秒588 0----完成分包--16点57分11秒596 0----开始发送--16点57分11秒596 1----开始压缩--16点57分11秒746 1----结束压缩--16点57分11秒796 1----开始分包--16点57分11秒798 1----完成分包--16点57分11秒798 1----开始发送--16点57分11秒798 2----开始压缩--16点57分11秒928 2----结束压缩--16点57分11秒978 2----开始分包--16点57分11秒978 2----完成分包--16点57分11秒981 2----开始发送--16点57分11秒981 3----开始压缩--16点57分12秒153 3----结束压缩--16点57分12秒208 3----开始分包--16点57分12秒208 3----完成分包--16点57分12秒208 3----开始发送--16点57分12秒208 4----开始压缩--16点57分12秒306 4----结束压缩--16点57分12秒356 4----开始分包--16点57分12秒356 4----完成分包--16点57分12秒356 4----开始发送--16点57分12秒356 0----完成发送--16点57分15秒136 5----开始压缩--16点57分15秒217 5----结束压缩--16点57分15秒267 5----开始分包--16点57分15秒267 5----完成分包--16点57分15秒267 5----开始发送--16点57分15秒267 1----完成发送--16点57分18秒224 |
截屏、打包压缩、分包都很快,在100MS内都可以完成
可是可以发现,发送的时间却需要很久
一个图片开始发送后,要等好久才能发送出去
多测试几次,发现每次发送的时间差还很不一样,差异很大
这又是为什么呢?
发送的代码明明和以前一样
没什么改动
为什么会这样呢?
把打印消息再细分,每个分包数据数组的每个数组包发送时都打印出来
0----开始压缩--17点4分23秒43 0----结束压缩--17点4分23秒98 0----开始分包--17点4分23秒98 0----完成分包--17点4分23秒105 0----开始发送--17点4分23秒105 0----分包数据块开始打包--17点4分23秒105 0----分包数据块完成打包--17点4分23秒108 0----分包数据块开始打包--17点4分23秒110 0----分包数据块完成打包--17点4分23秒110 1----开始压缩--17点4分23秒225 1----结束压缩--17点4分23秒283 1----开始分包--17点4分23秒283 1----完成分包--17点4分23秒285 1----开始发送--17点4分23秒285 1----分包数据块开始打包--17点4分23秒285 1----分包数据块完成打包--17点4分23秒285 2----开始压缩--17点4分23秒423 2----结束压缩--17点4分23秒475 2----开始分包--17点4分23秒475 2----完成分包--17点4分23秒475 2----开始发送--17点4分23秒475 2----分包数据块开始打包--17点4分23秒475 2----分包数据块完成打包--17点4分23秒475 3----开始压缩--17点4分23秒623 3----结束压缩--17点4分23秒675 3----开始分包--17点4分23秒675 3----完成分包--17点4分23秒675 3----开始发送--17点4分23秒675 3----分包数据块开始打包--17点4分23秒675 3----分包数据块完成打包--17点4分23秒675 4----开始压缩--17点4分23秒790 4----结束压缩--17点4分23秒843 4----开始分包--17点4分23秒843 4----完成分包--17点4分23秒843 4----开始发送--17点4分23秒845 4----分包数据块开始打包--17点4分23秒845 4----分包数据块完成打包--17点4分23秒845 0----分包数据块开始打包--17点4分24秒240 0----分包数据块完成打包--17点4分24秒240 1----分包数据块开始打包--17点4分24秒806 1----分包数据块完成打包--17点4分24秒809 2----分包数据块开始打包--17点4分25秒389 2----分包数据块完成打包--17点4分25秒391 3----分包数据块开始打包--17点4分25秒997 3----分包数据块完成打包--17点4分26秒0 4----分包数据块开始打包--17点4分26秒567 4----分包数据块完成打包--17点4分26秒567 0----完成发送--17点4分26秒621 5----开始压缩--17点4分26秒698 5----结束压缩--17点4分26秒748 5----开始分包--17点4分26秒748 5----完成分包--17点4分26秒748 5----开始发送--17点4分26秒748 5----分包数据块开始打包--17点4分26秒748 5----分包数据块完成打包--17点4分26秒748 1----分包数据块开始打包--17点4分27秒198 1----分包数据块完成打包--17点4分27秒198 2----分包数据块开始打包--17点4分27秒781 2----分包数据块完成打包--17点4分27秒781 3----分包数据块开始打包--17点4分28秒364 3----分包数据块完成打包--17点4分28秒364 4----分包数据块开始打包--17点4分28秒932 4----分包数据块完成打包--17点4分28秒934 5----分包数据块开始打包--17点4分29秒528 5----分包数据块完成打包--17点4分29秒528 1----完成发送--17点4分29秒580 6----开始压缩--17点4分29秒628 2----完成发送--17点4分29秒633 7----开始压缩--17点4分29秒665 3----完成发送--17点4分29秒673 6----结束压缩--17点4分29秒713 6----开始分包--17点4分29秒713 6----完成分包--17点4分29秒713 6----开始发送--17点4分29秒713 6----分包数据块开始打包--17点4分29秒713 6----分包数据块完成打包--17点4分29秒713 |
通过以上信息
我们就可以很清晰的发现了
其实每个分包的打包封装也是很快的------2MS
只所以发送的时间长,是等待CPU处理的时间长
每个图片的处理位置都是很凌乱的
1处理下处理2,2处理下处理3,3处理下可能就到4,4可能又到2
为什么会这样呢?
其实这就要联系到底层一点的东西了
CPU时钟的分配
这个咱们就不展开讲了(我也讲不清楚)
大家都知道CPU时钟在每个线程间分配是随机的
我们这里用的是线程池中的多个线程来按时间间隔发送图片
每个线程又调用了很多方法
每执行完一个,该方法对应的一个线程就停歇一次
然后别的线程的别的方法调用可能就把线程抢占了
对应这个CPU时钟的抢占
是没办法控制的
把打印的消息在细化一点
每个图片包发送前,发送后的时间,发送这个包的打包时间,我们统统打印出来
同时,为了不出现发送紊乱的情况
我们在发送UDPImageMessage数组前加上一个支持并发编程的锁(java.util.concurrent.locks.Lock)
再次打印
0----开始压缩--19点40分46秒266 0----结束压缩--19点40分46秒323 0----开始分包--19点40分46秒323 0----完成分包--19点40分46秒332 0----开始发送--19点40分46秒332 0----分包数据块开始打包--19点40分46秒336 0----分包数据块完成打包--19点40分46秒336 0----开始发送----i-----0-------19点40分46秒338 0----结束发送----i-----0-------19点40分46秒338 0----分包数据块开始打包--19点40分46秒338 0----分包数据块完成打包--19点40分46秒341 0----开始发送----i-----1-------19点40分46秒341 1----开始压缩--19点40分46秒436 1----结束压缩--19点40分46秒486 1----开始分包--19点40分46秒486 1----完成分包--19点40分46秒488 1----开始发送--19点40分46秒488 2----开始压缩--19点40分46秒636 2----结束压缩--19点40分46秒683 2----开始分包--19点40分46秒683 2----完成分包--19点40分46秒686 2----开始发送--19点40分46秒686 3----开始压缩--19点40分46秒846 3----结束压缩--19点40分46秒896 3----开始分包--19点40分46秒896 3----完成分包--19点40分46秒896 3----开始发送--19点40分46秒898 4----开始压缩--19点40分47秒8 4----结束压缩--19点40分47秒58 4----开始分包--19点40分47秒58 4----完成分包--19点40分47秒61 4----开始发送--19点40分47秒61 0----结束发送----i-----1-------19点40分47秒191 1----分包数据块开始打包--19点40分47秒191 0----完成发送--19点40分47秒191 1----分包数据块完成打包--19点40分47秒191 1----开始发送----i-----0-------19点40分47秒191 1----结束发送----i-----0-------19点40分47秒236 1----分包数据块开始打包--19点40分47秒236 1----分包数据块完成打包--19点40分47秒236 1----开始发送----i-----1-------19点40分47秒236 5----开始压缩--19点40分47秒266 5----结束压缩--19点40分47秒316 5----开始分包--19点40分47秒316 5----完成分包--19点40分47秒318 5----开始发送--19点40分47秒318 1----结束发送----i-----1-------19点40分48秒81 2----分包数据块开始打包--19点40分48秒81 1----完成发送--19点40分48秒81 2----分包数据块完成打包--19点40分48秒81 2----开始发送----i-----0-------19点40分48秒83 2----结束发送----i-----0-------19点40分48秒91 2----分包数据块开始打包--19点40分48秒91 2----分包数据块完成打包--19点40分48秒91 2----开始发送----i-----1-------19点40分48秒91 6----开始压缩--19点40分48秒119 6----结束压缩--19点40分48秒179 6----开始分包--19点40分48秒179 6----完成分包--19点40分48秒179 6----开始发送--19点40分48秒179 2----结束发送----i-----1-------19点40分49秒109 2----完成发送--19点40分49秒109 3----分包数据块开始打包--19点40分49秒109 3----分包数据块完成打包--19点40分49秒170 3----开始发送----i-----0-------19点40分49秒170 7----开始压缩--19点40分49秒198 3----结束发送----i-----0-------19点40分49秒228 3----分包数据块开始打包--19点40分49秒228 3----分包数据块完成打包--19点40分49秒230 3----开始发送----i-----1-------19点40分49秒230 7----结束压缩--19点40分49秒248 7----开始分包--19点40分49秒248 7----完成分包--19点40分49秒250 7----开始发送--19点40分49秒250 3----结束发送----i-----1-------19点40分50秒195 3----完成发送--19点40分50秒195 4----分包数据块开始打包--19点40分50秒195 4----分包数据块完成打包--19点40分50秒195 4----开始发送----i-----0-------19点40分50秒195 8----开始压缩--19点40分50秒239 4----结束发送----i-----0-------19点40分50秒246 4----分包数据块开始打包--19点40分50秒246 4----分包数据块完成打包--19点40分50秒246 4----开始发送----i-----1-------19点40分50秒246 8----结束压缩--19点40分50秒289 8----开始分包--19点40分50秒289 8----完成分包--19点40分50秒291 8----开始发送--19点40分50秒291 4----结束发送----i-----1-------19点40分51秒264 4----完成发送--19点40分51秒266 5----分包数据块开始打包--19点40分51秒266 5----分包数据块完成打包--19点40分51秒327 5----开始发送----i-----0-------19点40分51秒327 9----开始压缩--19点40分51秒357 5----结束发送----i-----0-------19点40分51秒385 5----分包数据块开始打包--19点40分51秒385 5----分包数据块完成打包--19点40分51秒387 5----开始发送----i-----1-------19点40分51秒387 9----结束压缩--19点40分51秒410 9----开始分包--19点40分51秒410 9----完成分包--19点40分51秒410 9----开始发送--19点40分51秒410 5----结束发送----i-----1-------19点40分52秒337 5----完成发送--19点40分52秒337 6----分包数据块开始打包--19点40分52秒337 6----分包数据块完成打包--19点40分52秒340 6----开始发送----i-----0-------19点40分52秒340 10----开始压缩--19点40分52秒381 6----结束发送----i-----0-------19点40分52秒406 6----分包数据块开始打包--19点40分52秒406 6----分包数据块完成打包--19点40分52秒406 6----开始发送----i-----1-------19点40分52秒406 10----结束压缩--19点40分52秒436 10----开始分包--19点40分52秒436 10----完成分包--19点40分52秒436 10----开始发送--19点40分52秒436 6----结束发送----i-----1-------19点40分53秒448 6----完成发送--19点40分53秒448 |
我们发现
时间的消耗
还是主要在抢占CPU线程资源上
因为不一定什么时候才能轮到某个图像对应的线程来发
所以
发送等待时间可能长,可能短
这就给系统造成了很大不可靠性
而且等待时间超过3秒后
就已经对实时性控制失去了意义
我们能不能追根溯源
回到前面讨论的线程去
能不能就不开过多的线程来抢占CPU时钟
只开一个,这样就每次只会发一个了呢?
其实,这事不可行的
因为除了我们的发送图片的各个线程在抢占CPU时钟外
系统的其他线程也在抢占
把传输线程减少,只会减少整个程序对CPU的占有率
只会使每个图片发送时间变得更长
那
我们是不是开的同时发送图片的线程越多
程序抢占的CPU时钟越多,这样就越好呢?
答案也是否定的
开的线程越多
程序是占有的时钟越多了
但是针对某一个线程
可能它占得CPU时钟的概率就更小了
那么
每一个图片可能发出去的时间差就变的更大了
关于此问题的解决
恳请各位高手指点
分享到:
相关推荐
标题"UDP-tongxun-.zip_udp 多线程"暗示了我们将讨论如何在UDP(User Datagram Protocol)中使用多线程技术。 UDP是一种无连接的传输层协议,它不保证数据的顺序、可靠性和完整性,但因其轻量级和低延迟特性,常...
本篇文章将深入探讨“UDP-Custom-Device.zip”压缩包中的内容,重点介绍如何利用NI VeriStand实现UDP(User Datagram Protocol)通信。 首先,UDP是一种无连接的传输协议,常用于需要高效、实时但不保证数据完整性...
UDP转发脚本udp-forwarding--udp-forward-master.zip
在这个“udp广播-server-client实例”中,我们将探讨如何在Windows环境下实现UDP广播的服务器和客户端程序。 首先,广播是指在局域网内,一个节点向所有其他节点发送数据包的过程。UDP广播是利用UDP协议进行广播...
W5500-UDP-F4-cubeMX-freeRTOS STM32F4单片机SPI驱动W5500以太网模块实现UDP通讯,工程用STM32CubeMX生成,移植野火w5500库文件。加上freeRTOS的任务需要修改加大分配内存,因为一个以太网UDP通讯的BUFF就2K了。 ...
UDP是一种无连接的、不可靠的传输协议,适用于需要高效传输、实时性要求较高的场景,如网络发现和多播通信。 以下是一个关于VB6中使用UDP广播的详细知识点: 1. **UDP广播概念**: UDP广播是指发送者将数据包发送...
总结,C# UDP多线程通信是网络编程中的一个重要实践,它结合了UDP的高效传输特性和多线程的并发优势,适用于处理大量并发的网络请求。在实际项目中,开发者需要根据具体需求选择合适的同步机制,保证程序的稳定性和...
UTF-8编码是一种广泛使用的Unicode字符编码,确保了跨平台和多语言环境下的文本一致性。在JMeter中,设置UTF-8编码可以确保测试过程中涉及的文本数据正确无误地发送和接收。 Apache JMeter版本5.4.3是这个压缩包...
在本文档中,我们将重点探讨如何使用CH395Q模块通过Socket0进行UDP广播通信。 首先,CH395Q的使用涉及到硬件连接和配置。用户可以根据具体需求,通过杜邦线将芯片的引脚连接到合适的电路板或微控制器,如STM32F103...
本文将深入探讨如何利用TIO框架开发UDP应用,以解压缩后的项目"tio-udp-showcase-master"为例,揭示其背后的技术原理和实践方法。 TIO,全称Tiny IO,是一款高性能、轻量级的Java NIO框架,它简化了网络编程的复杂...
C语言实现,TCP/IP 服务器与客户端,UDP 服务器与客户端
udp控制舵机udp-controller-serivo-master.zip
【标题】"基于UDP协议的多线程高速接收QT工程"揭示了这个项目的核心技术是使用UDP协议进行高速数据接收,并结合多线程技术来处理海量数据,且它是在QT框架下实现的。QT是一个流行的跨平台应用程序开发框架,特别适合...
总之,这个例子深入地探讨了在Linux环境中利用UDP进行多线程并发编程的技术,涵盖了套接字编程、线程创建、同步机制以及可能的文件传输协议的实现。对于学习网络编程、并发处理和UDP协议的人来说,这是一个有价值的...
基于Verilog的UDP代码,支持GMII、RGMII、SGMII等接口,FPGA开发支持1G、10G、25G速率。内含多种开发板上的实现例子,如ML605、KC705、VCU108、VCU118、ExaNIC_X10、ExaNIC_X25、HXT100G等等。是FPGA开发UDP以太网的...
本篇将详细介绍如何在C#中实现UDP广播的发送与接收,并结合线程监听和委托的概念进行深入探讨。 首先,UDP广播是一种将数据包发送到特定网络地址(通常是255.255.255.255)的方式,使得网络上的所有设备都能接收到...
总的来说,“UDP广播传输文件”是一个结合了UDP协议、多线程技术以及线程间操作控件的实用工具。它利用UDP广播的特性实现了高效的文件传输,同时通过多线程处理保证了程序的并发性能,从而在不稳定的网络环境中也能...
服务器端,利用qt实现tcp/udp发送文字消息,检测U盘插拔。里面包括x86和arm两个版本的源码。参见博客:http://blog.csdn.net/yanzi1225627/article/details/8772812
QT下多线程UDP Socket示例是一个典型的网络通信编程应用场景,它涉及到QT库中的网络模块,特别是关于UDP(用户数据报协议)的使用以及多线程技术。在本示例中,开发者创建了一个UDP服务器,该服务器能够在不影响主...
这里我们将深入探讨这个主题,分为三个主要部分:多线程、UDP协议以及数据存储。 首先,多线程是并发编程的一种方式,允许程序同时执行多个独立的任务。在多线程环境下,不同的线程可以并行处理不同的任务,提高...