IO操作中,是否有可能做到js让手机页面强制竖屏让Windows写缓

Orion - oracle提供的测试io性能的工具-2jliu-ITPUB博客
Orion - oracle提供的测试io性能的工具
1203阅读 0评论 
Orion - oracle提供的测试io性能的工具[@more@]Orion是提供的测试io性能的工具。它可以用来模拟Oracle数据库IO,也可以用来仿真ASM的条带化的功能。Orion可以支持下列IO负载1. 小的随机的IO:OLTP的应用主要是随机的读写,大小和数据的块大小一样(一般是8K)。这样的应用主要是关注的吞吐量是IOPS和一个请求的平均延时时间。Orion可以仿真一个随机IO负载。指定的读写百分比,指定的IO大小,指定的IOs,IOs是分布在不同的磁盘上。2. 大的连续的IO:数据仓库的应用,数据装载,,和会产生连续的读写流,这些读写是由多个1M的IO组成的。这些应用都是处理大数据量的数据,主要是关注总体的数据吞吐量MBPS3. 大的随机的IO: 一个连续的读写和其他的活动同时访问磁盘。基于条带化,一个连续的读写扩展到多个磁盘上。因此,在磁盘的这个层次上,许多的连续的读写被看作随机的1M的IO,又被称作多用户的连续IO。4. 混合的负载: Orion可以同时仿真前俩种负载:小的随机的IO,大的连续的IO。这将使你可以仿真,OLTP的8K的随机读写的负载和4个连续的1M IO读写的备份的负载。针对不同的IO负载,Orion可以在不同的IO压力测试并得到性能参数:MBPS,IOPS,和IO延迟时间。负载是术语,代表异步的IOs的数目。内部本质来说,每一个负载层次,Orion一直在尽快的发I/O请求来完成这个层次的I/O负载。针对随机的负载(大的和小的),负载的层次就是I/Os的数目。针对大的连续的负载,负载的层次就是连续的读写流和每次读写流的IO的数目。在负载层次范围内测试指定的负载将帮助用户理解性能是怎么受影响的。测试目标:理论上,ORION可以用来测试任何支持异步的字符设备。ORION已经在下列类型的设备上测试过。1. DAS(directed_attatched)的存储:2. SAN(storage-area network)的存储:3. ORION没有在NAS(network-attached storage).ORION对存储设备的供应商:供应商可以用ORION来理解Oracle是如何来在存储上执行的。也可以用Orion来找出适合Oracle最好的存储配置。ORION对Oracle管理员Oracle管理员可以根据期望的工作量使用Orion来评估和比较不同的存储阵列。他们也可以用Orion来决定峰值时优化的网络连接数,存储阵列数,存储阵列控制器数,和磁盘数。附录A描述了根据数据库现在的工作量来推测IOPS和MBPS需求。开始使用Orion1. 下载orion: 有Linux/x86,Solaris/SPARC 和Windows/x86版本2. 安装Orion
Linux/Solaris:解压Orion执行文件。
gunzip orion_linux_x86-64.giz
: 运行程序
C:temp> orion_windows_x86-64.msi
3. 选择测试名,我们使用的名是mytest4. 创建文件名 mytest.lun,例如:
/dev/raw/raw1
/dev/raw/raw2
/dev/raw/raw85. 验证设备是不是可以访问。可以用下面的命令:
$ dd if=/dev/raw/raw1 f=/dev/null bs=32k count=1024 1024+0 records in 1024+0 records out
6. 验证在你的平台上已经有异步IO的类库。Orion测试完全是依赖异步的IO。在linux和solaris,类库libaio需要安装并被访问。环境变量名是LD_LIBRARY_PATH或者是LIBPATH,window已经安装异步IO。7. 第一次测试,建议使用simple,simple测试衡量在不同的负载下的小随机读和大的随机读。这些结果给我一些想法,不同类型的IO和负载下的IO性能。simple测试的命令: ./orion_linux_x86-64 -run simple -testname mytest -num_disks 4ORION: ORacle IO Numbers -- Version 11.1.0.7.0mytest_5Test will take approximately 30 minutesLarger caches may take longerOrion生成的IO负载层次考虑了在mytest.lun文件中磁盘的数目。8. 结果将被记录在输出文件中。输出文件:Orion将产生几个输出文件,1. mytest_summary.txt: 这个文件包含: a. 输入参数 b. 针对大的随机和连续的工作量下观察到的最大的吞吐量 c. 针对小的随机的工作量的IO速率 d. 针对小的随机的工作量的最小的延迟时间。[root@dbs101 software]# more mytest_5_summary.txtORION VERSION 11.1.0.7.0Commandline:-run simple -testname mytest -num_disks 4This maps to this test:Test: mytestSmall IO size: 8 KBLarge IO size: 1024 KBIO Types: Small Random IOs, Large Random IOsSimulated Array Type: CONCATWrite: 0%Cache Size: Not EnteredDuration for each
Point: 60 secondsSmall Columns:,
0Large Columns:,
8Total Data Points: 29Name: /dev/sda5 Size: Name: /dev/sdb1 Size: Name: /dev/sdc1 Size: Name: /dev/sdd1 Size: 4 FILEs found.Maximum Large MBPS=62.80 @ Small=0 and Large=7Maximum Small IOPS=647 @ Small=20 and Large=0Minimum Small Latency=7.32 @ Small=1 and Large=02. mytest_mbps.csv文件:这是个csv文件。包含大的随机或者连续的IO工作量。所有的csv输出文件有个2维表。行代表大的IO负载层次。列代表小的IO负载层次。simple测试不包含大的和小的IO结合。所以MBPS文件只有一个列,0代表没有小的IO。# more mytest_5_mbps.csvLarge/Small,
62.44在这个例子中,当负载层次在5的时候,没有小的IO操作,我们得到的数据吞吐量是60.33MBPS我们可以用excel图形来显示MBPS的速率。test_5_mbps.csvLarge/Small,
3. mytest_iops.csv: 这是个小的随机的IO工作量的IOPS吞吐量。4. mytest_lat.csv: 这是个小的随机的IO工作量下的延迟时间。# more mytest_5_lat.csvLarge/Small,
85. mytest_trace.txt: 包含扩展的,未处理的测试输出。输入参数:Orion可以使用命令的参数来测试任意一种工作量。强制输入的参数:run:测试运行的层次,这个选项提供simple,normal,advanced的层次。如果没有指定advanced,那么设置有些非强制的参数(-cache_size和-verbose)将会出错。 simple:产生小的随机的IO和大的连续的IO工作量。在这个选项中,小的随机的IO和 大的连续的IO是分开测试的。这个参数对应下列的Orion调用: ./orion -run advanced -testname mytest
-num_disks 4
-size_small 8 -size_large 1024 -type rand
-simulate concat -write 0 -duragion 60
-matrix basic normal: 除了simple的功能外,还会产生小的随机的IO和大的连续的IO的结合。 ./orion -run advanced -testname mytest
-num_disks 4
-size_small 8 -size_large 1024 -type rand
-simulate concat -write 0 -duragion 60
-matrix detailed advanced: 如果用这个选项,用户需要指定可选的参数。testname: 输入文件必须是.lunnum_disks: 实际测试的物理磁盘的数目。可选的输入参数:help:帮助信息size_small: 小的随机工作量的IO的大小(KB)size_large: 大的随机的或者连续工作量的大小(KB)。type:大的IO的工作量(默认是rand): rand:大的随机的IO seq:大的连续的IOnum_streamIO: 每个大的连续读写流的IO数目。只是在-type seq下使用。simulate:大的连续的IO工作量小的数据分布。 contact:串联指定的luns成一个虚拟的卷。在虚拟的卷上的连续的测试从某个点到一个 lun的结束点。然后再到下一个lun。 raid0:在指定的luns上条带化成一个虚拟的卷。条带的大小是1M(和asm的条带大小一 致),可以通过参数-stripe来更改。write: 和读相比的写的百分比,这个参数在小的随机的和大的连续的IO工作量下适用。在大的连续的IO,每个读写流要么是读要么是写。这个参数是指只是写百分比。写的数据都是垃圾数据。 写的测试将破坏的指定的lun。cache_size: 存储阵列的读写缓存大小(MB)。针对大的连续的IO工作量,Orion将在每个测试点之前warm的cache。使用缓存大小来决定缓存操作。如果没有指定,将有个默认值。如果是0的话,将没有warm缓存。duration: 每个测试点的时间。(默认是60)matrix: 混合工作量测试的类型 basic:没有混合的工作量,小的随机的IO和大的连续的IO分开测试。 detailed:小的随机的IO和大的连续的IO结合起来测试。 point: 单个测试点,S代表小的随机的IO,L代表大的随机/连续的IO。S -num_small L -num_large col: 大的随机/连续的IO row: 小的随机的IO max:和detailed一样,只是在最大的负载下测试工作量。可以用-num_small和 -num_large参数指定。num_small: 小的随机的IO的最大数目。num_large: 大的随机的IO或者一个读写流的并发数目。verbose:打印进度和状态到控制台。命令行例子: 为了理解你的存储性能,小的随机读和大的随机IO读工作量,先开始运行: ./orion -run simple -num_disks 4 -testname mytest 测试小的随机读和大的随机读的IO工作量,运行: ./orion -run normal -testname mytest -num_disks 4 测试32K和1MB随机读的组合 ./orion -run advanced -testname mytest -num_disks 4 -size_small 32
-size_large 1024 -type rand -matrix detailed 测试1MB连续写流,仿真1MB的raid-0条带化 ./orion -run advanced -testname mytest -num_disk 4 -simulate raid0
-stripe 1024 -write 100 -type seq -matrix col -num_small 0常见问题:在.lun中的卷发生IO错误: 用dd拷贝文件命令来验证 验证操作系统支持异步IO 在linux和solaris中, 类库libaio必须在类库路径中如果使用的是NAS 确保文件系统被加载 .lun的文件要包含一个或多个已有文件,Orion不和目录或加载点工 作。文件要足够大,能代表你实际数据文件大小。 NFS在linux下异步IO性能很差 如果测试的时候遇到没有初始化的或者未写过的块的时候,有些智能的NAS系统 将产生伪造的数据,解决方法是写所有的块。如果在windows下测试 在裸设备上测试的时候,要映射一个盘符如果是运行32位Orion在64位Linux上 拷贝一份32位的libaio到64位机器上如果测试的磁盘数超过30 你应该使用duration参数,并为每个测试点制定一个更长的时间(120秒)。因 为Orion让所有的轴都运行在一个负载下。每个测试点需要加大时间。 你可能遇到下列的错误
specify a longer -duration value.类库的错误 参考第一个常见错误 NT-ONLY:确保oracle的类库和orion在同一个目录遇到”unbelievably good" 可能有个很大读写缓存,一般存储阵列控制器有很大的影响。找出缓存的大小, 并在参数-cache_size中为orion指定。 卷的大小不够,尝试关闭缓存。如果其他卷共享存储,将会看到突出的IO操作。 Orion报告长时间运行 如果num_disks高的话,运行时间也很长。 参数cache_size影响运行时间,Orion为每个测试点准备缓存需要2分钟。 如 果你关闭了你的缓存,你可以指定参数cache_size=0 如果指定的参数duration很长,运行时间是很长。附录A:分类数据库的IO负载为了正确的配置数据库的存储设备,必须了解数据库的性能需求。 1 IO请求主要是单个块还是多个块
数据库将发出多个IO请求:并行查询,查询大数据量的表扫描,直接数据装
载,备份恢复。一般来说,OLTP主要是单个IO请求,DSS数据仓库是多个IO请
求。 2 平均和峰值的IOPS是多少? 写占多少百分比。 3 平均和峰值的MBPS是多少?写占多少百分比。如果你的数据库IO请求主要是单个块,那就关注IOPS,如果数据库IO请求主要是多个块,那就关注MBPS。10gR2数据库:可以从视图v$sysstat得到IO的类型。 单个数据块的读:"physical read total IO requests" - "physical read total multi block requests" 多个数据块的读:"physical read total multi block requests" 读的总和:"physical read total IO requests" 单个数据块的写:"physical write total IO requests" - "physical write total multi block requests" 多个数据块的写:"physical write total multi block requests" 写的总和:"physical write total IO requests"使用这些数据,你可以评估在一段时间范围内(包含正常时间和峰值时间)读写的IOPS和MBPS,select name, value
from v$sysstat where name in ('physical read total IO requests',
'physical read total multi block requests',
'physical write total IO requests',
'physical write total multi block requests');NAME VALUEphysical read total IO requests physical read total multi block requests 2255995physical write total IO requests 9968770physical write total multi block requests
251551单个数据块读是98%单个数据块写实97%也可以从awr报表中得到这些数据。Instance Activity Stats
DB/Inst: DBS108A/dbs108a
-> Ordered by statistic name
per Second
per Trans-------------------------------- ------------------ -------------- -------------...physical read total IO requests
38.7physical read total bytes
319,881,216
444,897.4physical read total multi block
0.2...physical write total IO requests
6.0physical write total bytes
49,528,320
68,885.0physical write total multi block
0.0附录B:数据仓库在数据仓库设计和的时候,IO性能是一个关键的部分,典型的数据仓库系统是IO集中,操作在大数据量上,数据加载,重建索引和创建物化视图。数据仓库支持的IO必须设计符合过度的需求。数据仓库的存储配置是根据IO带宽,而不是总的容量。磁盘的容量比磁盘吞吐量速率发展快,结果少数几个磁盘可以存储大量的数据。但是大量的磁盘不能提供同样IO吞吐量。你可以用多个磁盘和管道来得到最大的带宽。条带化是一种方法来实现。实现一个大的条带大小(1M)来确保时间来定位磁盘和传输数据。orion可以仿真连续的IO吞吐量,例如: 白天的工作量:当终端客户,其他应用查询系统:许多单独的并发只读IO 数据装载:终端用户可能访问数据库,写的工作量和一些可能并行读(也许是 装载程序或者终端用户) 重建索引和物化视图:读写工作量 备份:只读的工作量,可能高的并行度 使用下列选项来仿真不同的数据仓库的工作量 run:使用advanced来仿真只读连续的IO large:大的连续读的IO大小。这个参数是os io大小的整数倍。 type: 使用seq num_streamIO: 增加这个参数来仿真并行执行操作。指定计划的并行度。一个 好的开始点是CPU数目*一个CPU的线程数。 simulate:如果使用硬件条带化或者卷管理器条带化,则使用concat。如果还 没有条带化,比如ASM,则使用raid0。默认条带大小是1M。 write:写占用的百分比。 matrix: 使用point来仿真单个连续工作量,或者使用col来仿真不断增加的大 的连续的工作量。 num_large: 最大的大的IOs./orion -run advanced -testname mytest -matrix point -num_small 0 -num_large 4 -size_large 1024 -num_disks 4 -type seq -num_streamIO 8 -simulate raid0 -cache_size 0 -write 0-verbose Commandline:-run advanced -testname mytest -matrix point -num_small 0 -num_large 4 -size_large 1024 -num_disks 4 -type seq -num_streamIO 8 -simulate raid0 -cache_size 0 -write 0This maps to this test:Test: mytestSmall IO size: 8 KBLarge IO size: 1024 KBIO Types: Small Random IOs, Large Sequential StreamsNumber of Concurrent IOs Per Stream: 8Force streams to separate disks: NoSimulated Array Type: RAID 0Stripe Depth: 1024 KBWrite: 0%Cache Size: 0 MBDuration for each Data Point: 60 secondsSmall Columns:,
0Large Columns:,
4Total Data Points: 1Name: /dev/sda5 Size: Name: /dev/sdb1 Size: Name: /dev/sdc1 Size: Name: /dev/sdd1 Size: 4 FILEs found.Maximum Large MBPS=66.88 @ Small=0 and Large=4从测试数据看,在这种情况下,吞吐量是66.88M。理想的情况下:oracle可以达到95%。下面的语句4个并发的会话。select /*+ NO_MERGE(sales) */ count(*) from (select /*+ FULL(s) PARALLEL (s,8) */* from all_sales s) sales在一个很好平衡的数据仓库配置,应该有足够的IO来利用CPU。作为一个起始点,可以使用下列规则:一个GHZ的CPU可以驱动100M。比如说有4个3G的CPUs,那么你的存储应该提供4*3*100=1200MBPS的吞吐量。在环境中,这个数量可以乘以节点的数目。1. 1 disk./orion_linux_x86-64 -run simple -num_disks 1 -testname mytest1ORION VERSION 11.1.0.7.0Commandline:-run simple -num_disks 1 -testname mytest1This maps to this test:Test: mytest1Small IO size: 8 KBLarge IO size: 1024 KBIO Types: Small Random IOs, Large Random IOsSimulated Array Type: CONCATWrite: 0%Cache Size: Not EnteredDuration for each Data Point: 60 secondsSmall Columns:,
0Large Columns:,
2Total Data Points: 8Name: /dev/sda5 Size: 1 FILEs found.Maximum Large MBPS=29.20 @ Small=0 and Large=2Maximum Small IOPS=177 @ Small=4 and Large=0Minimum Small Latency=7.59 @ Small=1 and Large=0从这里看到这个磁盘的极限是177IOPS,29.20MBPS。avg-cpu:
%sys %iowait
87.39Device:
rrqm/s wrqm/s
wkB/s avgrq-szsda5
0.00 164.73
0.60 2635.67
6.06 100.22从IOSTAT的输出看到当这个磁盘的IOPS是164的时候,%util利用率已经是100%。等待时间是18ms。2. 2 disk./orion_linux_x86-64 -run simple -num_disks 2 -testname mytest2Commandline:-run simple -num_disks 2 -testname mytest2This maps to this test:Test: mytest2Small IO size: 8 KBLarge IO size: 1024 KBIO Types: Small Random IOs, Large Random IOsSimulated Array Type: CONCATWrite: 0%Cache Size: Not EnteredDuration for each Data Point: 60 secondsSmall Columns:,
0Large Columns:,
4Total Data Points: 15Name: /dev/sda5 Size: Name: /dev/sdb1 Size: 2 FILEs found.Maximum Large MBPS=50.94 @ Small=0 and Large=4Maximum Small IOPS=330 @ Small=10 and Large=0Minimum Small Latency=7.41 @ Small=1 and Large=0从summary文件中看到两个磁盘的极限,MBPS是50.94M, IPOS是330。avg-cpu:
%sys %iowait
87.22Device:
rrqm/s wrqm/s
wkB/s avgrq-sz avgqu-sz
0.00 101.61
0.60 1622.49
0.00 99.20
0.40 1600.00
74.70从IOSTAT的输出看到当这个磁盘的IOPS是200的时候,%util利用率已经是77%。等待时间是10ms。avg-cpu:
%sys %iowait
87.19Device:
rrqm/s wrqm/s
wkB/s avgrq-sz avgqu-sz
0.00 166.73
0.80 2686.97
0.00 165.73
0.40 2658.12
96.47从IOSTAT的输出看到当这个磁盘的IOPS是330左右的时候,%util利用率已经是99%。等待时间是30ms。avg-cpu:
%sys %iowait
87.07Device:
rrqm/s wrqm/s
wkB/s avgrq-sz avgqu-sz
0.00 52.10
0.60 52533.87
9.62 26266.93
0.00 49.70
0.20 51309.02
6.41 25654.51
84.91从IOSTAT的输出看到当这个磁盘的MBPS是51M左右的时候,%util利用率已经是93%。等待时间是70ms。两个磁盘的条带大小是1M。第一块磁盘:.1 = 504K, 第二块磁盘:.7=516K3. 3 disk./orion_linux_x86-64 -run simple -num_disks 3 -testname mytest3Commandline:-run simple -num_disks 3 -testname mytest3This maps to this test:Test: mytest3Small IO size: 8 KBLarge IO size: 1024 KBIO Types: Small Random IOs, Large Random IOsSimulated Array Type: CONCATWrite: 0%Cache Size: Not EnteredDuration for each Data Point: 60 secondsSmall Columns:,
0Large Columns:,
6Total Data Points: 22Name: /dev/sda5 Size: Name: /dev/sdb1 Size: Name: /dev/sdc1 Size: 3 FILEs found.Maximum Large MBPS=73.25 @ Small=0 and Large=6Maximum Small IOPS=492 @ Small=15 and Large=0Minimum Small Latency=7.30 @ Small=1 and Large=0从summary文件中看到三个磁盘的极限,MBPS是73.25M, IPOS是492。avg-cpu:
%sys %iowait
87.27Device:
rrqm/s wrqm/s
wkB/s avgrq-sz avgqu-sz
0.00 108.40
0.80 1731.20
0.00 104.60
0.40 1676.80
0.00 100.00
0.40 1609.60
71.88从IOSTAT的输出看到当三个磁盘的IOPS是300的时候,%util利用率已经是70%。等待时间是10ms。avg-cpu:
%sys %iowait
87.37Device:
rrqm/s wrqm/s
wkB/s avgrq-sz avgqu-sz
0.00 165.13
0.60 2658.12
0.00 160.72
0.20 2581.16
0.00 160.92
0.20 2571.54
95.79从IOSTAT的输出看到当三个磁盘的IOPS是485的时候,%util利用率已经是97%。等待时间是29ms。3. 3 disk./orion_linux_x86-64 -run simple -num_disks 4 -testname mytest4ORION VERSION 11.1.0.7.0Commandline:-run simple -num_disks 4 -testname mytest4This maps to this test:Test: mytest4Small IO size: 8 KBLarge IO size: 1024 KBIO Types: Small Random IOs, Large Random IOsSimulated Array Type: CONCATWrite: 0%Cache Size: Not EnteredDuration for each Data Point: 60 secondsSmall Columns:,
0Large Columns:,
8Total Data Points: 29Name: /dev/sda5 Size: Name: /dev/sdb1 Size: Name: /dev/sdc1 Size: Name: /dev/sdd1 Size: 4 FILEs found.Maximum Large MBPS=63.24 @ Small=0 and Large=8Maximum Small IOPS=649 @ Small=20 and Large=0Minimum Small Latency=7.27 @ Small=1 and Large=0从summary文件中看到四个磁盘的极限,MBPS是63.24M, IPOS是649。avg-cpu:
%sys %iowait
87.14Device:
rrqm/s wrqm/s
wkB/s avgrq-sz avgqu-sz
0.00 92.77
0.60 1487.55
0.00 93.57
0.40 1500.40
0.00 86.14
0.40 1375.10
0.00 86.95
0.00 1394.38
62.67从IOSTAT的输出看到当三个磁盘的IOPS是357的时候,%util利用率已经是70%。等待时间是10ms。avg-cpu:
%sys %iowait
87.34Device:
rrqm/s wrqm/s
wkB/s avgrq-sz avgqu-sz
0.00 155.51
0.80 2459.32
0.00 163.33
0.40 2619.64
0.00 163.33
0.40 2606.81
0.00 157.92
0.00 2561.92
94.67从IOSTAT的输出看到当三个磁盘的IOPS是638的时候,%util利用率已经是96%。等待时间是30ms。响应时间+IOPS 就说明了系统的当前io状况啊,响应时间在 10ms 左右能达到的最大iops 能力,是系统io能力的一个最重要指标。衡量系统io能力,我们也是关注的高峰期一段时间内稳定运行的状况简单来说,如果一个database在peak time显示出来的db file sequential/scattered readaverage wait time的指标明显低于7 ms, 那么,我们不应该把焦点集中在Storage 上。虽然这个指标不一定说明Storage 性能很好,但是至少表明系统的瓶颈并不在Storage 上1,改善Storage性能未必能获得很高回报。反之,如果一个database在peak time显示出来的db file sequential/scattered read average wait time的指标明显高于10 ms,我们可能需要关注Storage的性能是否存在瓶颈,当然,同时也可以从降低Total IO的角度去处理。一个random IO的理论时间是:7ms =
4-5ms(磁盘平均寻道时间)+ 2ms (传输时间) + 一些其它的消耗如果不考虑file system cache hit(raw device/direct IO)
以及storage cache hit , 同时没有磁盘竞争,那么,db file sequntial read的时间就会是 7ms左右.而由于file system cache hit和storage cache hit的情况下,没有磁盘竞争的系统db filesequntial read 会小于 7ms 如果有磁盘竞争,而且竞争产生的延迟> file system cache hit和storage cache hit的好处,就会大于7ms .10ms 可以说是一个经验值,就是磁盘竞争产生的延迟比较高了
北京皓辰网域网络信息技术有限公司. 版权所有最近一门课要求编写一个上位机串口通信工具,我基于Java编写了一个带有图形界面的简单串口通信工具,下面详述一下过程,供大家参考 ^_^
首先,你需要下载一个额外的支持Java串口通信操作的jar包,由于m比较老了,而且不支持64位系统,这里推荐Rxtx这个jar包(32位/64位均支持)。
官方下载地址: (注:可能需要FQ才能下载)
不能FQ的童鞋,可以在这里下载:
下载解压jar包并在 Java Build Path 下引入:
注:如果运行过程中抛出 java.lang.UnsatisfiedLinkError 错误或 gnu.io 下的类找不到,请将rxtx解压包中的&rxtxParallel.dll,rxtxSerial.dll 这两个文件复制到&C:\Windows\System32 目录下即可解决该错误。
关于该jar包的使用,我写了一个SerialTool.java类,该类提供关于串口通信的各简单服务,代码如下(注意该类位于 serialPort 包里):
package serialP
import java.io.IOE
import java.io.InputS
import java.io.OutputS
import java.util.ArrayL
import java.util.E
import java.util.TooManyListenersE
import mPortI
import gnu.io.NoSuchPortE
import gnu.io.PortInUseE
import gnu.io.SerialP
import gnu.io.SerialPortEventL
import gnu.io.UnsupportedCommOperationE
import serialException.*;
* 串口服务类,提供打开、关闭串口,读取、发送串口数据等服务(采用单例设计模式)
* @author zhong
public class SerialTool {
private static SerialTool serialTool = null;
//在该类被ClassLoader加载时就初始化一个SerialTool对象
if (serialTool == null) {
serialTool = new SerialTool();
//私有化SerialTool类的构造方法,不允许其他类生成SerialTool对象
private SerialTool() {}
* 获取提供服务的SerialTool对象
* @return serialTool
public static SerialTool getSerialTool() {
if (serialTool == null) {
serialTool = new SerialTool();
return serialT
* 查找所有可用端口
* @return 可用端口名称列表
public static final ArrayList&String& findPort() {
//获得当前所有可用串口
Enumeration&CommPortIdentifier& portList = CommPortIdentifier.getPortIdentifiers();
ArrayList&String& portNameList = new ArrayList&&();
//将可用串口名添加到List并返回该List
while (portList.hasMoreElements()) {
String portName = portList.nextElement().getName();
portNameList.add(portName);
return portNameL
* 打开串口
* @param portName 端口名称
* @param baudrate 波特率
* @return 串口对象
* @throws SerialPortParameterFailure 设置串口参数失败
* @throws NotASerialPort 端口指向设备不是串口类型
* @throws NoSuchPort 没有该端口对应的串口设备
* @throws PortInUse 端口已被占用
public static final SerialPort openPort(String portName, int baudrate) throws SerialPortParameterFailure, NotASerialPort, NoSuchPort, PortInUse {
//通过端口名识别端口
CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(portName);
//打开端口,并给端口名字和一个timeout(打开操作的超时时间)
CommPort commPort = portIdentifier.open(portName, 2000);
//判断是不是串口
if (commPort instanceof SerialPort) {
SerialPort serialPort = (SerialPort) commP
//设置一下串口的波特率等参数
serialPort.setSerialPortParams(baudrate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
} catch (UnsupportedCommOperationException e) {
throw new SerialPortParameterFailure();
//System.out.println("Open " + portName + " sucessfully !");
return serialP
//不是串口
throw new NotASerialPort();
} catch (NoSuchPortException e1) {
throw new NoSuchPort();
} catch (PortInUseException e2) {
throw new PortInUse();
* 关闭串口
* @param serialport 待关闭的串口对象
public static void closePort(SerialPort serialPort) {
if (serialPort != null) {
serialPort.close();
serialPort = null;
* 往串口发送数据
* @param serialPort 串口对象
* @param order
待发送数据
* @throws SendDataToSerialPortFailure 向串口发送数据失败
* @throws SerialPortOutputStreamCloseFailure 关闭串口对象的输出流出错
public static void sendToPort(SerialPort serialPort, byte[] order) throws SendDataToSerialPortFailure, SerialPortOutputStreamCloseFailure {
OutputStream out = null;
out = serialPort.getOutputStream();
out.write(order);
out.flush();
} catch (IOException e) {
throw new SendDataToSerialPortFailure();
} finally {
if (out != null) {
out.close();
out = null;
} catch (IOException e) {
throw new SerialPortOutputStreamCloseFailure();
* 从串口读取数据
* @param serialPort 当前已建立连接的SerialPort对象
* @return 读取到的数据
* @throws ReadDataFromSerialPortFailure 从串口读取数据时出错
* @throws SerialPortInputStreamCloseFailure 关闭串口对象输入流出错
public static byte[] readFromPort(SerialPort serialPort) throws ReadDataFromSerialPortFailure, SerialPortInputStreamCloseFailure {
InputStream in = null;
byte[] bytes = null;
in = serialPort.getInputStream();
int bufflenth = in.available();
//获取buffer里的数据长度
while (bufflenth != 0) {
bytes = new byte[bufflenth];
//初始化byte数组为buffer中数据的长度
in.read(bytes);
bufflenth = in.available();
} catch (IOException e) {
throw new ReadDataFromSerialPortFailure();
} finally {
if (in != null) {
in.close();
in = null;
} catch(IOException e) {
throw new SerialPortInputStreamCloseFailure();
* 添加监听器
* @param port
* @param listener 串口监听器
* @throws TooManyListeners 监听类对象过多
public static void addListener(SerialPort port, SerialPortEventListener listener) throws TooManyListeners {
//给串口添加监听器
port.addEventListener(listener);
//设置当有数据到达时唤醒监听接收线程
port.notifyOnDataAvailable(true);
//设置当通信中断时唤醒中断线程
port.notifyOnBreakInterrupt(true);
} catch (TooManyListenersException e) {
throw new TooManyListeners();
注:该类方法中 throw 的 Exception 都是我自定义的 Exception,之所以这么做是为了方便在主程序中进行相应处理,下面贴其中一个Exception出来给大家做下说明:
(注意我所有自定义的 Exception 都放在 serialException 包里)
package serialE
public class SerialPortParameterFailure extends Exception {
private static final long serialVersionUID = 1L;
public SerialPortParameterFailure() {}
public String toString() {
return "设置串口参数失败!打开串口操作未完成!";
每个自定义的Exception类我都重写了它的 toString() 方法,便于主程序捕捉到该Exception后打印对应的错误信息
其中在serialException包里还有一个专门将接收到的Exception对象内的错误信息提取出来转换成字符串并返回的类,代码如下:
package serialE
import java.io.IOE
import java.io.PrintW
import java.io.StringW
* 负责将传入的Exception中的错误信息提取出来并转换成字符串;
* @author zhong
public class ExceptionWriter {
* 将Exception中的错误信息封装到字符串中并返回该字符串
* @param e 包含错误的Exception
* @return 错误信息字符串
public static String getErrorInfoFromException(Exception e) {
StringWriter sw = null;
PrintWriter pw = null;
sw = new StringWriter();
pw = new PrintWriter(sw);
e.printStackTrace(pw);
return "\r\n" + sw.toString() + "\r\n";
} catch (Exception e2) {
return "出错啦!未获取到错误信息,请检查后重试!";
} finally {
if (pw != null) {
pw.close();
if (sw != null) {
sw.close();
} catch (IOException e1) {
e1.printStackTrace();
主程序类的使用,Client.java里含有程序的入口地址(main方法),它的作用是显示一个欢迎界面并调用DataView.java这个类进行实际的串口数据显示。
Client.java代码如下:
package serialP
import java.awt.C
import java.awt.FlowL
import java.awt.F
import java.awt.F
import java.awt.G
import java.awt.GridL
import java.awt.I
import java.awt.L
import java.awt.P
import java.awt.T
import java.awt.event.KeyA
import java.awt.event.KeyE
import java.awt.event.WindowA
import java.awt.event.WindowE
import javax.swing.JOptionP
import serialException.ExceptionW
* @author zhong
public class Client extends Frame{
private static final long serialVersionUID = 1L;
* 程序界面宽度
public static final int WIDTH = 800;
* 程序界面高度
public static final int HEIGHT = 620;
* 程序界面出现位置(横坐标)
public static final int LOC_X = 200;
* 程序界面出现位置(纵坐标)
public static final int LOC_Y = 70;
Color color = Color.WHITE;
Image offScreen = null;
//用于双缓冲
//设置window的icon(这里我自定义了一下Windows窗口的icon图标,因为实在觉得哪个小咖啡图标不好看 = =)
Toolkit toolKit = getToolkit();
Image icon = toolKit.getImage(Client.class.getResource("computer.png"));
//持有其他类
DataView dataview = new DataView(this);
//主界面类(显示监控数据主面板)
* @param args
public static void main(String[] args) {
new Client().launchFrame();
* 显示主界面
public void launchFrame() {
this.setBounds(LOC_X, LOC_Y, WIDTH, HEIGHT);
//设定程序在桌面出现的位置
this.setTitle("CDIO工程项目");
//设置程序标题
this.setIconImage(icon);
this.setBackground(Color.white);
//设置背景色
this.addWindowListener(new WindowAdapter() {
//添加对窗口状态的监听
public void windowClosing(WindowEvent arg0) {
//当窗口关闭时
System.exit(0);
//退出程序
this.addKeyListener(new KeyMonitor());
//添加键盘监听器
this.setResizable(false);
//窗口大小不可更改
this.setVisible(true);
//显示窗口
new Thread(new RepaintThread()).start();
//开启重画线程
* 画出程序界面各组件元素
public void paint(Graphics g) {
Color c = g.getColor();
g.setFont(new Font("微软雅黑", Font.BOLD, 40));
g.setColor(Color.black);
g.drawString("欢迎使用上位机实时监控系统", 45, 190);
g.setFont(new Font("微软雅黑", Font.ITALIC, 26));
g.setColor(Color.BLACK);
g.drawString("Version:1.0
Powered By:ZhongLei", 280, 260);
g.setFont(new Font("微软雅黑", Font.BOLD, 30));
g.setColor(color);
g.drawString("&&&&点击Enter键进入主界面&&&&", 100, 480);
//使文字 "&&&&点击Enter键进入主界面&&&&" 黑白闪烁
if (color == Color.WHITE)
color = Color.
else if (color == color.BLACK)
color = Color.
* 双缓冲方式重画界面各元素组件
public void update(Graphics g) {
if (offScreen == null)
offScreen = this.createImage(WIDTH, HEIGHT);
Graphics gOffScreen = offScreen.getGraphics();
Color c = gOffScreen.getColor();
gOffScreen.setColor(Color.white);
gOffScreen.fillRect(0, 0, WIDTH, HEIGHT);
//重画背景画布
this.paint(gOffScreen);
//重画界面元素
gOffScreen.setColor(c);
g.drawImage(offScreen, 0, 0, null);
//将新画好的画布&贴&在原画布上
* 内部类形式实现对键盘事件的监听
private class KeyMonitor extends KeyAdapter {
public void keyReleased(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_ENTER) {
//当监听到用户敲击键盘enter键后执行下面的操作
setVisible(false);
//隐去欢迎界面
dataview.setVisible(true);
//显示监测界面
dataview.dataFrame();
//初始化监测界面
* 重画线程(每隔250毫秒重画一次)
private class RepaintThread implements Runnable {
public void run() {
while(true) {
repaint();
Thread.sleep(250);
} catch (InterruptedException e) {
//重画线程出错抛出异常时创建一个Dialog并显示异常详细信息
String err = ExceptionWriter.getErrorInfoFromException(e);
JOptionPane.showMessageDialog(null, err, "错误", RMATION_MESSAGE);
System.exit(0);
运行截图:
注:实际运行过程中最下面的&点击Enter键进入主界面&有一个一闪一闪的效果(是通过每隔一段时间重画一次界面,让这句话以白黑两色反复交替出现实现的),双缓冲方式利于解决重画时界面闪烁的问题(如果不使用双缓冲方式的话相当于每次重画时是在旧界面上一点一点画上新东西,而双缓冲实质上是通过先在内存中直接画好一张新界面图,然后一次性直接用新界面覆盖掉旧界面)
DataView.java代码如下:(该类用于实时显示串口数据)
简单说明:
硬件设备每隔一段时间通过串口发送一次数据到计算机,该串口工具成功连接至硬件设备并添加监听后,会在每次接收到数据时解析数据并更新界面;
你在使用时很可能需求跟我不一样,该类仅供参考,实际使用中你很可能需要重新制作数据显示界面以及数据解析方式
package serialP
import java.awt.B
import java.awt.C
import java.awt.C
import java.awt.F
import java.awt.F
import java.awt.G
import java.awt.I
import java.awt.L
import java.awt.T
import java.awt.event.ActionE
import java.awt.event.ActionL
import java.awt.event.WindowA
import java.awt.event.WindowE
import java.util.L
import java.util.TooManyListenersE
import javax.swing.JOptionP
import gnu.io.SerialP
import gnu.io.SerialPortE
import gnu.io.SerialPortEventL
import serialException.*;
* 监测数据显示类
* @author Zhong
public class DataView extends Frame {
private static final long serialVersionUID = 1L;
Client client = null;
private List&String& commList = null;
//保存可用端口号
private SerialPort serialPort = null;
//保存串口对象
private Font font = new Font("微软雅黑", Font.BOLD, 25);
private Label tem = new Label("暂无数据", Label.CENTER);
private Label hum = new Label("暂无数据", Label.CENTER);
private Label pa = new Label("暂无数据", Label.CENTER);
private Label rain = new Label("暂无数据", Label.CENTER);
private Label win_sp = new Label("暂无数据", Label.CENTER);
private Label win_dir = new Label("暂无数据", Label.CENTER);
private Choice commChoice = new Choice();
//串口选择(下拉框)
private Choice bpsChoice = new Choice();
//波特率选择
private Button openSerialButton = new Button("打开串口");
Image offScreen = null;
//重画时的画布
//设置window的icon
Toolkit toolKit = getToolkit();
Image icon = toolKit.getImage(DataView.class.getResource("computer.png"));
* 类的构造方法
* @param client
public DataView(Client client) {
this.client =
commList = SerialTool.findPort();
//程序初始化时就扫描一次有效串口
* 主菜单窗口显示;
* 添加Label、按钮、下拉条及相关事件监听;
public void dataFrame() {
this.setBounds(client.LOC_X, client.LOC_Y, client.WIDTH, client.HEIGHT);
this.setTitle("CDIO工程项目");
this.setIconImage(icon);
this.setBackground(Color.white);
this.setLayout(null);
this.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent arg0) {
if (serialPort != null) {
//程序退出时关闭串口释放资源
SerialTool.closePort(serialPort);
System.exit(0);
tem.setBounds(140, 103, 225, 50);
tem.setBackground(Color.black);
tem.setFont(font);
tem.setForeground(Color.white);
hum.setBounds(520, 103, 225, 50);
hum.setBackground(Color.black);
hum.setFont(font);
hum.setForeground(Color.white);
pa.setBounds(140, 193, 225, 50);
pa.setBackground(Color.black);
pa.setFont(font);
pa.setForeground(Color.white);
rain.setBounds(520, 193, 225, 50);
rain.setBackground(Color.black);
rain.setFont(font);
rain.setForeground(Color.white);
add(rain);
win_sp.setBounds(140, 283, 225, 50);
win_sp.setBackground(Color.black);
win_sp.setFont(font);
win_sp.setForeground(Color.white);
add(win_sp);
win_dir.setBounds(520, 283, 225, 50);
win_dir.setBackground(Color.black);
win_dir.setFont(font);
win_dir.setForeground(Color.white);
add(win_dir);
//添加串口选择选项
commChoice.setBounds(160, 397, 200, 200);
//检查是否有可用串口,有则加入选项中
if (commList == null || commList.size()&1) {
JOptionPane.showMessageDialog(null, "没有搜索到有效串口!", "错误", RMATION_MESSAGE);
for (String s : commList) {
commChoice.add(s);
add(commChoice);
//添加波特率选项
bpsChoice.setBounds(526, 396, 200, 200);
bpsChoice.add("1200");
bpsChoice.add("2400");
bpsChoice.add("4800");
bpsChoice.add("9600");
bpsChoice.add("14400");
bpsChoice.add("19200");
bpsChoice.add("115200");
add(bpsChoice);
//添加打开串口按钮
openSerialButton.setBounds(250, 490, 300, 50);
openSerialButton.setBackground(Color.lightGray);
openSerialButton.setFont(new Font("微软雅黑", Font.BOLD, 20));
openSerialButton.setForeground(Color.darkGray);
add(openSerialButton);
//添加打开串口按钮的事件监听
openSerialButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//获取串口名称
String commName = commChoice.getSelectedItem();
//获取波特率
String bpsStr = bpsChoice.getSelectedItem();
//检查串口名称是否获取正确
if (commName == null || commName.equals("")) {
JOptionPane.showMessageDialog(null, "没有搜索到有效串口!", "错误", RMATION_MESSAGE);
//检查波特率是否获取正确
if (bpsStr == null || bpsStr.equals("")) {
JOptionPane.showMessageDialog(null, "波特率获取错误!", "错误", RMATION_MESSAGE);
//串口名、波特率均获取正确时
int bps = Integer.parseInt(bpsStr);
//获取指定端口名及波特率的串口对象
serialPort = SerialTool.openPort(commName, bps);
//在该串口对象上添加监听器
SerialTool.addListener(serialPort, new SerialListener());
//监听成功进行提示
JOptionPane.showMessageDialog(null, "监听成功,稍后将显示监测数据!", "提示", RMATION_MESSAGE);
} catch (SerialPortParameterFailure | NotASerialPort | NoSuchPort | PortInUse | TooManyListeners e1) {
//发生错误时使用一个Dialog提示具体的错误信息
JOptionPane.showMessageDialog(null, e1, "错误", RMATION_MESSAGE);
this.setResizable(false);
new Thread(new RepaintThread()).start();
//启动重画线程
* 画出主界面组件元素
public void paint(Graphics g) {
Color c = g.getColor();
g.setColor(Color.black);
g.setFont(new Font("微软雅黑", Font.BOLD, 25));
g.drawString(" 温度: ", 45, 130);
g.setColor(Color.black);
g.setFont(new Font("微软雅黑", Font.BOLD, 25));
g.drawString(" 湿度: ", 425, 130);
g.setColor(Color.black);
g.setFont(new Font("微软雅黑", Font.BOLD, 25));
g.drawString(" 压强: ", 45, 220);
g.setColor(Color.black);
g.setFont(new Font("微软雅黑", Font.BOLD, 25));
g.drawString(" 雨量: ", 425, 220);
g.setColor(Color.black);
g.setFont(new Font("微软雅黑", Font.BOLD, 25));
g.drawString(" 风速: ", 45, 310);
g.setColor(Color.black);
g.setFont(new Font("微软雅黑", Font.BOLD, 25));
g.drawString(" 风向: ", 425, 310);
g.setColor(Color.gray);
g.setFont(new Font("微软雅黑", Font.BOLD, 20));
g.drawString(" 串口选择: ", 45, 410);
g.setColor(Color.gray);
g.setFont(new Font("微软雅黑", Font.BOLD, 20));
g.drawString(" 波特率: ", 425, 410);
* 双缓冲方式重画界面各元素组件
public void update(Graphics g) {
if (offScreen == null)
offScreen = this.createImage(Client.WIDTH, Client.HEIGHT);
Graphics gOffScreen = offScreen.getGraphics();
Color c = gOffScreen.getColor();
gOffScreen.setColor(Color.white);
gOffScreen.fillRect(0, 0, Client.WIDTH, Client.HEIGHT);
//重画背景画布
this.paint(gOffScreen);
//重画界面元素
gOffScreen.setColor(c);
g.drawImage(offScreen, 0, 0, null);
//将新画好的画布&贴&在原画布上
* 重画线程(每隔30毫秒重画一次)
private class RepaintThread implements Runnable {
public void run() {
while(true) {
//调用重画方法
repaint();
//扫描可用串口
commList = SerialTool.findPort();
if (commList != null && commList.size()&0) {
//添加新扫描到的可用串口
for (String s : commList) {
//该串口名是否已存在,初始默认为不存在(在commList里存在但在commChoice里不存在,则新添加)
boolean commExist = false;
for (int i=0; i&commChoice.getItemCount(); i++) {
if (s.equals(commChoice.getItem(i))) {
//当前扫描到的串口名已经在初始扫描时存在
commExist = true;
if (commExist) {
//当前扫描到的串口名已经在初始扫描时存在,直接进入下一次循环
//若不存在则添加新串口名至可用串口下拉列表
commChoice.add(s);
//移除已经不可用的串口
for (int i=0; i&commChoice.getItemCount(); i++) {
//该串口是否已失效,初始默认为已经失效(在commChoice里存在但在commList里不存在,则已经失效)
boolean commNotExist = true;
for (String s : commList) {
if (s.equals(commChoice.getItem(i))) {
commNotExist = false;
if (commNotExist) {
//System.out.println("remove" + commChoice.getItem(i));
commChoice.remove(i);
//如果扫描到的commList为空,则移除所有已有串口
commChoice.removeAll();
Thread.sleep(30);
} catch (InterruptedException e) {
String err = ExceptionWriter.getErrorInfoFromException(e);
JOptionPane.showMessageDialog(null, err, "错误", RMATION_MESSAGE);
System.exit(0);
* 以内部类形式创建一个串口监听类
* @author zhong
private class SerialListener implements SerialPortEventListener {
* 处理监控到的串口事件
public void serialEvent(SerialPortEvent serialPortEvent) {
switch (serialPortEvent.getEventType()) {
case SerialPortEvent.BI: // 10 通讯中断
JOptionPane.showMessageDialog(null, "与串口设备通讯中断", "错误", RMATION_MESSAGE);
case SerialPortEvent.OE: // 7 溢位(溢出)错误
case SerialPortEvent.FE: // 9 帧错误
case SerialPortEvent.PE: // 8 奇偶校验错误
case SerialPortEvent.CD: // 6 载波检测
case SerialPortEvent.CTS: // 3 清除待发送数据
case SerialPortEvent.DSR: // 4 待发送数据准备好了
case SerialPortEvent.RI: // 5 振铃指示
case SerialPortEvent.OUTPUT_BUFFER_EMPTY: // 2 输出缓冲区已清空
case SerialPortEvent.DATA_AVAILABLE: // 1 串口存在可用数据
//System.out.println("found data");
byte[] data = null;
if (serialPort == null) {
JOptionPane.showMessageDialog(null, "串口对象为空!监听失败!", "错误", RMATION_MESSAGE);
data = SerialTool.readFromPort(serialPort);
//读取数据,存入字节数组
//System.out.println(new String(data));
// 自定义解析过程,你在实际使用过程中可以按照自己的需求在接收到数据后对数据进行解析
if (data == null || data.length & 1) {
//检查数据是否读取正确
JOptionPane.showMessageDialog(null, "读取数据过程中未获取到有效数据!请检查设备或程序!", "错误", RMATION_MESSAGE);
System.exit(0);
String dataOriginal = new String(data);
//将字节数组数据转换位为保存了原始数据的字符串
String dataValid = "";
//有效数据(用来保存原始数据字符串去除最开头*号以后的字符串)
String[] elements = null;
//用来保存按空格拆分原始字符串后得到的字符串数组
//解析数据
if (dataOriginal.charAt(0) == '*') {
//当数据的第一个字符是*号时表示数据接收完成,开始解析
dataValid = dataOriginal.substring(1);
elements = dataValid.split(" ");
if (elements == null || elements.length & 1) {
//检查数据是否解析正确
JOptionPane.showMessageDialog(null, "数据解析过程出错,请检查设备或程序!", "错误", RMATION_MESSAGE);
System.exit(0);
//更新界面Label值
/*for (int i=0; i&elements. i++) {
System.out.println(elements[i]);
//System.out.println("win_dir: " + elements[5]);
tem.setText(elements[0] + " ℃");
hum.setText(elements[1] + " %");
pa.setText(elements[2] + " hPa");
rain.setText(elements[3] + " mm");
win_sp.setText(elements[4] + " m/s");
win_dir.setText(elements[5] + " &");
} catch (ArrayIndexOutOfBoundsException e) {
JOptionPane.showMessageDialog(null, "数据解析过程出错,更新界面数据失败!请检查设备或程序!", "错误", RMATION_MESSAGE);
System.exit(0);
} catch (ReadDataFromSerialPortFailure | SerialPortInputStreamCloseFailure e) {
JOptionPane.showMessageDialog(null, e, "错误", RMATION_MESSAGE);
System.exit(0);
//发生读取错误时显示错误信息后退出系统
运行截图:
整个项目源码打包下载:
阅读(...) 评论()

我要回帖

更多关于 老人强制让座老人被打 的文章

 

随机推荐