Java (Big-Endian) 與 C/C++ (通常 Little-Endian) 進行 Socket 通訊時,基本類型序列化需處理位序轉換、無符號類型映射、字串長度前綴等差異。本文提供生產級 CIOUtil 工具類,含完整單元測試。
| C/C++ 類型 | 位元組 | Java 對應 | 備註 |
|---|---|---|---|
| unsigned char / uint8_t | 1 | byte (有符號) | Java 無無符號,邏輯上 & 0xFF |
| unsigned short / uint16_t | 2 | char (無符號 16-bit) | Java char 唯一無符號基本型別 |
| unsigned int / uint32_t | 4 | int (有符號) | 大值需用 long 接收 |
| unsigned long long / uint64_t | 8 | long | |
| float | 4 | float | IEEE 754 相同 |
| double | 8 | double | IEEE 754 相同 |
Integer.reverseBytes()、Long.reverseBytes()、Short.reverseBytes() 一鍵大小端互換。Float.floatToIntBits()/intBitsToFloat()、Double.doubleToLongBits()/longBitsToDouble() 保持位模式不變。uint16_t 長度(2字節) + UTF-8 字節 格式,雙方約定一致。public abstract class CIOUtil {
public static final String CHARSET = "UTF-8";
// --- 讀取 ---
public static int readInt(DataInputStream in) throws IOException {
return Integer.reverseBytes(in.readInt()); // LE -> BE
}
public static long readLong(DataInputStream in) throws IOException {
return Long.reverseBytes(in.readLong());
}
public static short readShort(DataInputStream in) throws IOException {
return Short.reverseBytes(in.readShort());
}
public static char readChar(DataInputStream in) throws IOException {
return (char) readShort(in); // unsigned short -> Java char
}
public static float readFloat(DataInputStream in) throws IOException {
return Float.intBitsToFloat(readInt(in));
}
public static double readDouble(DataInputStream in) throws IOException {
return Double.longBitsToDouble(readLong(in));
}
public static String readUTF(DataInputStream in) throws IOException {
short len = readShort(in); // 讀長度前綴
byte[] bytes = new byte[len];
in.readFully(bytes);
return new String(bytes, CHARSET);
}
// --- 寫入 ---
public static void writeInt(DataOutputStream out, int v) throws IOException {
out.writeInt(Integer.reverseBytes(v)); // BE -> LE
}
public static void writeLong(DataOutputStream out, long v) throws IOException {
out.writeLong(Long.reverseBytes(v));
}
public static void writeShort(DataOutputStream out, short v) throws IOException {
out.writeShort(Short.reverseBytes(v));
}
public static void writeChar(DataOutputStream out, char v) throws IOException {
writeShort(out, (short) v);
}
public static void writeFloat(DataOutputStream out, float v) throws IOException {
writeInt(out, Float.floatToIntBits(v));
}
public static void writeDouble(DataOutputStream out, double v) throws IOException {
writeLong(out, Double.doubleToLongBits(v));
}
public static void writeUTF(DataOutputStream out, String s) throws IOException {
byte[] bytes = s.getBytes(CHARSET);
writeShort(out, (short) bytes.length);
out.write(bytes);
}
// boolean、byte[] 直讀直寫無需轉換
}
public class CIOUtilTest {
private void roundTrip(String name, Object input,
Consumer<DataOutputStream> writer,
Function<DataInputStream, Object> reader) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
writer.accept(dos);
dos.flush();
byte[] data = baos.toByteArray();
ByteArrayInputStream bais = new ByteArrayInputStream(data);
DataInputStream dis = new DataInputStream(bais);
Object output = reader.apply(dis);
assertEquals(name, input, output);
}
@Test public void testInt() throws IOException {
roundTrip("int", 123456,
os -> CIOUtil.writeInt(os, 123456),
is -> CIOUtil.readInt(is));
}
@Test public void testLong() throws IOException { /* 同理 */ }
@Test public void testFloat() throws IOException { /* 近似比較 */ }
@Test public void testDouble() throws IOException { /* 近似比較 */ }
@Test public void testChar() throws IOException { /* '中' */ }
@Test public void testUTF() throws IOException { /* "中文支持" */ }
}
// Java 發送端
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(baos);
CIOUtil.writeInt(out, 1001); // 命令碼
CIOUtil.writeUTF(out, "Hello C++"); // 參數
send(baos.toByteArray());
// C++ 接收端 (略)
/*
uint32_t cmd = ntohl(*(uint32_t*)buf); // 網路序=大端,C++通常小端需轉
uint16_t len = ntohs(*(uint16_t*)(buf+4));
string param(buf+6, len);
*/
#pragma pack(1) 或手動序列化各欄位。short 限制 64KB,超長需改 int。ByteBuffer + Order(BIG_ENDIAN) 替代 DataOutputStream。CIOUtil 封裝了 Java 與 C/C++ 跨語言通訊的所有底層細節:大小端轉換、無符號映射、浮點位模式保持、長度前綴字串。配合單元測試驗證雙向轉換一致性,可直接用於生產環境 Socket 協議編解碼。