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++程序实时通讯数据移植问题的研究的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Socket网络通讯开发总结之:Java
- 下一篇: Java与C底层数据类型转换