webForumDet fria alternativet

Skicka nästlande c structs över nätverk till java.

5 svar · 348 visningar · startad av Nickemannen

NickemannenMedlem sedan aug. 20003 524 inlägg
#1

Skicka nästlande c structs över nätverk till java.

Jag känner till Swig projektet men det fungerar inte riktigt som jag vill.

Någon som har några tips. I en struct skickar jag en char array och den vet jag vilken slags struct den är med hjälp av ett Id nummer som medföljer den strukten som den andra struckten är näslad i.

Alltså mega problem för java att översätta detta. Någon med tips.

Det är ju ingen omöjlighet eftersom jag vet i vilken ordning och vad som är vad men hur skall jag med minsta möjliga arbete klara av detta.

LimeMedlem sedan sep. 2001837 inlägg
#2

Hmm... För mig så låter det här som ett encoding/decoding-problem...

Du vet hur structarna ser ut, eller hur? Du vet vad som är gemensamt i alla structar.

Om du skickar datat till ett javaprogram som läser in det, utan att tolka det i en buffert så är det efter det bara att börja koda av det...

antag att du har två structar, A och B som ser ut såhär:

struct A {
int type;
int len;
char[] data;
B* bStruct;
A* nextA;
}

och

struct B {
int type;
int alfa;
int beta;
int omega;
}

Då kan du skapa en basklass som ser ut något såhär:

import java.nio.ByteBuffer;

public abstract class Element {

	protected ByteBuffer buff;
	
	public Element(ByteBuffer b)
	{
		this.buff = b;
	}
	
	public abstract void decode();
	
}

och dessa subklasar du en för varje struct:


import java.nio.ByteBuffer;

public class AElement extends Element {

	int len;

	byte[] data;

	BElement bStruct;

	AElement nextA;

	public AElement(ByteBuffer b) {
		super(b);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see Element#decode()
	 */
	public void decode() {
		len = buff.getInt();
		data = new byte[len];
		buff.get(data, 0, data.length);
		bStruct = (BElement) ElementFactory.getElement(buff);
		if(bStruct != null)
			bStruct.decode();
		nextA = (AElement) ElementFactory.getElement(buff);
		if(nextA != null)
			nextA.decode();

	}

	public static final int typeCode = 1;
}

import java.nio.ByteBuffer;

public class BElement extends Element {

	int alfa;

	int beta;

	int omega;

	public BElement(ByteBuffer b) {
		super(b);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see Element#decode()
	 */
	public void decode() {
		alfa = buff.getInt();
		beta = buff.getInt();
		omega = buff.getInt();

	}

	public static final int typeCode = 2;

}

Jag brukar vilja skapa själva Elementen ur en fabrik så jag skrev ihop en ElementFactory.


import java.nio.ByteBuffer;

public class ElementFactory {

