Java和C/C++程序实时通讯数据移植问题的研究
簡(jiǎn)介:?摘要:本文研究了數(shù)據(jù)存儲(chǔ)格式中大尾小尾問題,根據(jù)此原理解決了Java程序和C/C++通訊及讀取服務(wù)器端文件時(shí)的數(shù)據(jù)移植問題。
問題起源
該問題起源于筆者設(shè)計(jì)的基于Web的遠(yuǎn)程測(cè)控系統(tǒng)。它的基本原理是:服務(wù)器端運(yùn)行一VC編制的服務(wù)器程序,客戶端使用Java applet;VC服務(wù)器程序接收到Java applet發(fā)送的命令后,采集各種信息,并將所有數(shù)據(jù)發(fā)向applet,實(shí)現(xiàn)了基于Web的遠(yuǎn)程溫度、加速度的實(shí)時(shí)監(jiān)控。
VC程序和Applet之間的通訊方式采用了基于TCP/IP協(xié)議的socket通訊,筆者不準(zhǔn)備在socket的通訊本身進(jìn)行過多的講述,而將重點(diǎn)研究實(shí)時(shí)通訊中涉及到的數(shù)據(jù)移植問題。
回頁首
數(shù)據(jù)移植的原因及其解決
基于Web的測(cè)試軟件是由C++數(shù)據(jù)采集服務(wù)器程序和客戶端Java顯示程序兩部分構(gòu)成,前者用C++,后者Java語言,存在數(shù)據(jù)移植問題。因?yàn)樵谟?jì)算機(jī)系統(tǒng)中,當(dāng)包含數(shù)字的二進(jìn)制文件從一個(gè)結(jié)構(gòu)移到另一結(jié)構(gòu)時(shí),就出現(xiàn)大尾小尾問題。不同CPU在多字節(jié)數(shù)(如四字節(jié)int)存儲(chǔ)時(shí)有兩種方法,一種方法叫小尾(little_endian),數(shù)據(jù)的低字節(jié)被放置在連續(xù)存儲(chǔ)區(qū)的首位,另一種方法叫大尾(big_endian),數(shù)據(jù)的高字節(jié)被放置在連續(xù)存儲(chǔ)區(qū)的首位。Intel 80×86家族處理器是最后一個(gè)仍然堅(jiān)持小尾的主要結(jié)構(gòu)。所有其他的CPU結(jié)構(gòu)(Motorola 680×0和所有RISC芯片)或者是純粹的大尾或者是既適應(yīng)大尾也適應(yīng)小尾,大尾被認(rèn)為是更符合邏輯的方法)。當(dāng)數(shù)字由小尾處理器寫入文件然后又由大尾處理器讀取(或者倒過來)時(shí),數(shù)字就會(huì)被搞亂(除了0和-1)。
運(yùn)用C++或C語言,數(shù)據(jù)在文件中的存儲(chǔ)形式是與處理器相關(guān)的,這使得簡(jiǎn)單的數(shù)據(jù)文件的移植成為一個(gè)大問題。而Java作為平臺(tái)獨(dú)立語言,所有的數(shù)據(jù)都是以大尾形式存儲(chǔ)到文件中,Java語言本身產(chǎn)生的數(shù)據(jù)文件無移植問題。但是它在與C/ C++通訊時(shí)還應(yīng)注意,
舉個(gè)例子:float型數(shù)據(jù)1.5在VC程序和Java程序中的表示如下:
| 數(shù)值 | 在c++或c程序中的字節(jié)表示 | 在java程序中的字節(jié)表示 |
| 1.5 | 00111111 11000000 00000000 00000000 | 00000000 00000000 11000000 00111111 |
| byte[] data=new byte[4]; length=in.read(data,0,data.length);//將服務(wù)器發(fā)送的字節(jié)流讀入并存入data數(shù)組 DataInputStream huin = new DataInputStream(new ByteArrayInputStream(data)); float f = huin.readFloat(); //將1.5讀出 |
但是,f并不等于1.5,必須將data接收到的字節(jié)流進(jìn)行數(shù)據(jù)移植(小尾排序方式改為大尾排序),data[0]的值和data[3]值互換,data[1]的值和data[2]的值互換。
| byte[] data=new byte[4]; length=in.read(data,0,data.length);//將服務(wù)器發(fā)送的字節(jié)流讀入并存入data數(shù)組 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讀出 |
這樣,得到了正確的結(jié)果。
筆者針對(duì)此問題設(shè)計(jì)了一個(gè)數(shù)據(jù)移植類WindowsStream,它的作用是把C/C++格式的數(shù)據(jù)轉(zhuǎn)換成Java格式數(shù)據(jù),使得Java程序可以讀取C/C++發(fā)送的數(shù)據(jù)和文件。該類將各種數(shù)據(jù)類型讀入緩沖中(逐個(gè)字節(jié)讀),然后在緩沖區(qū)中改變字節(jié)的排序方式,其源程序如下:
| 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服務(wù)器程序(服務(wù)端口8888)(該程序可以在ftp//202.114.6.107/incoming處下載或E_mailTome:windgf@263.net),在接收到客戶端命令(此處設(shè)為“DAT”)后,將向客戶端返回2K byte型數(shù)據(jù),Java客戶端使用UdpData的read方法接收。read方法中host表示運(yùn)行UDP服務(wù)器程序的IP地址,buffer用于存儲(chǔ)接收到的數(shù)據(jù),ch1用于存儲(chǔ)轉(zhuǎn)換后的short型數(shù)據(jù),下面這個(gè)程序片斷簡(jiǎn)要演示了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) { } } } |
回頁首
服務(wù)器端.wav文件的讀取
Java和C++程序在寫文件時(shí)也使用了不同的數(shù)據(jù)格式,所以,Java程序不能從C++程序創(chuàng)建的文件直接讀取二進(jìn)制數(shù)據(jù)。在讀取服務(wù)器端的.wav文件時(shí),同樣涉及到數(shù)據(jù)移植的問題,.wav的文件結(jié)構(gòu)具有如下:
| 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:記錄通道數(shù),hz:采樣頻率,bit:模數(shù)轉(zhuǎn)換的位數(shù),ds:文件的長(zhǎng)度,wave:指向數(shù)據(jù)的指針。此處假定long int在C/C++中是四個(gè)字節(jié),而在Java中為8個(gè)字節(jié)。
以下是類WavFile源文件,其read方法用于讀取.wav文件,location標(biāo)識(shí).wav文件的URL,buffer是用于存儲(chǔ)原始數(shù)據(jù)的緩存,ch1和ch2分別用于存儲(chǔ)左右兩通道的數(shù)據(jù),
| 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演示程序
根據(jù)前面的分析,筆者設(shè)計(jì)了一Java applet程序,成功讀取服務(wù)器端.wav文件和接收UDP服務(wù)器程序發(fā)送的數(shù)據(jù),并在applet的面版上進(jìn)行顯示。
| 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 { ...見前面 } |
總結(jié)
以上是生活随笔為你收集整理的Java和C/C++程序实时通讯数据移植问题的研究的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Socket网络通讯开发总结之:Java
- 下一篇: Visual Studio 如何创建C/