Golangでのバイトシーケンス化操作
4271 ワード
ネットワークプログラムを書くとき、構造体や整数などのデータ型をバイナリのbuffer列にシーケンス化する必要があります.あるいは1つのbufferから1つの構造体が解析され、最も典型的にはプロトコルのheader部分でhead lengthまたはbody lengthがパケットのパッケージングとパケットの取り外しの過程で、所定の整数タイプに従って解析する必要があり、サイズエンドシーケンスの問題に関連する.
1.Cでの操作方法
Cで一番簡単な方法はmemcpyで×××数や構造体などの他のタイプを1つのメモリにコピーし、必要なタイプに強く戻します.次のようになります.
必要に応じてntoh/htonシリーズ関数を用いてサイズエンドシーケンスの変換を行う.
2.golangでの操作
「encoding/binary」を使用すると、一般的なバイナリシーケンス化機能を提供できます.このモジュールには、主に次のインタフェースがあります.
Readインタフェースではbufの内容をdataパラメータで表されるデータ構造に埋め込むことができ,Writeインタフェースではdataパラメータに含まれるデータをbufferに書き込むことができる.変数BigEndianおよびLittleEndianは、ByteOrderインタフェースを実装するオブジェクトであり、インタフェースで提供される方法によってuintxタイプをbufに直接シーケンス化(uintx()または逆シーケンス化(putuintx()することができる.
2.1構造体を1つのbufにシーケンス化する
構造オブジェクトをシーケンス化する場合、シーケンス化された構造のサイズは既知である必要があり、その構造のサイズはSizeインタフェースによって取得され、bufferのサイズを決定することができることに注意してください.
固定サイズの構造体は、構造体に[]byteのようなスライスメンバーが現れないことが要求されます.そうしないと、Sizeは-1を返し、通常のシーケンス化操作はできません.
対応する出力は次のとおりです.
必要なbufferの大きさはSizeで得ることができます.Writeによりオブジェクトaの内容をbufferにシーケンス化することができる.ここでは、小エンドシーケンス方式でシーケンス化を行う(x 86アーキテクチャはいずれも小エンドシーケンスであり、ネットワークバイトシーケンスは大エンドシーケンスである).
構造体の場合「」メンバーはシーケンス化されません.
2.2 bufから1つの構造に逆シーケンス化する
bufferから読み込む場合は、同様に構造体のサイズを固定し、逆シーケンス化が必要な構造体メンバーは、エクスポート可能、すなわち大文字で始まるメンバーでなければなりません.逆シーケンス化しない:
出力:
ここではReadを用いてbufferから構造体オブジェクトaaにデータをインポートする.構造体の対応するメンバーがエクスポート可能でない場合、変換時にpanicエラーが発生します.
2.3整数をbufにシーケンス化し、bufから逆シーケンス化する
Read/Writeでuintxタイプの変数を直接読み書きすることで×××数のシーケンス化と逆シーケンス化.ネットワークでは×××数のシーケンス化は非常に一般的であるため、システムライブラリはtype ByteOrderインタフェースを提供し、uint 16/uint 32/uint 64のシーケンス化と逆シーケンス化を容易に行うことができます.
出力:
binaryを呼び出すLittleEndian.PutUint 16は、uint 16型のデータを、小端順の形式でbufferにシーケンス化することができる.binaryを通ります.LittleEndian.UIT 16はbufferの内容を逆シーケンス化する.
3.実際の例
ネットワーク・パケット・ヘッダの定義と初期化を見てみましょう.
これは一般的なtcpパッケージングの例です.例ではbinary.BigEndian.UIT 16は、データをネットワーク順の形式で読み出し、headの対応する構造に入れる.
1.Cでの操作方法
Cで一番簡単な方法はmemcpyで×××数や構造体などの他のタイプを1つのメモリにコピーし、必要なタイプに強く戻します.次のようになります.
// produce
int a = 32;
char *buf = (char *)malloc(sizeof(int));
memcpy(buf,&a,sizeof(int));
// consume
int b ;
memcpy(&b,buf,sizeof(int))
必要に応じてntoh/htonシリーズ関数を用いてサイズエンドシーケンスの変換を行う.
2.golangでの操作
「encoding/binary」を使用すると、一般的なバイナリシーケンス化機能を提供できます.このモジュールには、主に次のインタフェースがあります.
func Read(r io.Reader, order ByteOrder, data interface{}) error
func Write(w io.Writer, order ByteOrder, data interface{}) error
func Size(v interface{}) int
var BigEndian bigEndian
var LittleEndian littleEndian
/*
type ByteOrder interface {
Uint16([]byte) uint16
Uint32([]byte) uint32
Uint64([]byte) uint64
PutUint16([]byte, uint16)
PutUint32([]byte, uint32)
PutUint64([]byte, uint64)
String() string
}
/*
Readインタフェースではbufの内容をdataパラメータで表されるデータ構造に埋め込むことができ,Writeインタフェースではdataパラメータに含まれるデータをbufferに書き込むことができる.変数BigEndianおよびLittleEndianは、ByteOrderインタフェースを実装するオブジェクトであり、インタフェースで提供される方法によってuintxタイプをbufに直接シーケンス化(uintx()または逆シーケンス化(putuintx()することができる.
2.1構造体を1つのbufにシーケンス化する
構造オブジェクトをシーケンス化する場合、シーケンス化された構造のサイズは既知である必要があり、その構造のサイズはSizeインタフェースによって取得され、bufferのサイズを決定することができることに注意してください.
i := uint16(1)
size := binary.Size(i)
固定サイズの構造体は、構造体に[]byteのようなスライスメンバーが現れないことが要求されます.そうしないと、Sizeは-1を返し、通常のシーケンス化操作はできません.
type A struct {
// should be exported member when read back from buffer
One int32
Two int32
}
var a A
a.One = int32(1)
a.Two = int32(2)
buf := new(bytes.Buffer)
fmt.Println("a's size is ",binary.Size(a))
binary.Write(buf,binary.LittleEndian,a)
fmt.Println("after write ,buf is:",buf.Bytes())
対応する出力は次のとおりです.
a's size is 8
after write ,buf is : [1 0 0 0 2 0 0 0]
必要なbufferの大きさはSizeで得ることができます.Writeによりオブジェクトaの内容をbufferにシーケンス化することができる.ここでは、小エンドシーケンス方式でシーケンス化を行う(x 86アーキテクチャはいずれも小エンドシーケンスであり、ネットワークバイトシーケンスは大エンドシーケンスである).
構造体の場合「」メンバーはシーケンス化されません.
2.2 bufから1つの構造に逆シーケンス化する
bufferから読み込む場合は、同様に構造体のサイズを固定し、逆シーケンス化が必要な構造体メンバーは、エクスポート可能、すなわち大文字で始まるメンバーでなければなりません.逆シーケンス化しない:
type A struct {
// should be exported member when read back from buffer
One int32
Two int32
}
var aa A
buf := new(bytes.Buffer)
binary.Write(buf,binary.LittleEndian,a)
binary.Read(buf,binary.LittleEndian,&aa)
fmt.Println("after aa is ",aa)
出力:
after write ,bufis : [1 0 0 0 2 0 0 0]
before aa is : {0 0}
after aa is {1 2}
ここではReadを用いてbufferから構造体オブジェクトaaにデータをインポートする.構造体の対応するメンバーがエクスポート可能でない場合、変換時にpanicエラーが発生します.
2.3整数をbufにシーケンス化し、bufから逆シーケンス化する
Read/Writeでuintxタイプの変数を直接読み書きすることで×××数のシーケンス化と逆シーケンス化.ネットワークでは×××数のシーケンス化は非常に一般的であるため、システムライブラリはtype ByteOrderインタフェースを提供し、uint 16/uint 32/uint 64のシーケンス化と逆シーケンス化を容易に行うことができます.
int16buf := new(bytes.Buffer)
i := uint16(1)
binary.Write(int16buf,binary.LittleEndian,i)
fmt.Println(“write buf is:”int16buf.Bytes())
var int16buf2 [2]byte
binary.LittleEndian.PutUint16(int16buf2[:],uint16(1))
fmt.Println("put buffer is :",int16buf2[:])
ii := binary.LittleEndian.Uint16(int16buf2[:])
fmt.Println("Get buf is :",ii)
出力:
write buffer is : [1 0]
put buf is: [1 0]
Get buf is : 1
binaryを呼び出すLittleEndian.PutUint 16は、uint 16型のデータを、小端順の形式でbufferにシーケンス化することができる.binaryを通ります.LittleEndian.UIT 16はbufferの内容を逆シーケンス化する.
3.実際の例
ネットワーク・パケット・ヘッダの定義と初期化を見てみましょう.
type Head struct {
Cmd byte
Version byte
Magic uint16
Reserve byte
HeadLen byte
BodyLen uint16
}
func NewHead(buf []byte)*Head{
head := new(Head)
head.Cmd = buf[0]
head.Version = buf[1]
head.Magic = binary.BigEndian.Uint16(buf[2:4])
head.Reserve = buf[4]
head.HeadLen = buf[5]
head.BodyLen = binary.BigEndian.Uint16(buf[6:8])
return head
}
これは一般的なtcpパッケージングの例です.例ではbinary.BigEndian.UIT 16は、データをネットワーク順の形式で読み出し、headの対応する構造に入れる.