	public static Element getElement(ByteBuffer buff)
	{
		Element result = null;
		int type = buff.getInt(); // The type is first in every buffer
		switch (type) {
		case AElement.typeCode:
			result = new AElement(buff);
			
			break;

			case BElement.typeCode:
			result = new BElement(buff);
			break;
			
		default:
			break;
		}
		
		return result;
	}
	
	
}

Notera att detta är principkod. Jag har dock andra applikationer som simulerar protokoll som använder samma principer.

Hoppas det hjälper lite.

NickemannenMedlem sedan aug. 20003 524 inlägg
#3

Det blir en hel del kod men det löser mitt problem på ett sätt som jag tycker är snyggt tack

LimeMedlem sedan sep. 2001837 inlägg
#4

Det blir det när man håller på med den här typen av saker.

Kan nämna att jag även har gjort en implementation där kodning och avkodning sker genom att man använder sig av Reflektion och tittar på en viss böna... Då sparar man mycket kod...

NickemannenMedlem sedan aug. 20003 524 inlägg
#5

Det är inte så att du har Encode också?

Jag testade att göra såhär
ByteBuffer bb = ByteBuffer.allocate(256);
bb.putInt(0);
// ObserverId
bb.putInt(ObserverId);
bb.putInt(compLog);
bb.putInt(MainServer);

och sedan skicka bb över nätverket till min C applikation som tar emot men det ända den får är 0 0 0 0 0 där jag plockar ut.

När jag skriver ut min Buffer med getint
for(int i=0;i<15;i++)
{
System.out.println(bb.getInt(i));
}
0
0
0
55
0
0
0
5
0
0
0
45
0
0
0
Även om jag använder index blir det såhär.

Jag har testatt att göra om den till en byte array men det blir samma resultat.

LimeMedlem sedan sep. 2001837 inlägg
#6

Ah... Det är här det roliga börjar... :-)

Man bör ha en metod som heter getSize() som returnerar antalet bytes man behöver för att fylla bufferten. Annars finns risken att man gör bort sig...

Det som är lite knepigt är att få reda på hur många bitar som används för varje typ. Se http://java.sun.com/docs/books/tutorial/java/nutsandbolts/datatypes.html.

ByteBuffer.putInt(int i) skriver 4 bytes och en byte är 8 bits.

En int är alltså 32 bitar lång.

Om man då kikar på din kod så borde bitmönstret på din maskin, som jag gör ett antagande är en 32-bitars och inte en 16 eller 64 bitarsmaskin, se ut såhär. jag har valt att gör det i byterader (8-bitarsrader.) Detta om det är BIG_ENDIAN (Största värdesiffran först).

bit 1 2 3 4 5 6 7 8
--------------------------------
byte
0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 0
2 0 0 0 0 0 0 0 0
3 0 0 0 0 0 0 0 0 (0)

-
4 0 0 0 0 0 0 0 0
5 0 0 0 0 0 0 0 0
6 0 0 0 0 0 0 0 0
7 0 0 1 1 0 1 1 1 (55)
-
8 0 0 0 0 0 0 0 0
9 0 0 0 0 0 0 0 0
10 0 0 0 0 0 0 0 0
11 0 0 0 0 0 1 0 1 (5)
-
12 0 0 0 0 0 0 0 0
13 0 0 0 0 0 0 0 0
14 0 0 0 0 0 0 0 0
15 0 0 1 0 1 1 0 1 (45)
---
16 ???
17 ???
18 ???

när jag läser ut detta i en forloop får jag detta. Jag har skrivit raderna inom parentes.

0 (0-4)
0 (1-5)
0 (2-6)
0 (3-7)
55 (4-8)
14080 (5-9)
3604480 (6-10)
922746880 (7-11)
4 (8-12)
1024 (9-13)
262144 (10-14)
67108864 (11-15)
45 (12-16)
11520 (13-17)
2949120 (14-18)

Vad din for-loop gör är alltså att först läsa från rad 0 och 4 bytes och skriva ut detta (0) sedan från rad 1 och 4 bytes och skriva ut detta (0)... Vid rad 4 läser den 4 bytes och skriver ut 55.

Vid rad 5 läser den ut
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1 1 1 0 0 0 0 0 0 0 0

som mycket riktigt blir 14080.

Native för Windows 2000 är LITTLE_ENDIAN, alltså minsta värdesiffran först och då blir bitmönstret att se ut såhär:

bit 1 2 3 4 5 6 7 8
--------------------------------
byte
0 0 0 0 0 0 0 0 0 (0)
1 0 0 0 0 0 0 0 0
2 0 0 0 0 0 0 0 0
3 0 0 0 0 0 0 0 0

-
4 0 0 1 1 0 1 1 1 (55)
5 0 0 0 0 0 0 0 0
6 0 0 0 0 0 0 0 0
7 0 0 0 0 0 0 0 0
-
8 0 0 0 0 0 1 0 1 (5)
9 0 0 0 0 0 0 0 0
10 0 0 0 0 0 0 0 0
11 0 0 0 0 0 0 0 0
-
12 0 0 1 0 1 1 0 1 (45)
13 0 0 0 0 0 0 0 0
14 0 0 0 0 0 0 0 0
15 0 0 0 0 0 0 0 0

och utskriften:

0 (0-4)
922746880 (1-5)
3604480 (2-6)
14080 (3-7)
55 (4-8)
67108864 (5-9)
262144 (6-10)
1024 (7-11)
4 (8-12)
754974720 (9-13)
2949120 (10-14)
11520 (11-15)
45 (12-16)
0 (13-17)
0 (14-18)

Av din utskrift kan jag alltså sluta mig till att du använder BIG_ENDIAN.

Frågan är nu då hur du läser in detta på mottagarsidan???
Det är här det blir liiiiite knepigt... Risken finns att en integer inte representeras på detta sätt på C-sidan... Det gäller att veta hur många bitar man ska läsa av varje typ... Eller hur många man ska skriva...

Blir det något klarare?

En jättebra sak är att titta på ByteBufferten som man gjort eller tagit emot i en debugger.

Genererad på 371 ms · cache AV · v20260730165559-full.f96bc7eb