0.作業要求
- 使用ASN.1編寫一個數據結構。數據結構自己考慮。
- 分別使用asn1c、JavaAsn1Compiler等對這個數據結構進行編譯。
- 使用c/java/python進行編碼,并存儲,而后用另外一種編程語言進行解碼,比如,用C編碼,可以用java或者python解碼。
- 對“要求一”中的數據結構,使用protobuffer實現一次。這里不強制要求不同的語言實現編碼和解碼。
1.使用ASN.1編寫一個數據結構
數據結構如下所示:
由兩個INTEGER型和兩個OCTET STRING型的數據構成
RectangleModule1 DEFINITIONS ::=
BEGIN
Rectangle ::= SEQUENCE {
height INTEGER,
width INTEGER,
author OCTET STRING,
title OCTET STRING
}
END
2.分別使用asn1c、JavaAsn1Compiler等對這個數據結構進行編譯
asn1c -fnative-types rectangle.asn1
由于編譯出的文件過多,下圖只截取部分編譯結果
htkz@htkz:~/Desktop/ASN1/ASN.1$ asn1c -fnative-types rectangle.asn
然后是java
htkz@htkz:~/java_asn1/src$ java -jar JAC.jar -d ~/java_asn1/src/jac_test -p rectangle rectangle.asn1
3.編程實現編碼與解碼
本次編程選擇使用Java進行編碼,然后使用python進行解碼
3.1 Java編碼
在之前使用的JavaAsn1Compiler編譯出來的類中添加如下代碼,來給數據數據結構賦值
public static void main(String []args) {
// 創建輸出流變量
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
BerOutputStream out = new BerOutputStream(outStream);
// 對rec這個類進行賦值
// 賦值結果為rec(author= b'1234', title = b'1234', height = 1011, width = 21)
Rectangle rec = new Rectangle();
byte[] name = new byte[4];
name[0] = '1';
name[1] = '2';
name[2] = '3';
name[3] = '4';
rec.author.setValue(name);
rec.title.setValue(name);
rec.height.setValue(1011);
rec.width.setValue(21);
// 對輸出變量進行編碼
try {
rec.encode(out);
System.out.println(out.toString());
}catch (java.io.IOException e1){
System.out.println(e1);
}
// 將編碼后的結果寫入test_rec文件,此時文件是二進制數據
File f = new File("test_rec");
try{
OutputStream outFile = new FileOutputStream(f);
try{outFile.write(outStream.toByteArray());}
catch (java.io.IOException e2){
System.out.println(e2);
}
}catch(java.io.FileNotFoundException e1){
System.out.println(e1);
}
PS:java文件的運行需要導入JAC.jar的包,這個包就在之前使用的JavaAsn1Compiler_3.0中,具體路徑如下
JavaAsn1Compiler_3.0/lib/JAC.jar
程序運行之后會在代碼路徑下生成編碼后的二進制文件test_rec_2,這個文件中的數據為rec(author= b'1234', title = b'1234', height = 1011, width = 21),之后使用python對這個文件進行解碼
3.2 python解碼
首先在python中定義相應的類rectangle.py
from pyasn1.type import univ, namedtype
class Rectangle(univ.Sequence):
componentType = namedtype.NamedTypes(
namedtype.NamedType('height', univ.Integer()),
namedtype.NamedType('width', univ.Integer()),
namedtype.NamedType('author', univ.OctetString()),
namedtype.NamedType('title', univ.OctetString()),
)
PS:本來這個類按照pyasn1官方文檔的方法,應該是使用asn1ate 這個工具來直接識別asn文件獲取類的解構,就像前面的asn1c和 JavaAsn1Compiler一樣,但是我git clone下來之后發現asn1ate這個工具還是有一點問題,就懶得調試了,沒有用它。而是直接在pyasn1-modules中找了樣例,直接自己手寫了一個。
之后就是解碼相關文件的代碼了,相當簡潔
from pyasn1.codec.der.decoder import decode as der_decoder
from rectangle import Rectangle
if __name__ == "__main__":
text = file = open('test_rec', 'rb').read()
result = der_decoder(text, asn1Spec=Rectangle())
print(result[0].prettyPrint())
4.protobuffer實現
首先根據上述數據結構編寫proto文件如下
message Rectangle {
required int32 height = 1;
required int32 width = 2;
optional string author = 3;
optional string title = 4;
}
下載protobufgitfer
git clone https://github.com/google/protobuf.git
之后使用如下指令生成python、java、cpp文件
protoc --proto_path= rectangle.proto --python_out ./
protoc --proto_path= rectangle.proto --cpp_out ./
protoc --proto_path= rectangle.proto --java_out ./