Java和C/C++程序实时通讯数据移植问题的研究
简介: 摘要:本文研究了数据存储格式中大尾小尾问题,根据此原理解决了Java程序和C/C++通讯及读取服务器端文件时的数据移植问题。
问题起源
该问题起源于笔者设计的基于Web的远程测控系统。它的基本原理是:服务器端运行一VC编制的服务器程序,客户端使用Java applet;VC服务器程序接收到Java applet发送的命令后,采集各种信息,并将所有数据发向applet,实现了基于Web的远程温度、加速度的实时监控。
VC程序和Applet之间的通讯方式采用了基于TCP/IP协议的socket通讯,笔者不准备在socket的通讯本身进行过多的讲述,而将重点研究实时通讯中涉及到的数据移植问题。
回页首
数据移植的原因及其解决
基于Web的测试软件是由C++数据采集服务器程序和客户端Java显示程序两部分构成,前者用C++,后者Java语言,存在数据移植问题。因为在计算机系统中,当包含数字的二进制文件从一个结构移到另一结构时,就出现大尾小尾问题。不同CPU在多字节数(如四字节int)存储时有两种方法,一种方法叫小尾(little_endian),数据的低字节被放置在连续存储区的首位,另一种方法叫大尾(big_endian),数据的高字节被放置在连续存储区的首位。Intel 80×86家族处理器是最后一个仍然坚持小尾的主要结构。所有其他的CPU结构(Motorola 680×0和所有RISC芯片)或者是纯粹的大尾或者是既适应大尾也适应小尾,大尾被认为是更符合逻辑的方法)。当数字由小尾处理器写入文件然后又由大尾处理器读取(或者倒过来)时,数字就会被搞乱(除了0和-1)。
运用C++或C语言,数据在文件中的存储形式是与处理器相关的,这使得简单的数据文件的移植成为一个大问题。而Java作为平台独立语言,所有的数据都是以大尾形式存储到文件中,Java语言本身产生的数据文件无移植问题。但是它在与C/ C++通讯时还应注意,
举个例子:float型数据1.5在VC程序和Java程序中的表示如下:
数值 | 在c++或c程序中的字节表示 | 在java程序中的字节表示 |
1.5 | 00111111 11000000 00000000 00000000 | 00000000 00000000 11000000 00111111 |
byte[] data=new byte[4]; length=in.read(data,0,data.length);//将服务器发送的字节流读入并存入data数组 DataInputStream huin = new DataInputStream(new ByteArrayInputStream(data)); float f = huin.readFloat(); //将1.5读出 |
但是,f并不等于1.5,必须将data接收到的字节流进行数据移植(小尾排序方式改为大尾排序),data[0]的值和data[3]值互换,data[1]的值和data[2]的值互换。
byte[] data=new byte[4]; length=in.read(data,0,data.length);//将服务器发送的字节流读入并存入data数组 byte b1; b1=data[0]; data[0]=data[3]; data[3]=b1; b1=data[1]; data[1]=data[2]; data[2]=b1; DataInputStream huin = new DataInputStream(new ByteArrayInputStream(data)); float f = huin.readFloat(); //将1.5读出 |
这样,得到了正确的结果。
笔者针对此问题设计了一个数据移植类WindowsStream,它的作用是把C/C++格式的数据转换成Java格式数据,使得Java程序可以读取C/C++发送的数据和文件。该类将各种数据类型读入缓冲中(逐个字节读),然后在缓冲区中改变字节的排序方式,其源程序如下:
class WindowStream extends FilterInputStream { public WindowStream(InputStream in) { super(in); } public final byte readByte() throws IOException { int a=in.read(); return (byte)(a); } public final short readShort() throws IOException { InputStream in=this.in; int a1=in.read(); int a2=in.read(); return (short)((a2<<8)+a1); } public final int readInt() throws IOException { InputStream in=this.in; int a1=in.read(); int a2=in.read(); int a3=in.read(); int a4=in.read(); return ((a4<<24)+(a3<<16)+(a2<<8)+a1); } public final long readLong() throws IOException { InputStream in=this.in; return ((long)readInt()<<32)+(readInt()&0xFFFFFFFFL); } public final float readFloat() throws IOException { return Float.intBitsToFloat(readInt()); } public final double readDouble() throws IOException { return Double.longBitsToDouble(readLong()); } } |
配合本Java客户程序(其源程序见最后)的UDP服务器程序(服务端口8888)(该程序可以在ftp//202.114.6.107/incoming处下载或E_mailTome:windgf@263.net),在接收到客户端命令(此处设为“DAT”)后,将向客户端返回2K byte型数据,Java客户端使用UdpData的read方法接收。read方法中host表示运行UDP服务器程序的IP地址,buffer用于存储接收到的数据,ch1用于存储转换后的short型数据,下面这个程序片断简要演示了UdpData的用法。
... host=getCodeBase().getHost(); UdpData udph1=new UdpData(); ... udp1.read(host,buffer,x1); ch=1; fre=11025; len=1024; bit=16; ... UdpData的源程序: class UdpData { public UdpData(){} public void read(String host,byte[] buffer,short[] ch1) { int i,len=0; InputStream in; OutputStream out; Socket server; try { String str="DAT"; byte[] order=str.getBytes(); server=new Socket(host,8888,false); in=server.getInputStream(); out=server.getOutputStream(); out.write(order,0,order.length); out.flush(); len=in.read(buffer); server.close(); } catch(Exception e) { len=0; } try { WindowStream input=new WindowStream(new ByteArrayInputStream(buffer)); for(i=0;i<1024;i++) ch1[i]=input.readShort(); } catch(Exception e) { } } } |
回页首
服务器端.wav文件的读取
Java和C++程序在写文件时也使用了不同的数据格式,所以,Java程序不能从C++程序创建的文件直接读取二进制数据。在读取服务器端的.wav文件时,同样涉及到数据移植的问题,.wav的文件结构具有如下:
char r[4]; long int fs; char w[8]; long int hs; short int p,ch; long int hz,bhz; short int b,bit; char d[4]; long int ds; char *wave; |
ch:记录通道数,hz:采样频率,bit:模数转换的位数,ds:文件的长度,wave:指向数据的指针。此处假定long int在C/C++中是四个字节,而在Java中为8个字节。
以下是类WavFile源文件,其read方法用于读取.wav文件,location标识.wav文件的URL,buffer是用于存储原始数据的缓存,ch1和ch2分别用于存储左右两通道的数据,
class WavFile { public WavFile(){} public void read(URL location,byte[] buffer,short[] ch1,short[] ch2,int max,int[] par) { try { URLConnection con=location.openConnection(); DataInputStream in=new DataInputStream(con.getInputStream()); in.read(buffer); in.close(); } catch(Exception e){} int i,m,bit=0,ch=0,fre=0,len=0; try { WindowStream input=new WindowStream(new ByteArrayInputStream(buffer)); input.readLong(); input.readLong(); input.readInt(); input.readShort(); ch=input.readShort(); fre=input.readInt(); input.readInt(); input.readShort(); bit=input.readShort(); input.readInt(); len=input.readInt()*8/ch/bit; if(len>max)len=max; if(bit==16) for(i=0;i<len;i++) { ch1[i]=input.readShort(); if(ch==2) ch2[i]=input.readShort(); } if(bit==8) for(i=0;i<len;i++) { ch1[i]=(short)(input.readByte()-128); if(ch==2) ch2[i]=(short)(input.readByte()-128); } } catch(Exception e){} par[0]=ch; par[1]=fre; par[2]=len; par[3]=bit; } } |
下面的程序片断演示了如何在java程序中使用WavFile读取.wav文件
int ch=0,len=0,fre=0,bit=0; short x1[]=new short[16385]; short x2[]=new short[16385]; byte buffer[]=new byte[64540]; int par[]=new int[5]; name=”ding.wav”; url1=new URL(getDocumentBase(),name); WavFile h1=new WavFile(); ... h1.read(url,buffer,x1,x2,16384,par); ch=par[0]; fre=par[1]; len=par[2]; bit=par[3]; |
回页首
Java演示程序
根据前面的分析,笔者设计了一Java applet程序,成功读取服务器端.wav文件和接收UDP服务器程序发送的数据,并在applet的面版上进行显示。
import java.awt.*; import java.applet.*; import java.net.*; import java.util.*; import java.io.*; public class read_wav_udp extends Applet { TextField tex0; Button but1,but2,but3; WavFile h1=new WavFile(); UdpData udph1=new UdpData(); int ch=0,len=0,fre=0,bit=0; double t=0,a1=0,a2=0; short x1[]=new short[16385]; short x2[]=new short[16385]; byte buffer[]=new byte[64540]; int par[]=new int[5]; public read_wav_udp(){} public void init() { Font NewFnt=new Font("Roman",Font.PLAIN,12); this.setFont(NewFnt); resize(540,300); setLayout(null); udph1=new UdpData(); tex0=new TextField(""); add(tex0); tex0.reshape(28,10,500,20); but1=new Button("Ding"); add(but1); but1.reshape(130,260,60,20); but2=new Button("Chord"); add(but2); but2.reshape(230,260,60,20); but3=new Button("UDP"); add(but3); but3.reshape(330,260,60,20); data("ding.wav"); } public boolean action(Event evt,Object o) { if(evt.target==but1) data("ding.wav"); if(evt.target==but2) data("chord.wav"); if(evt.target==but3) data1(); repaint(); return true; } public void data(String name) { URL url1; try { url1=new URL(getDocumentBase(),name); } catch(Exception e) { url1=getDocumentBase(); } h1.read(url1,buffer,x1,x2,16384,par); ch=par[0]; fre=par[1]; len=par[2]; bit=par[3]; } public void data1() { String host; host=getCodeBase().getHost(); udph1.read(host,buffer,x1); ch=1; fre=11025; len=1024; bit=16; } public void paint(Graphics g) { drawwave(g); tex0.setText("CH="+ch+",Fs="+fre+",Len="+len+",Bit="+bit); } public void drawwave(Graphics g) { int i,xx1,xx2,yy1,yy2; double ff=1,sa,la,k,mm; g.setColor(Color.lightGray); g.fillRect(0,0,600,400); g.setColor(Color.black); g.drawRect(28,40,500,200); g.drawString("A",14,50); g.drawString("-A",8,240); g.drawString("0",14,145); g.drawString("0",30,255); g.drawString("T",525,255); t=500.0/fre; if(ch==0)return; sa=la=x1[1]; for(i=1;i<500;i++) { if(sa>x1[i]) sa=x1[i]; if(la<x1[i]) la=x1[i]; } a1=Math.max(Math.abs(sa),Math.abs(la)); k=1.2*a1; ff=100/k; g.setColor(Color.red); for(i=1;i<500;i++) { xx1=28+i; yy1=(int)(140-x1[i]*ff); xx2=29+i; yy2=(int)(140-x1[i+1]*ff); g.drawLine(xx1,yy1,xx2,yy2); } if(ch==1) return; sa=la=x2[1]; for(i=1;i<500;i++) { if(sa>x2[i]) sa=x2[i]; if(la<x2[i]) la=x2[i]; } a2=Math.max(Math.abs(sa),Math.abs(la)); k=1.2*a2; ff=100.0/k; g.setColor(Color.blue); for(i=i;i<500;i++) { xx1=28+i; yy1=(int)(140-x2[i]*ff); xx2=29+i; yy2=(int)(140-x2[i+1]*ff); g.drawLine(xx1,yy1,xx2,yy2); } } } class WindowStream extends FilterInputStream { ...见前面 } class WavFile { ...见前面 } class UdpData { ...见前面 } |
Java和C/C++程序实时通讯数据移植问题的研究相关推荐
- Java 接受reactjs数据_[Java教程]react.js 父子组件数据绑定实时通讯
[Java教程]react.js 父子组件数据绑定实时通讯 0 2017-09-23 17:00:14 import React,{Component} from 'react'import Reac ...
- 利用SparkSQL(java版)将离线数据或实时流数据写入hive的用法及坑点
1. 通常利用SparkSQL将离线或实时流数据的SparkRDD数据写入Hive,一般有两种方法.第一种是利用org.apache.spark.sql.types.StructType和org.ap ...
- java与微信小程序通讯_java与微信小程序实现websocket长连接
本文实例为大家分享了java与微信小程序实现websocket长连接的具体代码,供大家参考,具体内容如下 背景: 需要在小程序实现地图固定坐标下实时查看消息 java环境 :tomcat7 jdk1. ...
- java 解析数据包_一种基于Java语言的网络通讯数据包解析方法与流程
本发明涉及网络通讯领域,特别涉及一种基于Java语言的网络通讯数据包解析方法. 背景技术: 计算机系统和网络的大量普及使用使全球跨入了信息化时代.但是,正由于现代社会中几乎一切都在"计算机化 ...
- 用java实现网络爬虫,实时获取中国地震台网数据
用java实现网络爬虫,实时获取中国地震台网数据 1.如何从网络中爬取相关数据 2.怎么进行数据处理 3.绘图设计 4. 存在问题: 5.java程序的源文件 5.1 爬虫程序 5.2 绘制柱状图程序 ...
- 微信小程序webview与h5通过postMessage实现实时通讯的实现
文章转自:https://www.jb51.net/article/167957.htm 这篇文章主要介绍了微信小程序webview与h5通过postMessage实现实时通讯的实现,文中通过示例代码 ...
- Android IOS WebRTC 音视频开发总结(六二)-- 大数据解密国外实时通讯行业开发现状...
Android IOS WebRTC 音视频开发总结(六二)-- 大数据解密国外实时通讯行业开发现状 本文主要介绍国外实时通讯行业现状,文章最早发表在我们的微信公众号上,详见这里,欢迎关注微信公众号b ...
- 分析了 9 万条程序员招聘数据,Python 薪资竟反超 Java?
作者 | 周哲 责编 | 郭芮 出品 | CSDN 博客 本周,我统计了某招聘网站,获得有效程序员招聘数据9万条.针对招聘信息,提取编程语言关键字,并统计如下. 编程语言和工资 1.编程语言比例 2. ...
- 写一段jdbc连oracle的程序java类_并实现数据查询_一段Jdbc连Oracle的程序,并实现数据查询....
一段Jdbc连Oracle的程序,并实现数据查询. 程序如下: package hello.ant; import java.sql.*; public class jdbc { String dbU ...
最新文章
- 认真点,带你全面了解xutils3
- 1.6 去除字符串中的空格(trim())
- bootstrap 一排5个_Bootstrap5 列(Columns)
- 如何输入一个整数逆序输出_如何理解运算放大器的共模输入和输出摆幅限制?...
- eclipse 中警告信息汇总
- bin code led_-/bin/sh: ./led: not found的解决办法
- 简单干净的C#方法设计案例:SFCUI.AjaxValue()之三
- 软件性能测试——瓶颈分析方法,性能测试——瓶颈分析方法
- 357.计算各个位数不同的数字个数
- delphi 2010 mysql_Delphi2010 DBExpress+MySQL 程序的打包
- vs2010中svn使用教程_VS2010中使用ankhSVN | 学步园
- qq群相册显示服务器错误怎么回事,QQ群相册的删除系统提示1004错误怎么办
- Win32多语言IME应用程序编程接口(API)
- vscode中文注释斜体修改
- 【09年特长生第四题】开发区规划
- cadence ic5141安装包_Cadence IC5141 安装文档
- Flash AS3.0实战
- 阿里软件开发工程师面经
- FullCalendar demo实例
- 下行物理信道rs_LTE上行、下行物理信道及物理信道及信号的区别